Platform-ImportExport.st 28 KB

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