Platform-ImportExport.st 28 KB

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