Platform-ImportExport.st 28 KB

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