Platform-ImportExport.st 27 KB

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