Platform-ImportExport.st 28 KB

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