Platform-ImportExport.st 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172
  1. Smalltalk createPackage: 'Platform-ImportExport'!
  2. Object subclass: #AbstractExporter
  3. instanceVariableNames: ''
  4. package: 'Platform-ImportExport'!
  5. !AbstractExporter commentStamp!
  6. I am an abstract exporter for Amber source code.
  7. ## API
  8. Use `#exportPackage:on:` to export a given package on a Stream.!
  9. !AbstractExporter methodsFor: 'accessing'!
  10. extensionMethodsOfPackage: aPackage
  11. | result |
  12. result := OrderedCollection new.
  13. (self extensionProtocolsOfPackage: aPackage) do: [ :each |
  14. result addAll: each methods ].
  15. ^ result
  16. !
  17. extensionProtocolsOfPackage: aPackage
  18. | extensionName result |
  19. extensionName := '*', aPackage name.
  20. result := OrderedCollection new.
  21. "The classes must be loaded since it is extensions only.
  22. Therefore topological sorting (dependency resolution) does not matter here.
  23. Not sorting topologically improves the speed by a number of magnitude.
  24. Not to shuffle diffs, classes are sorted by their name."
  25. (Smalltalk classes asArray sorted: [ :a :b | a name < b name ]) do: [ :each |
  26. ({each. each theMetaClass} copyWithout: nil) do: [ :behavior |
  27. (behavior protocols includes: extensionName) ifTrue: [
  28. result add: (ExportMethodProtocol name: extensionName theClass: behavior) ] ] ].
  29. ^ result
  30. ! !
  31. !AbstractExporter methodsFor: 'output'!
  32. exportPackage: aPackage on: aStream
  33. self subclassResponsibility
  34. ! !
  35. AbstractExporter subclass: #ChunkExporter
  36. instanceVariableNames: ''
  37. package: 'Platform-ImportExport'!
  38. !ChunkExporter commentStamp!
  39. I am an exporter dedicated to outputting Amber source code in the classic Smalltalk chunk format.
  40. I do not output any compiled code.!
  41. !ChunkExporter methodsFor: 'accessing'!
  42. extensionCategoriesOfPackage: aPackage
  43. "Issue #143: sort protocol alphabetically"
  44. | name map result |
  45. name := aPackage name.
  46. result := OrderedCollection new.
  47. (Package sortedClasses: Smalltalk classes) do: [ :each |
  48. {each. each theMetaClass} do: [ :aClass |
  49. map := Dictionary new.
  50. aClass protocolsDo: [ :category :methods |
  51. category = ('*', name) ifTrue: [ map at: category put: methods ] ].
  52. result addAll: ((map keys sorted: [ :a :b | a <= b ]) collect: [ :category |
  53. MethodCategory name: category theClass: aClass methods: (map at: category) ]) ] ].
  54. ^ result
  55. !
  56. ownCategoriesOfClass: aClass
  57. "Answer the protocols of aClass that are not package extensions"
  58. "Issue #143: sort protocol alphabetically"
  59. | map |
  60. map := Dictionary new.
  61. aClass protocolsDo: [ :each :methods |
  62. (each match: '^\*') ifFalse: [ map at: each put: methods ] ].
  63. ^ (map keys sorted: [ :a :b | a <= b ]) collect: [ :each |
  64. MethodCategory name: each theClass: aClass methods: (map at: each) ]
  65. !
  66. ownCategoriesOfMetaClass: aClass
  67. "Issue #143: sort protocol alphabetically"
  68. ^ self ownCategoriesOfClass: aClass theMetaClass
  69. !
  70. ownMethodProtocolsOfClass: aClass
  71. "Answer a collection of ExportMethodProtocol object of aClass that are not package extensions"
  72. ^ aClass ownProtocols collect: [ :each |
  73. ExportMethodProtocol name: each theClass: aClass ]
  74. ! !
  75. !ChunkExporter methodsFor: 'convenience'!
  76. chunkEscape: aString
  77. "Replace all occurrences of !! with !!!! and trim at both ends."
  78. ^ (aString replace: '!!' with: '!!!!') trimBoth
  79. ! !
  80. !ChunkExporter methodsFor: 'output'!
  81. exportBehavior: aBehavior on: aStream
  82. aBehavior exportBehaviorDefinitionTo: aStream using: self.
  83. self
  84. exportProtocols: (self ownMethodProtocolsOfClass: aBehavior)
  85. on: aStream
  86. !
  87. exportCategoryEpilogueOf: aCategory on: aStream
  88. aStream write: ' !!'; lf; lf
  89. !
  90. exportCategoryPrologueOf: aCategory on: aStream
  91. aStream
  92. write: '!!';
  93. print: aCategory theClass;
  94. write: ' methodsFor: ';
  95. print: aCategory;
  96. write: '!!'
  97. !
  98. exportDefinitionOf: aClass on: aStream
  99. "Chunk format."
  100. aStream
  101. print: aClass superclass;
  102. write: ' subclass: ';
  103. printSymbol: aClass name;
  104. lf.
  105. aClass traitComposition
  106. ifNotEmpty: [ aStream tab; write: aClass usesDefinition; lf ].
  107. aStream
  108. tab;
  109. write: 'instanceVariableNames: ';
  110. print: (' ' join: aClass instanceVariableNames);
  111. lf;
  112. tab;
  113. write: 'package: ';
  114. print: aClass category;
  115. write: '!!';
  116. lf.
  117. aClass comment ifNotEmpty: [ aStream
  118. write: '!!'; print: aClass; write: ' commentStamp!!'; lf;
  119. write: { self chunkEscape: aClass comment. '!!' }; lf ].
  120. aStream lf
  121. !
  122. exportMetaDefinitionOf: aClass on: aStream
  123. | classIvars classTraitComposition |
  124. classIvars := aClass class instanceVariableNames.
  125. classTraitComposition := aClass class traitComposition.
  126. (classIvars notEmpty or: [classTraitComposition notEmpty]) ifTrue: [
  127. aStream
  128. print: aClass theMetaClass.
  129. classTraitComposition
  130. ifEmpty: [ aStream space ]
  131. ifNotEmpty: [ aStream lf; tab; write: aClass class usesDefinition; lf; tab ].
  132. aStream
  133. write: 'instanceVariableNames: ';
  134. print: (' ' join: classIvars);
  135. write: '!!'; lf; lf ]
  136. !
  137. exportMethod: aMethod on: aStream
  138. aStream
  139. lf; lf; write: (self chunkEscape: aMethod source); lf;
  140. write: '!!'
  141. !
  142. exportPackage: aPackage on: aStream
  143. self
  144. exportPackageDefinitionOf: aPackage on: aStream;
  145. exportPackageImportsOf: aPackage on: aStream.
  146. aPackage sortedClasses do: [ :each |
  147. self exportBehavior: each on: aStream.
  148. each theMetaClass ifNotNil: [ :meta | self exportBehavior: meta on: aStream ] ].
  149. self
  150. exportProtocols: (self extensionProtocolsOfPackage: aPackage)
  151. on: aStream
  152. !
  153. exportPackageDefinitionOf: aPackage on: aStream
  154. aStream
  155. write: 'Smalltalk createPackage: ';
  156. print: aPackage name;
  157. write: '!!';
  158. lf
  159. !
  160. exportPackageImportsOf: aPackage on: aStream
  161. aPackage imports ifNotEmpty: [ :imports | aStream
  162. write: '(Smalltalk packageAt: ';
  163. print: aPackage name;
  164. write: { ') imports: '. self chunkEscape: aPackage importsDefinition. '!!' };
  165. lf ]
  166. !
  167. exportProtocol: aProtocol on: aStream
  168. self exportProtocolPrologueOf: aProtocol on: aStream.
  169. aProtocol methods do: [ :method |
  170. self exportMethod: method on: aStream ].
  171. self exportProtocolEpilogueOf: aProtocol on: aStream
  172. !
  173. exportProtocolEpilogueOf: aProtocol on: aStream
  174. aStream write: ' !!'; lf; lf
  175. !
  176. exportProtocolPrologueOf: aProtocol on: aStream
  177. aStream
  178. write: '!!';
  179. print: aProtocol theClass;
  180. write: ' methodsFor: ';
  181. print: aProtocol name;
  182. write: '!!'
  183. !
  184. exportProtocols: aCollection on: aStream
  185. aCollection do: [ :each |
  186. self exportProtocol: each on: aStream ]
  187. !
  188. exportTraitDefinitionOf: aClass on: aStream
  189. "Chunk format."
  190. aStream
  191. write: 'Trait named: '; printSymbol: aClass name; lf.
  192. aClass traitComposition
  193. ifNotEmpty: [ aStream tab; write: aClass usesDefinition; lf ].
  194. aStream
  195. tab; write: 'package: '; print: aClass category; write: '!!'; lf.
  196. aClass comment ifNotEmpty: [
  197. aStream
  198. write: '!!'; print: aClass; write: ' commentStamp!!'; lf;
  199. write: { self chunkEscape: aClass comment. '!!' }; lf ].
  200. aStream lf
  201. ! !
  202. AbstractExporter subclass: #Exporter
  203. instanceVariableNames: ''
  204. package: 'Platform-ImportExport'!
  205. !Exporter commentStamp!
  206. I am responsible for outputting Amber code into a JavaScript string.
  207. The generated output is enough to reconstruct the exported data, including Smalltalk source code and other metadata.
  208. ## Use case
  209. I am typically used to save code outside of the Amber runtime (committing to disk, etc.).!
  210. !Exporter methodsFor: 'accessing'!
  211. ownMethodsOfClass: aClass
  212. "Issue #143: sort methods alphabetically"
  213. ^ ((aClass methodDictionary values) sorted: [ :a :b | a selector <= b selector ])
  214. reject: [ :each | (each protocol match: '^\*') ]
  215. !
  216. ownMethodsOfMetaClass: aClass
  217. "Issue #143: sort methods alphabetically"
  218. ^ self ownMethodsOfClass: aClass theMetaClass
  219. ! !
  220. !Exporter methodsFor: 'output'!
  221. exportBehavior: aBehavior on: aStream
  222. aBehavior exportBehaviorDefinitionTo: aStream using: self.
  223. aBehavior ownMethods do: [ :method |
  224. self exportMethod: method on: aStream ]
  225. !
  226. exportDefinitionOf: aClass on: aStream
  227. aStream
  228. lf;
  229. write: {
  230. '$core.addClass('.
  231. aClass name asJavascript. ', '.
  232. aClass superclass ifNil: [ 'null' ] ifNotNil: [ :superclass | superclass asJavascript ]. ', '.
  233. aClass instanceVariableNames asJavascript. ', '.
  234. aClass category asJavascript.
  235. ');' }.
  236. aClass comment ifNotEmpty: [
  237. aStream
  238. lf;
  239. write: '//>>excludeStart("ide", pragmas.excludeIdeData);'; lf;
  240. write: { aClass asJavascript. '.comment='. aClass comment crlfSanitized asJavascript. ';' }; lf;
  241. write: '//>>excludeEnd("ide");' ].
  242. aStream lf
  243. !
  244. exportMetaDefinitionOf: aClass on: aStream
  245. aStream lf.
  246. aClass theMetaClass instanceVariableNames ifNotEmpty: [ :classIvars | aStream
  247. write: { aClass theMetaClass asJavascript. '.iVarNames = '. classIvars asJavascript. ';' };
  248. lf ]
  249. !
  250. exportMethod: aMethod on: aStream
  251. aStream
  252. write: '$core.addMethod('; lf;
  253. write: '$core.method({'; lf;
  254. write: { 'selector: '. aMethod selector asJavascript. ',' }; lf;
  255. write: { 'protocol: '. aMethod protocol asJavascript. ',' }; lf;
  256. write: { 'fn: '. aMethod fn compiledSource. ',' }; lf;
  257. write: '//>>excludeStart("ide", pragmas.excludeIdeData);'; lf;
  258. write: { 'args: '. aMethod arguments asJavascript. ',' }; lf;
  259. write: { 'source: '. aMethod source asJavascript. ',' }; lf;
  260. write: { 'referencedClasses: '. aMethod referencedClasses asJavascript. ',' }; lf;
  261. write: '//>>excludeEnd("ide");'; lf;
  262. write: { 'messageSends: '. aMethod messageSends asJavascript }; lf;
  263. write: '}),'; lf;
  264. write: { aMethod methodClass asJavascript. ');' }; lf; lf
  265. !
  266. exportPackage: aPackage on: aStream
  267. self
  268. exportPackagePrologueOf: aPackage on: aStream;
  269. exportPackageDefinitionOf: aPackage on: aStream;
  270. exportPackageContextOf: aPackage on: aStream;
  271. exportPackageImportsOf: aPackage on: aStream;
  272. exportPackageTransportOf: aPackage on: aStream.
  273. aPackage sortedClasses do: [ :each |
  274. self exportBehavior: each on: aStream.
  275. each theMetaClass ifNotNil: [ :meta | self exportBehavior: meta on: aStream ] ].
  276. self exportPackageTraitCompositionsOf: aPackage on: aStream.
  277. (self extensionMethodsOfPackage: aPackage) do: [ :each |
  278. self exportMethod: each on: aStream ].
  279. self exportPackageEpilogueOf: aPackage on: aStream
  280. !
  281. exportPackageBodyBlockPrologueOf: aPackage on: aStream
  282. aStream
  283. write: 'if(!!$boot.nilAsReceiver)$boot.nilAsReceiver=$boot.nil;'; lf;
  284. write: 'var $core=$boot.api,nil=$boot.nilAsReceiver,$recv=$boot.asReceiver,$globals=$boot.globals;'; lf;
  285. write: 'if(!!$boot.nilAsClass)$boot.nilAsClass=$boot.dnu;'; lf
  286. !
  287. exportPackageContextOf: aPackage on: aStream
  288. aStream
  289. write: {
  290. '$core.packages['.
  291. aPackage name asJavascript.
  292. '].innerEval = '.
  293. 'function (expr) { return eval(expr); }'.
  294. ';' };
  295. lf
  296. !
  297. exportPackageDefinitionOf: aPackage on: aStream
  298. aStream
  299. write: { '$core.addPackage('. aPackage name asJavascript. ');' };
  300. lf
  301. !
  302. exportPackageEpilogueOf: aPackage on: aStream
  303. self subclassResponsibility
  304. !
  305. exportPackageImportsOf: aPackage on: aStream
  306. aPackage importsAsJson ifNotEmpty: [ :imports |
  307. aStream
  308. write: {
  309. '$core.packages['.
  310. aPackage name asJavascript.
  311. '].imports = '.
  312. imports asJavascript.
  313. ';' };
  314. lf ]
  315. !
  316. exportPackagePrologueOf: aPackage on: aStream
  317. self subclassResponsibility
  318. !
  319. exportPackageTraitCompositionsOf: aPackage on: aStream
  320. aPackage traitCompositions ifNotEmpty: [ :traitCompositions |
  321. traitCompositions keysAndValuesDo: [ :key :value | self exportTraitComposition: value of: key on: aStream ].
  322. aStream lf ]
  323. !
  324. exportPackageTransportOf: aPackage on: aStream
  325. aStream
  326. write: {
  327. '$core.packages['.
  328. aPackage name asJavascript.
  329. '].transport = '.
  330. aPackage transport asJSONString.
  331. ';' };
  332. lf
  333. !
  334. exportTraitComposition: aTraitComposition of: aBehavior on: aStream
  335. aStream write: {
  336. '$core.setTraitComposition('.
  337. aTraitComposition asJavascript.
  338. ', '.
  339. aBehavior asJavascript.
  340. ');' };
  341. lf
  342. !
  343. exportTraitDefinitionOf: aClass on: aStream
  344. aStream
  345. lf;
  346. write: {
  347. '$core.addTrait('.
  348. aClass name asJavascript. ', '.
  349. aClass category asJavascript.
  350. ');' }.
  351. aClass comment ifNotEmpty: [
  352. aStream
  353. lf;
  354. write: '//>>excludeStart("ide", pragmas.excludeIdeData);'; lf;
  355. write: { aClass asJavascript. '.comment='. aClass comment crlfSanitized asJavascript. ';' }; lf;
  356. write: '//>>excludeEnd("ide");' ].
  357. aStream lf
  358. ! !
  359. Exporter subclass: #AmdExporter
  360. instanceVariableNames: 'namespace'
  361. package: 'Platform-ImportExport'!
  362. !AmdExporter commentStamp!
  363. I am used to export Packages in an AMD (Asynchronous Module Definition) JavaScript format.!
  364. !AmdExporter methodsFor: 'output'!
  365. exportPackageEpilogueOf: aPackage on: aStream
  366. aStream
  367. write: '});';
  368. lf
  369. !
  370. exportPackagePrologueOf: aPackage on: aStream
  371. | importsForOutput loadDependencies pragmaStart pragmaEnd |
  372. pragmaStart := ''.
  373. pragmaEnd := ''.
  374. importsForOutput := self importsForOutput: aPackage.
  375. loadDependencies := self amdNamesOfPackages: aPackage loadDependencies.
  376. importsForOutput value ifNotEmpty: [
  377. pragmaStart := String lf, '//>>excludeStart("imports", pragmas.excludeImports);', String lf.
  378. pragmaEnd := String lf, '//>>excludeEnd("imports");', String lf ].
  379. aStream
  380. write: {
  381. 'define('.
  382. ((#('amber/boot' ':1:'), importsForOutput value, #(':2:'), loadDependencies asArray sorted) asJavascript
  383. replace: ',\s*["'']:1:["'']' with: pragmaStart)
  384. replace: ',\s*["'']:2:["'']' with: pragmaEnd.
  385. ', function('.
  386. ((((#('$boot' ':1:'), importsForOutput key, #(':2:')) join: ',')
  387. replace: ',\s*:1:' with: pragmaStart)
  388. replace: ',\s*:2:' with: pragmaEnd).
  389. '){"use strict";' };
  390. lf.
  391. self exportPackageBodyBlockPrologueOf: aPackage on: aStream
  392. ! !
  393. !AmdExporter methodsFor: 'private'!
  394. amdNamesOfPackages: anArray
  395. ^ (anArray
  396. select: [ :each | (self amdNamespaceOfPackage: each) notNil ])
  397. collect: [ :each | (self amdNamespaceOfPackage: each), '/', each name ]
  398. !
  399. amdNamespaceOfPackage: aPackage
  400. ^ (aPackage transport type = 'amd')
  401. ifTrue: [ aPackage transport namespace ]
  402. ifFalse: [ nil ]
  403. !
  404. importsForOutput: aPackage
  405. "Returns an association where key is list of import variables
  406. and value is list of external dependencies, with ones imported as variables
  407. put at the beginning with same order as is in key.
  408. For example imports:{'jQuery'->'jquery'. 'bootstrap'} would yield
  409. #('jQuery') -> #('jquery' 'bootstrap')"
  410. | namedImports anonImports importVarNames |
  411. namedImports := #().
  412. anonImports := #().
  413. importVarNames := #().
  414. aPackage imports do: [ :each | each isString
  415. ifTrue: [ anonImports add: each ]
  416. ifFalse: [ namedImports add: each value.
  417. importVarNames add: each key ]].
  418. ^ importVarNames -> (namedImports, anonImports)
  419. ! !
  420. Object subclass: #ChunkParser
  421. instanceVariableNames: 'stream last'
  422. package: 'Platform-ImportExport'!
  423. !ChunkParser commentStamp!
  424. I am responsible for parsing aStream contents in the chunk format.
  425. ## API
  426. ChunkParser new
  427. stream: aStream;
  428. nextChunk!
  429. !ChunkParser methodsFor: 'accessing'!
  430. last
  431. ^ last
  432. !
  433. stream: aStream
  434. stream := aStream
  435. ! !
  436. !ChunkParser methodsFor: 'reading'!
  437. nextChunk
  438. "The chunk format (Smalltalk Interchange Format or Fileout format)
  439. is a trivial format but can be a bit tricky to understand:
  440. - Uses the exclamation mark as delimiter of chunks.
  441. - Inside a chunk a normal exclamation mark must be doubled.
  442. - A non empty chunk must be a valid Smalltalk expression.
  443. - A chunk on top level with a preceding empty chunk is an instruction chunk:
  444. - The object created by the expression then takes over reading chunks.
  445. This method returns next chunk as a String (trimmed), empty String (all whitespace) or nil."
  446. | char result chunk |
  447. result := '' writeStream.
  448. [ char := stream next.
  449. char notNil ] whileTrue: [
  450. char = '!!' ifTrue: [
  451. stream peek = '!!'
  452. ifTrue: [ stream next "skipping the escape double" ]
  453. ifFalse: [ ^ last := result contents trimBoth "chunk end marker found" ]].
  454. result nextPut: char ].
  455. ^ last := nil "a chunk needs to end with !!"
  456. ! !
  457. !ChunkParser class methodsFor: 'instance creation'!
  458. on: aStream
  459. ^ self new stream: aStream
  460. ! !
  461. Object subclass: #ClassCommentReader
  462. instanceVariableNames: 'class'
  463. package: 'Platform-ImportExport'!
  464. !ClassCommentReader commentStamp!
  465. I provide a mechanism for retrieving class comments stored on a file.
  466. See also `ClassCategoryReader`.!
  467. !ClassCommentReader methodsFor: 'accessing'!
  468. class: aClass
  469. class := aClass
  470. ! !
  471. !ClassCommentReader methodsFor: 'fileIn'!
  472. scanFrom: aChunkParser
  473. | chunk |
  474. chunk := aChunkParser nextChunk.
  475. chunk ifNotEmpty: [
  476. self setComment: chunk ].
  477. ! !
  478. !ClassCommentReader methodsFor: 'initialization'!
  479. initialize
  480. super initialize.
  481. ! !
  482. !ClassCommentReader methodsFor: 'private'!
  483. setComment: aString
  484. class comment: aString
  485. ! !
  486. Object subclass: #ClassProtocolReader
  487. instanceVariableNames: 'class category'
  488. package: 'Platform-ImportExport'!
  489. !ClassProtocolReader commentStamp!
  490. I provide a mechanism for retrieving class descriptions stored on a file in the Smalltalk chunk format.!
  491. !ClassProtocolReader methodsFor: 'accessing'!
  492. class: aClass category: aString
  493. class := aClass.
  494. category := aString
  495. ! !
  496. !ClassProtocolReader methodsFor: 'fileIn'!
  497. scanFrom: aChunkParser
  498. | chunk |
  499. [ chunk := aChunkParser nextChunk.
  500. chunk isEmpty ] whileFalse: [
  501. self compileMethod: chunk ]
  502. ! !
  503. !ClassProtocolReader methodsFor: 'initialization'!
  504. initialize
  505. super initialize.
  506. ! !
  507. !ClassProtocolReader methodsFor: 'private'!
  508. compileMethod: aString
  509. Compiler new install: aString forClass: class protocol: category
  510. ! !
  511. Object subclass: #ExportMethodProtocol
  512. instanceVariableNames: 'name theClass'
  513. package: 'Platform-ImportExport'!
  514. !ExportMethodProtocol commentStamp!
  515. I am an abstraction for a method protocol in a class / metaclass.
  516. I know of my class, name and methods.
  517. I am used when exporting a package.!
  518. !ExportMethodProtocol methodsFor: 'accessing'!
  519. methods
  520. ^ (self theClass methodsInProtocol: self name)
  521. sorted: [ :a :b | a selector <= b selector ]
  522. !
  523. name
  524. ^ name
  525. !
  526. name: aString
  527. name := aString
  528. !
  529. theClass
  530. ^ theClass
  531. !
  532. theClass: aClass
  533. theClass := aClass
  534. ! !
  535. !ExportMethodProtocol class methodsFor: 'instance creation'!
  536. name: aString theClass: aClass
  537. ^ self new
  538. name: aString;
  539. theClass: aClass;
  540. yourself
  541. ! !
  542. Object subclass: #Importer
  543. instanceVariableNames: 'lastSection lastChunk'
  544. package: 'Platform-ImportExport'!
  545. !Importer commentStamp!
  546. I can import Amber code from a string in the chunk format.
  547. ## API
  548. Importer new import: aString!
  549. !Importer methodsFor: 'accessing'!
  550. lastChunk
  551. ^ lastChunk
  552. !
  553. lastSection
  554. ^ lastSection
  555. ! !
  556. !Importer methodsFor: 'fileIn'!
  557. import: aStream
  558. | chunk result parser lastEmpty |
  559. parser := ChunkParser on: aStream.
  560. lastEmpty := false.
  561. lastSection := 'n/a, not started'.
  562. lastChunk := nil.
  563. [
  564. [ chunk := parser nextChunk.
  565. chunk isNil ] whileFalse: [
  566. chunk
  567. ifEmpty: [ lastEmpty := true ]
  568. ifNotEmpty: [
  569. lastSection := chunk.
  570. result := Compiler new evaluateExpression: chunk.
  571. lastEmpty
  572. ifTrue: [
  573. lastEmpty := false.
  574. result scanFrom: parser ]] ].
  575. lastSection := 'n/a, finished'
  576. ] on: Error do: [:e | lastChunk := parser last. e resignal ].
  577. ! !
  578. Error subclass: #PackageCommitError
  579. instanceVariableNames: ''
  580. package: 'Platform-ImportExport'!
  581. !PackageCommitError commentStamp!
  582. I get signaled when an attempt to commit a package has failed.!
  583. Object subclass: #PackageHandler
  584. instanceVariableNames: ''
  585. package: 'Platform-ImportExport'!
  586. !PackageHandler commentStamp!
  587. I am responsible for handling package loading and committing.
  588. I should not be used directly. Instead, use the corresponding `Package` methods.!
  589. !PackageHandler methodsFor: 'accessing'!
  590. chunkContentsFor: aPackage
  591. ^ String streamContents: [ :str |
  592. self chunkExporter exportPackage: aPackage on: str ]
  593. !
  594. chunkExporterClass
  595. ^ ChunkExporter
  596. !
  597. commitPathJsFor: aPackage
  598. self subclassResponsibility
  599. !
  600. commitPathStFor: aPackage
  601. self subclassResponsibility
  602. !
  603. contentsFor: aPackage
  604. ^ String streamContents: [ :str |
  605. self exporter exportPackage: aPackage on: str ]
  606. !
  607. exporterClass
  608. self subclassResponsibility
  609. ! !
  610. !PackageHandler methodsFor: 'committing'!
  611. commit: aPackage
  612. self
  613. commit: aPackage
  614. onSuccess: []
  615. onError: [ :error |
  616. PackageCommitError new
  617. messageText: 'Commiting failed with reason: "' , (error responseText) , '"';
  618. signal ]
  619. !
  620. commit: aPackage onSuccess: aBlock onError: anotherBlock
  621. self
  622. commitJsFileFor: aPackage
  623. onSuccess: [
  624. self
  625. commitStFileFor: aPackage
  626. onSuccess: [ aPackage beClean. aBlock value ]
  627. onError: anotherBlock ]
  628. onError: anotherBlock
  629. !
  630. commitJsFileFor: aPackage onSuccess: aBlock onError: anotherBlock
  631. self
  632. ajaxPutAt: (self commitPathJsFor: aPackage), '/', aPackage name, '.js'
  633. data: (self contentsFor: aPackage)
  634. onSuccess: aBlock
  635. onError: anotherBlock
  636. !
  637. commitStFileFor: aPackage onSuccess: aBlock onError: anotherBlock
  638. self
  639. ajaxPutAt: (self commitPathStFor: aPackage), '/', aPackage name, '.st'
  640. data: (self chunkContentsFor: aPackage)
  641. onSuccess: aBlock
  642. onError: anotherBlock
  643. ! !
  644. !PackageHandler methodsFor: 'error handling'!
  645. onCommitError: anError
  646. PackageCommitError new
  647. messageText: 'Commiting failed with reason: "' , (anError responseText) , '"';
  648. signal
  649. ! !
  650. !PackageHandler methodsFor: 'factory'!
  651. chunkExporter
  652. ^ self chunkExporterClass new
  653. !
  654. exporter
  655. ^ self exporterClass new
  656. ! !
  657. !PackageHandler methodsFor: 'loading'!
  658. load: aPackage
  659. self subclassResponsibility
  660. ! !
  661. !PackageHandler methodsFor: 'private'!
  662. ajaxPutAt: aURL data: aString onSuccess: aBlock onError: anotherBlock
  663. | xhr |
  664. xhr := Platform newXhr.
  665. xhr open: 'PUT' url: aURL async: true.
  666. xhr onreadystatechange: [
  667. xhr readyState = 4 ifTrue: [
  668. (xhr status >= 200 and: [ xhr status < 300 ])
  669. ifTrue: aBlock
  670. ifFalse: anotherBlock ]].
  671. xhr send: aString
  672. ! !
  673. PackageHandler subclass: #AmdPackageHandler
  674. instanceVariableNames: ''
  675. package: 'Platform-ImportExport'!
  676. !AmdPackageHandler commentStamp!
  677. I am responsible for handling package loading and committing.
  678. I should not be used directly. Instead, use the corresponding `Package` methods.!
  679. !AmdPackageHandler methodsFor: 'accessing'!
  680. commitPathJsFor: aPackage
  681. ^ self toUrl: (self namespaceFor: aPackage)
  682. !
  683. commitPathStFor: aPackage
  684. "If _source is not mapped, .st will be committed to .js path.
  685. It is recommended not to use _source as it can be deprecated."
  686. | path pathWithout |
  687. path := self toUrl: (self namespaceFor: aPackage), '/_source'.
  688. pathWithout := self commitPathJsFor: aPackage.
  689. ^ path = (pathWithout, '/_source') ifTrue: [ pathWithout ] ifFalse: [ path ]
  690. !
  691. exporterClass
  692. ^ AmdExporter
  693. ! !
  694. !AmdPackageHandler methodsFor: 'committing'!
  695. namespaceFor: aPackage
  696. ^ aPackage transport namespace
  697. ! !
  698. !AmdPackageHandler methodsFor: 'loading'!
  699. load: aPackage
  700. Smalltalk amdRequire
  701. ifNil: [ self error: 'AMD loader not present' ]
  702. ifNotNil: [ :require |
  703. require value: (Array with: (self namespaceFor: aPackage), '/', aPackage name ) ]
  704. ! !
  705. !AmdPackageHandler methodsFor: 'private'!
  706. toUrl: aString
  707. ^ Smalltalk amdRequire
  708. ifNil: [ self error: 'AMD loader not present' ]
  709. ifNotNil: [ :require | (require basicAt: 'toUrl') value: aString ]
  710. ! !
  711. !AmdPackageHandler class methodsFor: 'commit paths'!
  712. defaultNamespace
  713. ^ Smalltalk defaultAmdNamespace
  714. !
  715. defaultNamespace: aString
  716. Smalltalk defaultAmdNamespace: aString
  717. ! !
  718. Object subclass: #PackageTransport
  719. instanceVariableNames: 'package'
  720. package: 'Platform-ImportExport'!
  721. !PackageTransport commentStamp!
  722. I represent the transport mechanism used to commit a package.
  723. My concrete subclasses have a `#handler` to which committing is delegated.!
  724. !PackageTransport methodsFor: 'accessing'!
  725. commitHandlerClass
  726. self subclassResponsibility
  727. !
  728. definition
  729. ^ ''
  730. !
  731. package
  732. ^ package
  733. !
  734. package: aPackage
  735. package := aPackage
  736. !
  737. type
  738. ^ self class type
  739. ! !
  740. !PackageTransport methodsFor: 'committing'!
  741. commit
  742. self commitHandler commit: self package
  743. !
  744. commitOnSuccess: aBlock onError: anotherBlock
  745. self commitHandler
  746. commit: self package
  747. onSuccess: aBlock
  748. onError: anotherBlock
  749. ! !
  750. !PackageTransport methodsFor: 'converting'!
  751. asJSON
  752. ^ #{ 'type' -> self type }
  753. ! !
  754. !PackageTransport methodsFor: 'factory'!
  755. commitHandler
  756. ^ self commitHandlerClass new
  757. ! !
  758. !PackageTransport methodsFor: 'initialization'!
  759. setupFromJson: anObject
  760. "no op. override if needed in subclasses"
  761. ! !
  762. !PackageTransport methodsFor: 'loading'!
  763. load
  764. self commitHandler load: self package
  765. ! !
  766. PackageTransport class instanceVariableNames: 'registry'!
  767. !PackageTransport class methodsFor: 'accessing'!
  768. classRegisteredFor: aString
  769. ^ registry at: aString
  770. !
  771. defaultType
  772. ^ AmdPackageTransport type
  773. !
  774. type
  775. "Override in subclasses"
  776. ^ nil
  777. ! !
  778. !PackageTransport class methodsFor: 'initialization'!
  779. initialize
  780. super initialize.
  781. self == PackageTransport
  782. ifTrue: [ registry := #{} ]
  783. ifFalse: [ self register ]
  784. ! !
  785. !PackageTransport class methodsFor: 'instance creation'!
  786. for: aString
  787. ^ (self classRegisteredFor: aString) new
  788. !
  789. fromJson: anObject
  790. anObject ifNil: [ ^ self for: self defaultType ].
  791. ^ (self for: anObject type)
  792. setupFromJson: anObject;
  793. yourself
  794. ! !
  795. !PackageTransport class methodsFor: 'registration'!
  796. register
  797. PackageTransport register: self
  798. !
  799. register: aClass
  800. aClass type ifNotNil: [
  801. registry at: aClass type put: aClass ]
  802. ! !
  803. PackageTransport subclass: #AmdPackageTransport
  804. instanceVariableNames: 'namespace'
  805. package: 'Platform-ImportExport'!
  806. !AmdPackageTransport commentStamp!
  807. I am the default transport for committing packages.
  808. See `AmdExporter` and `AmdPackageHandler`.!
  809. !AmdPackageTransport methodsFor: 'accessing'!
  810. commitHandlerClass
  811. ^ AmdPackageHandler
  812. !
  813. definition
  814. ^ String streamContents: [ :stream | stream
  815. write: { self class name. ' namespace: ' }; print: self namespace ]
  816. !
  817. namespace
  818. ^ namespace ifNil: [ self defaultNamespace ]
  819. !
  820. namespace: aString
  821. namespace := aString
  822. ! !
  823. !AmdPackageTransport methodsFor: 'actions'!
  824. setPath: aString
  825. "Set the path the the receiver's `namespace`"
  826. (require basicAt: 'config') value: #{
  827. 'paths' -> #{
  828. self namespace -> aString
  829. }
  830. }.
  831. ! !
  832. !AmdPackageTransport methodsFor: 'converting'!
  833. asJSON
  834. ^ super asJSON
  835. at: 'amdNamespace' put: self namespace;
  836. yourself
  837. ! !
  838. !AmdPackageTransport methodsFor: 'defaults'!
  839. defaultNamespace
  840. ^ Smalltalk defaultAmdNamespace
  841. ! !
  842. !AmdPackageTransport methodsFor: 'initialization'!
  843. setupFromJson: anObject
  844. self namespace: (anObject at: 'amdNamespace')
  845. ! !
  846. !AmdPackageTransport methodsFor: 'printing'!
  847. printOn: aStream
  848. super printOn: aStream.
  849. aStream
  850. nextPutAll: ' (AMD Namespace: ';
  851. nextPutAll: self namespace;
  852. nextPutAll: ')'
  853. ! !
  854. !AmdPackageTransport class methodsFor: 'accessing'!
  855. type
  856. ^ 'amd'
  857. ! !
  858. !AmdPackageTransport class methodsFor: 'instance creation'!
  859. namespace: aString
  860. ^ self new
  861. namespace: aString;
  862. yourself
  863. ! !
  864. !Behavior methodsFor: '*Platform-ImportExport'!
  865. commentStamp
  866. ^ ClassCommentReader new
  867. class: self;
  868. yourself
  869. !
  870. commentStamp: aStamp prior: prior
  871. ^ self commentStamp
  872. !
  873. methodsFor: aString
  874. ^ ClassProtocolReader new
  875. class: self category: aString;
  876. yourself
  877. !
  878. methodsFor: aString stamp: aStamp
  879. "Added for file-in compatibility, ignores stamp."
  880. ^ self methodsFor: aString
  881. ! !
  882. !BehaviorBody methodsFor: '*Platform-ImportExport'!
  883. exportBehaviorDefinitionTo: aStream using: anExporter
  884. self subclassResponsibility
  885. ! !
  886. !Class methodsFor: '*Platform-ImportExport'!
  887. exportBehaviorDefinitionTo: aStream using: anExporter
  888. anExporter exportDefinitionOf: self on: aStream
  889. ! !
  890. !Metaclass methodsFor: '*Platform-ImportExport'!
  891. exportBehaviorDefinitionTo: aStream using: anExporter
  892. anExporter exportMetaDefinitionOf: self instanceClass on: aStream
  893. ! !
  894. !Package methodsFor: '*Platform-ImportExport'!
  895. commit
  896. ^ self transport commit
  897. !
  898. load
  899. ^ self transport load
  900. !
  901. loadFromNamespace: aString
  902. ^ self transport
  903. namespace: aString;
  904. load
  905. ! !
  906. !Package class methodsFor: '*Platform-ImportExport'!
  907. load: aPackageName
  908. (self named: aPackageName) load
  909. !
  910. load: aPackageName fromNamespace: aString
  911. (self named: aPackageName) loadFromNamespace: aString
  912. ! !
  913. !Trait methodsFor: '*Platform-ImportExport'!
  914. exportBehaviorDefinitionTo: aStream using: anExporter
  915. anExporter exportTraitDefinitionOf: self on: aStream
  916. ! !