Kernel-Infrastructure.st 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. Smalltalk createPackage: 'Kernel-Infrastructure'!
  2. Object subclass: #AmberBootstrapInitialization
  3. instanceVariableNames: ''
  4. package: 'Kernel-Infrastructure'!
  5. !AmberBootstrapInitialization class methodsFor: 'initialization'!
  6. initializeClasses
  7. Smalltalk classes do: [ :each |
  8. each = SmalltalkImage ifFalse: [ each initialize ] ]
  9. ! !
  10. !AmberBootstrapInitialization class methodsFor: 'organization'!
  11. organizeClasses
  12. Smalltalk classes do: [ :each | each enterOrganization ]
  13. !
  14. organizeMethods
  15. Smalltalk classes do: [ :eachClass |
  16. eachClass definedMethods do: [ :eachMethod |
  17. eachMethod methodClass methodOrganizationEnter: eachMethod andLeave: nil ] ]
  18. ! !
  19. !AmberBootstrapInitialization class methodsFor: 'public api'!
  20. run
  21. SmalltalkImage initialize.
  22. Smalltalk adoptPackageDictionary.
  23. self
  24. organizeClasses;
  25. organizeMethods;
  26. initializeClasses
  27. ! !
  28. ProtoObject subclass: #JSObjectProxy
  29. instanceVariableNames: 'jsObject'
  30. package: 'Kernel-Infrastructure'!
  31. !JSObjectProxy commentStamp!
  32. I handle sending messages to JavaScript objects, making JavaScript object accessing from Amber fully transparent.
  33. My instances make intensive use of `#doesNotUnderstand:`.
  34. My instances are automatically created by Amber whenever a message is sent to a JavaScript object.
  35. ## Usage examples
  36. JSObjectProxy objects are instanciated by Amber when a Smalltalk message is sent to a JavaScript object.
  37. window alert: 'hello world'.
  38. window inspect.
  39. (window jQuery: 'body') append: 'hello world'
  40. Amber messages sends are converted to JavaScript function calls or object property access _(in this order)_. If n one of them match, a `MessageNotUnderstood` error will be thrown.
  41. ## Message conversion rules
  42. - `someUser name` becomes `someUser.name`
  43. - `someUser name: 'John'` becomes `someUser name = "John"`
  44. - `console log: 'hello world'` becomes `console.log('hello world')`
  45. - `(window jQuery: 'foo') css: 'background' color: 'red'` becomes `window.jQuery('foo').css('background', 'red')`
  46. __Note:__ For keyword-based messages, only the first keyword is kept: `window foo: 1 bar: 2` is equivalent to `window foo: 1 baz: 2`.!
  47. !JSObjectProxy methodsFor: 'accessing'!
  48. at: aString
  49. <inlineJS: 'return $self[''@jsObject''][aString]'>
  50. !
  51. at: aString ifAbsent: aBlock
  52. "return the aString property or evaluate aBlock if the property is not defined on the object"
  53. <inlineJS: '
  54. var obj = $self[''@jsObject''];
  55. return aString in obj ? obj[aString] : aBlock._value();
  56. '>
  57. !
  58. at: aString ifPresent: aBlock
  59. "return the evaluation of aBlock with the value if the property is defined or return nil"
  60. <inlineJS: '
  61. var obj = $self[''@jsObject''];
  62. return aString in obj ? aBlock._value_(obj[aString]) : nil;
  63. '>
  64. !
  65. at: aString ifPresent: aBlock ifAbsent: anotherBlock
  66. "return the evaluation of aBlock with the value if the property is defined
  67. or return value of anotherBlock"
  68. <inlineJS: '
  69. var obj = $self[''@jsObject''];
  70. return aString in obj ? aBlock._value_(obj[aString]) : anotherBlock._value();
  71. '>
  72. !
  73. at: aString put: anObject
  74. <inlineJS: 'return $self[''@jsObject''][aString] = anObject'>
  75. !
  76. in: aValuable
  77. ^ aValuable value: jsObject
  78. !
  79. jsObject
  80. ^ jsObject
  81. ! !
  82. !JSObjectProxy methodsFor: 'comparing'!
  83. = anObject
  84. anObject class == self class ifFalse: [ ^ false ].
  85. ^ JSObjectProxy compareJSObjectOfProxy: self withProxy: anObject
  86. ! !
  87. !JSObjectProxy methodsFor: 'converting'!
  88. asJavaScriptObject
  89. "Answers the receiver in a stringify-friendly fashion"
  90. ^ jsObject
  91. ! !
  92. !JSObjectProxy methodsFor: 'enumerating'!
  93. keysAndValuesDo: aBlock
  94. <inlineJS: '
  95. var o = $self[''@jsObject''];
  96. for(var i in o) {
  97. aBlock._value_value_(i, o[i]);
  98. }
  99. '>
  100. ! !
  101. !JSObjectProxy methodsFor: 'printing'!
  102. printOn: aStream
  103. aStream nextPutAll: self printString
  104. !
  105. printString
  106. <inlineJS: '
  107. var js = $self[''@jsObject''];
  108. return js.toString
  109. ? js.toString()
  110. : Object.prototype.toString.call(js)
  111. '>
  112. ! !
  113. !JSObjectProxy methodsFor: 'promises'!
  114. catch: aBlock
  115. (NativeFunction isNativeFunction: (self at: #then))
  116. ifTrue: [ ^ (TThenable >> #catch:) sendTo: jsObject arguments: {aBlock} ]
  117. ifFalse: [ ^ super catch: aBlock ]
  118. !
  119. on: aClass do: aBlock
  120. (NativeFunction isNativeFunction: (self at: #then))
  121. ifTrue: [ ^ (TThenable >> #on:do:) sendTo: jsObject arguments: {aClass. aBlock} ]
  122. ifFalse: [ ^ super on: aClass do: aBlock ]
  123. !
  124. then: aBlockOrArray
  125. (NativeFunction isNativeFunction: (self at: #then))
  126. ifTrue: [ ^ (TThenable >> #then:) sendTo: jsObject arguments: {aBlockOrArray} ]
  127. ifFalse: [ ^ super then: aBlockOrArray ]
  128. ! !
  129. !JSObjectProxy methodsFor: 'proxy'!
  130. doesNotUnderstand: aMessage
  131. ^ (JSObjectProxy lookupProperty: aMessage selector asJavaScriptPropertyName ofProxy: self)
  132. ifNil: [ super doesNotUnderstand: aMessage ]
  133. ifNotNil: [ :jsSelector |
  134. JSObjectProxy
  135. forwardMessage: jsSelector
  136. withArguments: aMessage arguments
  137. ofProxy: self ]
  138. ! !
  139. !JSObjectProxy methodsFor: 'streaming'!
  140. putOn: aStream
  141. aStream nextPutJSObject: jsObject
  142. ! !
  143. !JSObjectProxy class methodsFor: 'instance creation'!
  144. on: aJSObject
  145. | instance |
  146. instance := self new.
  147. self jsObject: aJSObject ofProxy: instance.
  148. ^ instance
  149. ! !
  150. !JSObjectProxy class methodsFor: 'proxy'!
  151. addObjectVariablesTo: aDictionary ofProxy: aProxy
  152. <inlineJS: '
  153. var jsObject = aProxy[''@jsObject''];
  154. for(var i in jsObject) {
  155. aDictionary._at_put_(i, jsObject[i]);
  156. }
  157. '>
  158. !
  159. compareJSObjectOfProxy: aProxy withProxy: anotherProxy
  160. <inlineJS: '
  161. var anotherJSObject = anotherProxy.a$cls ? anotherProxy["@jsObject"] : anotherProxy;
  162. return aProxy["@jsObject"] === anotherJSObject
  163. '>
  164. !
  165. forwardMessage: aString withArguments: anArray ofProxy: aProxy
  166. <inlineJS: '
  167. return $core.accessJavaScript(aProxy._jsObject(), aString, anArray);
  168. '>
  169. !
  170. jsObject: aJSObject ofProxy: aProxy
  171. <inlineJS: 'aProxy[''@jsObject''] = aJSObject'>
  172. !
  173. lookupProperty: aString ofProxy: aProxy
  174. "Looks up a property in JS object.
  175. Answer the property if it is present, or nil if it is not present."
  176. <inlineJS: 'return aString in aProxy._jsObject() ? aString : nil'>
  177. ! !
  178. Object subclass: #Organizer
  179. instanceVariableNames: ''
  180. package: 'Kernel-Infrastructure'!
  181. !Organizer commentStamp!
  182. I represent categorization information.
  183. ## API
  184. Use `#addElement:` and `#removeElement:` to manipulate instances.!
  185. !Organizer methodsFor: 'accessing'!
  186. addElement: anObject
  187. <inlineJS: '$core.addElement(self.elements, anObject)'>
  188. !
  189. elements
  190. ^ (self basicAt: 'elements') copy
  191. !
  192. removeElement: anObject
  193. <inlineJS: '$core.removeElement(self.elements, anObject)'>
  194. ! !
  195. Organizer subclass: #ClassOrganizer
  196. instanceVariableNames: ''
  197. package: 'Kernel-Infrastructure'!
  198. !ClassOrganizer commentStamp!
  199. I am an organizer specific to classes. I hold method categorization information for classes.!
  200. !ClassOrganizer methodsFor: 'accessing'!
  201. addElement: aString
  202. super addElement: aString.
  203. SystemAnnouncer current announce: (ProtocolAdded new
  204. protocol: aString;
  205. theClass: self theClass;
  206. yourself)
  207. !
  208. removeElement: aString
  209. super removeElement: aString.
  210. SystemAnnouncer current announce: (ProtocolRemoved new
  211. protocol: aString;
  212. theClass: self theClass;
  213. yourself)
  214. !
  215. theClass
  216. <inlineJS: 'return self.theClass'>
  217. !
  218. theClass: aClass
  219. <inlineJS: 'self.theClass = aClass'>
  220. ! !
  221. !ClassOrganizer methodsFor: 'initialization'!
  222. initialize
  223. super initialize.
  224. self basicAt: 'elements' put: #()
  225. ! !
  226. !ClassOrganizer class methodsFor: 'instance creation'!
  227. on: aClass
  228. ^ self new
  229. theClass: aClass;
  230. yourself
  231. ! !
  232. Organizer subclass: #PackageOrganizer
  233. instanceVariableNames: ''
  234. package: 'Kernel-Infrastructure'!
  235. !PackageOrganizer commentStamp!
  236. I am an organizer specific to packages. I hold classes categorization information.!
  237. !PackageOrganizer methodsFor: 'initialization'!
  238. initialize
  239. super initialize.
  240. self basicAt: 'elements' put: #()
  241. ! !
  242. Object subclass: #Package
  243. instanceVariableNames: 'evalBlock basicTransport name transport imports dirty organization'
  244. package: 'Kernel-Infrastructure'!
  245. !Package commentStamp!
  246. I am similar to a "class category" typically found in other Smalltalks like Pharo or Squeak. Amber does not have class categories anymore, it had in the beginning but now each class in the system knows which package it belongs to.
  247. Each package has a name and can be queried for its classes, but it will then resort to a reverse scan of all classes to find them.
  248. ## API
  249. Packages are manipulated through "Smalltalk current", like for example finding one based on a name or with `Package class >> #name` directly:
  250. Smalltalk current packageAt: 'Kernel'
  251. Package named: 'Kernel'
  252. A package differs slightly from a Monticello package which can span multiple class categories using a naming convention based on hyphenation. But just as in Monticello a package supports "class extensions" so a package can define behaviors in foreign classes using a naming convention for method categories where the category starts with an asterisk and then the name of the owning package follows.
  253. You can fetch a package from the server:
  254. Package load: 'Additional-Examples'!
  255. !Package methodsFor: 'accessing'!
  256. beClean
  257. dirty := false.
  258. SystemAnnouncer current announce: (PackageClean new
  259. package: self;
  260. yourself)
  261. !
  262. beDirty
  263. dirty := true.
  264. SystemAnnouncer current announce: (PackageDirty new
  265. package: self;
  266. yourself)
  267. !
  268. classTemplate
  269. ^ String streamContents: [ :stream | stream
  270. write: 'Object subclass: #NameOfSubclass'; lf;
  271. tab; write: 'instanceVariableNames: '''''; lf;
  272. tab; write: 'package: '; print: self name ]
  273. !
  274. definition
  275. ^ String streamContents: [ :stream | stream
  276. write: self class name; lf;
  277. tab; write: 'named: '; print: self name; lf;
  278. tab; write: { 'imports: '. self importsDefinition }; lf;
  279. tab; write: { 'transport: ('. self transport definition. ')' } ]
  280. !
  281. evalBlock
  282. ^ evalBlock
  283. !
  284. evalBlock: aBlock
  285. evalBlock := aBlock
  286. !
  287. imports
  288. ^ imports ifNil: [
  289. self imports: #().
  290. imports ]
  291. !
  292. imports: anArray
  293. self validateImports: anArray.
  294. imports := anArray asSet
  295. !
  296. importsDefinition
  297. ^ String streamContents: [ :stream |
  298. stream write: '{'.
  299. self sortedImportsAsArray
  300. do: [ :each | stream print: each ]
  301. separatedBy: [ stream write: '. ' ].
  302. stream write: '}' ]
  303. !
  304. javaScriptDescriptor: anObject
  305. | basicEval basicImports |
  306. basicEval := (anObject at: 'innerEval' ifAbsent: [ nil asJavaScriptObject ]).
  307. basicImports := (anObject at: 'imports' ifAbsent: [ #() ]).
  308. basicTransport := (anObject at: 'transport' ifAbsent: []).
  309. self
  310. evalBlock: basicEval;
  311. imports: (self importsFromJson: basicImports)
  312. !
  313. name
  314. ^ name
  315. !
  316. name: aString
  317. name := aString
  318. !
  319. organization
  320. ^ organization
  321. !
  322. transport
  323. ^ transport ifNil: [
  324. self transport: (PackageTransport fromJson: self basicTransport).
  325. transport ]
  326. !
  327. transport: aPackageTransport
  328. transport := aPackageTransport.
  329. aPackageTransport package: self
  330. ! !
  331. !Package methodsFor: 'classes'!
  332. classes
  333. ^ self organization elements
  334. !
  335. setupClasses
  336. self classes do: [ :each | each initialize ]
  337. !
  338. sortedClasses
  339. "Answer all classes in the receiver, sorted by superclass/subclasses and by class name for common subclasses (Issue #143)."
  340. ^ self class sortedClasses: self classes
  341. ! !
  342. !Package methodsFor: 'converting'!
  343. importsAsJson
  344. ^ self sortedImportsAsArray collect: [ :each |
  345. each isString
  346. ifTrue: [ each ]
  347. ifFalse: [ each key, '=', each value ]]
  348. !
  349. importsFromJson: anArray
  350. "Parses array of string, eg. #('asdf' 'qwer=tyuo')
  351. into array of Strings and Associations,
  352. eg. {'asdf'. 'qwer'->'tyuo'}"
  353. ^ anArray collect: [ :each |
  354. | split |
  355. split := each tokenize: '='.
  356. split size = 1
  357. ifTrue: [ split first ]
  358. ifFalse: [ split first -> split second ]]
  359. ! !
  360. !Package methodsFor: 'dependencies'!
  361. loadDependencies
  362. "Returns list of packages that need to be loaded
  363. before loading this package."
  364. | classes packages |
  365. classes := self loadDependencyClasses.
  366. ^ (classes collect: [ :each | each package ]) asSet
  367. remove: self ifAbsent: [];
  368. yourself
  369. !
  370. loadDependencyClasses
  371. "Returns classes needed at the time of loading a package.
  372. These are all that are used to subclass
  373. and to define an extension method
  374. as well as all traits used"
  375. | starCategoryName |
  376. starCategoryName := '*', self name.
  377. ^ (self classes collect: [ :each | each superclass ]) asSet
  378. addAll: (Smalltalk classes select: [ :each |
  379. each protocols, (each theMetaClass ifNil: [ #() ] ifNotNil: [ :meta | meta protocols])
  380. includes: starCategoryName ]);
  381. addAll: (Array streamContents: [ :as | self traitCompositions valuesDo: [ :each | as write: (each collect: [ :eachTT | eachTT trait ])]]);
  382. remove: nil ifAbsent: [];
  383. yourself
  384. !
  385. traitCompositions
  386. | traitCompositions |
  387. traitCompositions := Dictionary new.
  388. self classes do: [ :each |
  389. traitCompositions at: each put: each traitComposition.
  390. each theMetaClass ifNotNil: [ :meta | traitCompositions at: meta put: meta traitComposition ] ].
  391. ^ traitCompositions reject: [ :each | each isEmpty ]
  392. ! !
  393. !Package methodsFor: 'evaluating'!
  394. eval: aString
  395. ^ evalBlock
  396. ifNotNil: [ evalBlock value: aString ]
  397. ifNil: [ Compiler eval: aString ]
  398. ! !
  399. !Package methodsFor: 'initialization'!
  400. initialize
  401. super initialize.
  402. organization := PackageOrganizer new.
  403. evalBlock := nil.
  404. dirty := nil.
  405. imports := nil.
  406. transport := nil
  407. ! !
  408. !Package methodsFor: 'printing'!
  409. printOn: aStream
  410. super printOn: aStream.
  411. aStream
  412. nextPutAll: ' (';
  413. nextPutAll: self name;
  414. nextPutAll: ')'
  415. ! !
  416. !Package methodsFor: 'private'!
  417. basicTransport
  418. "Answer the transport literal JavaScript object as setup in the JavaScript file, if any"
  419. ^ basicTransport
  420. !
  421. sortedImportsAsArray
  422. "Answer imports sorted first by type (associations first),
  423. then by value"
  424. ^ self imports asArray
  425. sorted: [ :a :b |
  426. a isString not & b isString or: [
  427. a isString = b isString and: [
  428. a value <= b value ]]]
  429. ! !
  430. !Package methodsFor: 'testing'!
  431. isDirty
  432. ^ dirty ifNil: [ false ]
  433. !
  434. isPackage
  435. ^ true
  436. ! !
  437. !Package methodsFor: 'validation'!
  438. validateImports: aCollection
  439. aCollection do: [ :import |
  440. import isString ifFalse: [
  441. (import respondsTo: #key) ifFalse: [
  442. self error: 'Imports must be Strings or Associations' ].
  443. import key isString & import value isString ifFalse: [
  444. self error: 'Key and value must be Strings' ].
  445. (import key match: '^[a-zA-Z][a-zA-Z0-9]*$') ifFalse: [
  446. self error: 'Keys must be identifiers' ]]]
  447. ! !
  448. Package class instanceVariableNames: 'defaultCommitPathJs defaultCommitPathSt'!
  449. !Package class methodsFor: 'accessing'!
  450. named: aPackageName
  451. ^ Smalltalk
  452. packageAt: aPackageName
  453. ifAbsent: [
  454. Smalltalk createPackage: aPackageName ]
  455. !
  456. named: aPackageName ifAbsent: aBlock
  457. ^ Smalltalk packageAt: aPackageName ifAbsent: aBlock
  458. !
  459. named: aPackageName imports: anArray transport: aTransport
  460. | package |
  461. package := self named: aPackageName.
  462. package imports: anArray.
  463. package transport: aTransport.
  464. ^ package
  465. !
  466. named: aPackageName transport: aTransport
  467. | package |
  468. package := self named: aPackageName.
  469. package transport: aTransport.
  470. ^ package
  471. ! !
  472. !Package class methodsFor: 'instance creation'!
  473. named: aString javaScriptDescriptor: anObject
  474. | package |
  475. package := Smalltalk createPackage: aString.
  476. package javaScriptDescriptor: anObject.
  477. ^ package
  478. !
  479. new: aString
  480. ^ Package new
  481. name: aString;
  482. yourself
  483. ! !
  484. !Package class methodsFor: 'sorting'!
  485. sortedClasses: classes
  486. "Answer classes, sorted by superclass/subclasses and by class name for common subclasses (Issue #143)"
  487. | children others nodes expandedClasses |
  488. children := #().
  489. others := #().
  490. classes do: [ :each |
  491. (classes includes: each superclass)
  492. ifFalse: [ children add: each ]
  493. ifTrue: [ others add: each ]].
  494. nodes := children collect: [ :each |
  495. ClassSorterNode on: each classes: others level: 0 ].
  496. nodes := nodes sorted: [ :a :b | a theClass name <= b theClass name ].
  497. expandedClasses := Array new.
  498. nodes do: [ :aNode |
  499. aNode traverseClassesWith: expandedClasses ].
  500. ^ expandedClasses
  501. ! !
  502. Object subclass: #PackageStateObserver
  503. instanceVariableNames: ''
  504. package: 'Kernel-Infrastructure'!
  505. !PackageStateObserver commentStamp!
  506. My current instance listens for any changes in the system that might affect the state of a package (being dirty).!
  507. !PackageStateObserver methodsFor: 'accessing'!
  508. announcer
  509. ^ SystemAnnouncer current
  510. ! !
  511. !PackageStateObserver methodsFor: 'actions'!
  512. observeSystem
  513. self announcer
  514. on: PackageAdded
  515. send: #onPackageAdded:
  516. to: self;
  517. on: ClassAnnouncement
  518. send: #onClassModification:
  519. to: self;
  520. on: MethodAnnouncement
  521. send: #onMethodModification:
  522. to: self;
  523. on: ProtocolAnnouncement
  524. send: #onProtocolModification:
  525. to: self
  526. ! !
  527. !PackageStateObserver methodsFor: 'reactions'!
  528. onClassModification: anAnnouncement
  529. anAnnouncement theClass ifNotNil: [ :theClass | theClass package beDirty ]
  530. !
  531. onMethodModification: anAnnouncement
  532. anAnnouncement method package ifNotNil: [ :package | package beDirty ]
  533. !
  534. onPackageAdded: anAnnouncement
  535. anAnnouncement package beDirty
  536. !
  537. onProtocolModification: anAnnouncement
  538. anAnnouncement package ifNotNil: [ :package | package beDirty ]
  539. ! !
  540. PackageStateObserver class instanceVariableNames: 'current'!
  541. !PackageStateObserver class methodsFor: 'accessing'!
  542. current
  543. ^ current ifNil: [ current := self new ]
  544. ! !
  545. !PackageStateObserver class methodsFor: 'initialization'!
  546. initialize
  547. self current observeSystem
  548. ! !
  549. Error subclass: #ParseError
  550. instanceVariableNames: ''
  551. package: 'Kernel-Infrastructure'!
  552. !ParseError commentStamp!
  553. Instance of ParseError are signaled on any parsing error.
  554. See `Smalltalk >> #parse:`!
  555. Object subclass: #Setting
  556. instanceVariableNames: 'key value defaultValue'
  557. package: 'Kernel-Infrastructure'!
  558. !Setting commentStamp!
  559. I represent a setting **stored** at `Smalltalk settings`.
  560. In the current implementation, `Smalltalk settings` is an object persisted in the localStorage.
  561. ## API
  562. A `Setting` value can be read using `value` and set using `value:`.
  563. Settings are accessed with `'key' asSetting` or `'key' asSettingIfAbsent: aDefaultValue`.
  564. To read the value of a setting you can also use the convenience:
  565. `theValueSet := 'any.characteristic' settingValue`
  566. or with a default using:
  567. `theEnsuredValueSet := 'any.characteristic' settingValueIfAbsent: true`!
  568. !Setting methodsFor: 'accessing'!
  569. defaultValue
  570. ^ defaultValue
  571. !
  572. defaultValue: aStringifiableObject
  573. defaultValue := aStringifiableObject
  574. !
  575. key
  576. ^ key
  577. !
  578. key: aString
  579. key := aString
  580. !
  581. value
  582. ^ Smalltalk settings at: self key ifAbsent: [ self defaultValue ]
  583. !
  584. value: aStringifiableObject
  585. ^ Smalltalk settings at: self key put: aStringifiableObject
  586. ! !
  587. !Setting class methodsFor: 'instance creation'!
  588. at: aString ifAbsent: aDefaultValue
  589. ^ super new
  590. key: aString;
  591. defaultValue: aDefaultValue;
  592. yourself
  593. !
  594. new
  595. self shouldNotImplement
  596. ! !
  597. Object subclass: #SmalltalkImage
  598. instanceVariableNames: 'globalJsVariables packageDictionary'
  599. package: 'Kernel-Infrastructure'!
  600. !SmalltalkImage commentStamp!
  601. I represent the Smalltalk system, wrapping
  602. operations of variable `$core` declared in `support/boot.js`.
  603. ## API
  604. I have only one instance, accessed with global variable `Smalltalk`.
  605. ## Classes
  606. Classes can be accessed using the following methods:
  607. - `#classes` answers the full list of Smalltalk classes in the system
  608. - `#globals #at:` answers a specific global (usually, a class) or `nil`
  609. ## Packages
  610. Packages can be accessed using the following methods:
  611. - `#packages` answers the full list of packages
  612. - `#packageAt:` answers a specific package or `nil`
  613. ## Parsing
  614. The `#parse:` method is used to parse Amber source code.
  615. It requires the `Compiler` package and the `support/parser.js` parser file in order to work.!
  616. !SmalltalkImage methodsFor: 'accessing'!
  617. cancelOptOut: anObject
  618. "A Smalltalk object has a 'a$cls' property.
  619. If this property is shadowed for anObject by optOut:,
  620. the object is treated as plain JS object.
  621. This removes the shadow and anObject is Smalltalk object
  622. again if it was before."
  623. <inlineJS: 'delete anObject.klass; delete anObject.a$cls;'>
  624. !
  625. core
  626. <inlineJS: 'return $core'>
  627. !
  628. globals
  629. <inlineJS: 'return $globals'>
  630. !
  631. includesKey: aKey
  632. <inlineJS: 'return $core.hasOwnProperty(aKey)'>
  633. !
  634. optOut: anObject
  635. "A Smalltalk object has a 'a$cls' property.
  636. This shadows the property for anObject.
  637. The object is treated as plain JS object following this."
  638. <inlineJS: 'anObject.klass = null; anObject.a$cls = null'>
  639. !
  640. parse: aString
  641. | result |
  642. [ result := self basicParse: aString ]
  643. tryCatch: [ :ex | (self parseError: ex parsing: aString) signal ].
  644. ^ result
  645. source: aString;
  646. yourself
  647. !
  648. pseudoVariableNames
  649. ^ #('self' 'super' 'nil' 'true' 'false' 'thisContext')
  650. !
  651. readJSObject: anObject
  652. <inlineJS: 'return $core.readJSObject(anObject)'>
  653. !
  654. reservedWords
  655. ^ #(
  656. "http://www.ecma-international.org/ecma-262/6.0/#sec-keywords"
  657. break case catch class const continue debugger
  658. default delete do else export extends finally
  659. for function if import in instanceof new
  660. return super switch this throw try typeof
  661. var void while with yield
  662. "in strict mode"
  663. let static
  664. "Amber protected words: these should not be compiled as-is when in code"
  665. arguments
  666. "http://www.ecma-international.org/ecma-262/6.0/#sec-future-reserved-words"
  667. await enum
  668. "in strict mode"
  669. implements interface package private protected public
  670. )
  671. !
  672. settings
  673. ^ SmalltalkSettings
  674. !
  675. version
  676. "Answer the version string of Amber"
  677. ^ '0.20.0-pre'
  678. ! !
  679. !SmalltalkImage methodsFor: 'accessing amd'!
  680. amdRequire
  681. ^ self core at: 'amdRequire'
  682. !
  683. defaultAmdNamespace
  684. ^ 'transport.defaultAmdNamespace' settingValue
  685. !
  686. defaultAmdNamespace: aString
  687. 'transport.defaultAmdNamespace' settingValue: aString
  688. ! !
  689. !SmalltalkImage methodsFor: 'classes'!
  690. classes
  691. <inlineJS: 'return $core.classes()'>
  692. !
  693. removeClass: aClass
  694. aClass isMetaclass ifTrue: [ self error: aClass asString, ' is a Metaclass and cannot be removed!!' ].
  695. aClass allSubclassesDo: [ :subclass | self error: aClass name, ' has a subclass: ', subclass name ].
  696. aClass traitUsers ifNotEmpty: [ self error: aClass name, ' has trait users.' ].
  697. self deleteClass: aClass.
  698. aClass setTraitComposition: #().
  699. aClass theMetaClass ifNotNil: [ :meta | meta setTraitComposition: #() ].
  700. SystemAnnouncer current
  701. announce: (ClassRemoved new
  702. theClass: aClass;
  703. yourself)
  704. ! !
  705. !SmalltalkImage methodsFor: 'error handling'!
  706. asSmalltalkException: anObject
  707. "A JavaScript exception may be thrown.
  708. We then need to convert it back to a Smalltalk object"
  709. ^ ((self isSmalltalkObject: anObject) and: [ anObject isKindOf: Error ])
  710. ifTrue: [ anObject ]
  711. ifFalse: [ JavaScriptException on: anObject ]
  712. !
  713. parseError: anException parsing: aString
  714. | pos |
  715. pos := (anException basicAt: 'location') start.
  716. ^ ParseError new messageText: 'Parse error on line ', pos line ,' column ' , pos column ,' : Unexpected character ', (anException basicAt: 'found')
  717. ! !
  718. !SmalltalkImage methodsFor: 'globals'!
  719. addGlobalJsVariable: aString
  720. self globalJsVariables add: aString
  721. !
  722. deleteGlobalJsVariable: aString
  723. self globalJsVariables remove: aString ifAbsent:[]
  724. !
  725. globalJsVariables
  726. ^ globalJsVariables ifNil: [
  727. globalJsVariables := #(window document process global), self legacyGlobalJsVariables ]
  728. ! !
  729. !SmalltalkImage methodsFor: 'packages'!
  730. createPackage: packageName
  731. | package announcement |
  732. package := self basicCreatePackage: packageName.
  733. announcement := PackageAdded new
  734. package: package;
  735. yourself.
  736. SystemAnnouncer current announce: announcement.
  737. ^ package
  738. !
  739. packageAt: packageName
  740. self deprecatedAPI: 'Use #packageAt:ifAbsent: directly.'.
  741. ^ self packageAt: packageName ifAbsent: []
  742. !
  743. packageAt: packageName ifAbsent: aBlock
  744. ^ self packageDictionary at: packageName ifAbsent: aBlock
  745. !
  746. packageDictionary
  747. ^ packageDictionary ifNil: [ packageDictionary := Dictionary new ]
  748. !
  749. packages
  750. "Return all Package instances in the system."
  751. ^ self packageDictionary values copy
  752. !
  753. removePackage: packageName
  754. "Removes a package and all its classes."
  755. | pkg |
  756. pkg := self packageAt: packageName ifAbsent: [ self error: 'Missing package: ', packageName ].
  757. pkg classes do: [ :each |
  758. self removeClass: each ].
  759. self packageDictionary removeKey: packageName
  760. !
  761. renamePackage: packageName to: newName
  762. "Rename a package."
  763. | pkg |
  764. pkg := self packageAt: packageName ifAbsent: [ self error: 'Missing package: ', packageName ].
  765. self packageAt: newName ifAbsent: [ self error: 'Already exists a package called: ', newName ].
  766. pkg name: newName; beDirty.
  767. self packageDictionary
  768. at: newName put: pkg;
  769. removeKey: packageName
  770. ! !
  771. !SmalltalkImage methodsFor: 'private'!
  772. adoptPackageDictionary
  773. self core packages keysAndValuesDo: [ :key :value | Package named: key javaScriptDescriptor: value ]
  774. !
  775. basicCreatePackage: packageName
  776. "Create and bind a new bare package with given name and return it."
  777. ^ self packageDictionary at: packageName ifAbsentPut: [ Package new: packageName ]
  778. !
  779. basicParse: aString
  780. ^ SmalltalkParser parse: aString
  781. !
  782. deleteClass: aClass
  783. "Deletes a class by deleting its binding only. Use #removeClass instead"
  784. <inlineJS: '$core.removeClass(aClass)'>
  785. !
  786. legacyGlobalJsVariables
  787. "Legacy array of global JavaScript variables.
  788. Only used for BW compat, to be removed."
  789. <inlineJS: 'return $core.globalJsVariables'>
  790. ! !
  791. !SmalltalkImage methodsFor: 'testing'!
  792. existsJsGlobal: aString
  793. ^ Platform globals
  794. at: aString
  795. ifPresent: [ true ]
  796. ifAbsent: [ false ]
  797. !
  798. isSmalltalkObject: anObject
  799. "Consider anObject a Smalltalk object if it has a 'a$cls' property.
  800. Note that this may be unaccurate"
  801. <inlineJS: 'return anObject.a$cls !!= null'>
  802. ! !
  803. SmalltalkImage class instanceVariableNames: 'current'!
  804. !SmalltalkImage class methodsFor: 'initialization'!
  805. initialize
  806. | st |
  807. st := self current.
  808. st globals at: 'Smalltalk' put: st
  809. ! !
  810. !SmalltalkImage class methodsFor: 'instance creation'!
  811. current
  812. ^ current ifNil: [ current := super new ] ifNotNil: [ self deprecatedAPI. current ]
  813. !
  814. new
  815. self shouldNotImplement
  816. ! !
  817. JSObjectProxy setTraitComposition: {TThenable} asTraitComposition!
  818. ! !
  819. !ProtoStream methodsFor: '*Kernel-Infrastructure'!
  820. nextPutJSObject: aJSObject
  821. self nextPut: aJSObject
  822. ! !
  823. !String methodsFor: '*Kernel-Infrastructure'!
  824. asJavaScriptPropertyName
  825. <inlineJS: 'return $core.st2prop(self)'>
  826. !
  827. asSetting
  828. "Answer aSetting dedicated to locally store a value using this string as key.
  829. Nil will be the default value."
  830. ^ Setting at: self ifAbsent: nil
  831. !
  832. asSettingIfAbsent: aDefaultValue
  833. "Answer aSetting dedicated to locally store a value using this string as key.
  834. Make this setting to have aDefaultValue."
  835. ^ Setting at: self ifAbsent: aDefaultValue
  836. !
  837. settingValue
  838. ^ self asSetting value
  839. !
  840. settingValue: aValue
  841. "Sets the value of the setting that will be locally stored using this string as key.
  842. Note that aValue can be any object that can be stringifyed"
  843. ^ self asSetting value: aValue
  844. !
  845. settingValueIfAbsent: aDefaultValue
  846. "Answer the value of the locally stored setting using this string as key.
  847. Use aDefaultValue in case no setting is found"
  848. ^ (self asSettingIfAbsent: aDefaultValue) value
  849. ! !