Platform-ImportExport.st 27 KB

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