Platform-ImportExport.st 27 KB

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