Platform-ImportExport.st 28 KB

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