Helios-Helpers.st 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. Smalltalk current createPackage: 'Helios-Helpers'!
  2. Object subclass: #HLGenerationOutput
  3. instanceVariableNames: 'sourceCodes protocol targetClass'
  4. package: 'Helios-Helpers'!
  5. !HLGenerationOutput commentStamp!
  6. I am a simple data object used to store the result of a generation process!
  7. !HLGenerationOutput methodsFor: 'accessing'!
  8. protocol
  9. ^ protocol
  10. !
  11. protocol: aString
  12. protocol := aString
  13. !
  14. sourceCodes
  15. ^ sourceCodes
  16. !
  17. sourceCodes: aCollection
  18. sourceCodes := aCollection
  19. !
  20. targetClass
  21. ^ targetClass
  22. !
  23. targetClass: aClass
  24. targetClass := aClass
  25. ! !
  26. !HLGenerationOutput methodsFor: 'initialization'!
  27. initialize
  28. super initialize.
  29. sourceCodes := OrderedCollection new
  30. ! !
  31. !HLGenerationOutput methodsFor: 'protocol'!
  32. addSourceCode: aString
  33. sourceCodes add: aString
  34. !
  35. serialize
  36. sourceCodes do: [ :methodSourceCode |
  37. (targetClass includesSelector: methodSourceCode selector)
  38. ifFalse: [
  39. targetClass
  40. compile: methodSourceCode sourceCode
  41. protocol: protocol ] ]
  42. ! !
  43. HLGenerationOutput subclass: #HLGenerationOutputWithIndex
  44. instanceVariableNames: 'index'
  45. package: 'Helios-Helpers'!
  46. !HLGenerationOutputWithIndex commentStamp!
  47. I am a simple data object used to store the result of a generation process.
  48. In addition of my super class, I have an index where to put the cursor at the end of the process for the first method created (aka. the first in `sourceCodes`)!
  49. !HLGenerationOutputWithIndex methodsFor: 'accessing'!
  50. index
  51. ^ index
  52. !
  53. index: anIndex
  54. index := anIndex
  55. ! !
  56. Object subclass: #HLGenerator
  57. instanceVariableNames: 'output'
  58. package: 'Helios-Helpers'!
  59. !HLGenerator commentStamp!
  60. I am the abstract super class of the generators.
  61. My main method is `generate` which produce an `output` object!
  62. !HLGenerator methodsFor: 'accessing'!
  63. class: aClass
  64. output targetClass: aClass
  65. !
  66. output
  67. ^ output
  68. ! !
  69. !HLGenerator methodsFor: 'initialization'!
  70. initialize
  71. super initialize.
  72. output := HLGenerationOutput new
  73. ! !
  74. !HLGenerator methodsFor: 'protocol'!
  75. compile
  76. output targetClass ifNil: [ self error: 'class should not be nil'].
  77. ! !
  78. HLGenerator subclass: #HLAccessorsGenerator
  79. instanceVariableNames: ''
  80. package: 'Helios-Helpers'!
  81. !HLAccessorsGenerator commentStamp!
  82. I am a generator used to compile the getters/setters of a class!
  83. !HLAccessorsGenerator methodsFor: 'double-dispatch'!
  84. accessorProtocolForObject
  85. output protocol: 'accessing'
  86. !
  87. accessorsSourceCodesForObject
  88. | sources |
  89. sources := OrderedCollection new.
  90. output targetClass instanceVariableNames sorted do: [ :each |
  91. sources
  92. add: (self getterFor: each);
  93. add: (self setterFor: each) ].
  94. output sourceCodes: sources
  95. ! !
  96. !HLAccessorsGenerator methodsFor: 'private'!
  97. getterFor: anInstanceVariable
  98. ^ HLMethodSourceCode new
  99. selector:anInstanceVariable;
  100. sourceCode: (String streamContents: [ :stream |
  101. stream << anInstanceVariable.
  102. stream cr tab.
  103. stream << '^ ' << anInstanceVariable ])
  104. !
  105. setterFor: anInstanceVariable
  106. ^ HLMethodSourceCode new
  107. selector: anInstanceVariable, ':';
  108. sourceCode: (String streamContents: [ :stream |
  109. stream << anInstanceVariable << ': anObject'.
  110. stream cr tab.
  111. stream << anInstanceVariable << ' := anObject' ])
  112. ! !
  113. !HLAccessorsGenerator methodsFor: 'protocol'!
  114. compile
  115. super compile.
  116. output targetClass
  117. accessorsSourceCodesWith: self;
  118. accessorProtocolWith: self
  119. ! !
  120. HLGenerator subclass: #HLInitializeGenerator
  121. instanceVariableNames: ''
  122. package: 'Helios-Helpers'!
  123. !HLInitializeGenerator commentStamp!
  124. I am used to double-dispatch the `initialize` method(s) generation.
  125. Usage:
  126. ^ HLInitializeGenerator new
  127. class: aClass;
  128. generate;
  129. output
  130. I am a disposable object!
  131. !HLInitializeGenerator methodsFor: 'double-dispatch'!
  132. initializeForObject
  133. output addSourceCode: self initializeCodeForObject
  134. !
  135. initializeIndexForObject
  136. output index: self computeIndexForObject
  137. !
  138. initializeProtocolForObject
  139. output protocol: self retrieveProtocolForObject
  140. ! !
  141. !HLInitializeGenerator methodsFor: 'initialization'!
  142. initialize
  143. super initialize.
  144. output := HLGenerationOutputWithIndex new
  145. ! !
  146. !HLInitializeGenerator methodsFor: 'private'!
  147. computeIndexForObject
  148. | instVars headerSize firstInstVarSize |
  149. "32 is the size of the `initiliaze super initialize` part"
  150. headerSize := 32.
  151. instVars := output targetClass instanceVariableNames.
  152. firstInstVarSize := instVars sorted
  153. ifEmpty: [ 0 ]
  154. ifNotEmpty:[ instVars first size + 4 ].
  155. ^ headerSize + firstInstVarSize
  156. !
  157. generateInitializeCodeForObject
  158. ^ String streamContents: [ :str || instVars size |
  159. instVars := output targetClass instanceVariableNames sorted.
  160. size := instVars size.
  161. str << 'initialize'.
  162. str cr tab << 'super initialize.';cr.
  163. str cr tab.
  164. instVars withIndexDo: [ :name :index |
  165. index ~= 1 ifTrue: [ str cr tab ].
  166. str << name << ' := nil'.
  167. index ~= size ifTrue: [ str << '.' ] ] ].
  168. !
  169. initializeCodeForObject
  170. ^ HLMethodSourceCode new
  171. selector: 'initialize';
  172. sourceCode: self generateInitializeCodeForObject;
  173. yourself
  174. !
  175. retrieveProtocolForObject
  176. ^ 'initialization'
  177. ! !
  178. !HLInitializeGenerator methodsFor: 'protocol'!
  179. compile
  180. super compile.
  181. output targetClass
  182. initializeSourceCodesWith: self;
  183. initializeIndexWith: self;
  184. initializeProtocolWith: self
  185. ! !
  186. Object subclass: #HLMethodClassifier
  187. instanceVariableNames: 'prefixMapping'
  188. package: 'Helios-Helpers'!
  189. !HLMethodClassifier commentStamp!
  190. I am in charge of categorizing methods following this strategy:
  191. - is it an accessor?
  192. - is it overriding a superclass method?
  193. - is it starting with a know prefix?
  194. - how are categorized the other implementations?!
  195. !HLMethodClassifier methodsFor: 'initialization'!
  196. buildPrefixDictionary
  197. prefixMapping := Dictionary new.
  198. prefixMapping
  199. at: 'test' put: 'tests';
  200. at: 'bench' put: 'benchmarking';
  201. at: 'copy' put: 'copying';
  202. at: 'initialize' put: 'initialization';
  203. at: 'accept' put: 'visitor';
  204. at: 'visit' put: 'visitor';
  205. at: 'signal' put: 'signalling';
  206. at: 'parse' put: 'parsing';
  207. at: 'add' put: 'adding';
  208. at: 'is' put: 'testing';
  209. at: 'as' put: 'converting';
  210. at: 'new' put: 'instance creation'.
  211. !
  212. initialize
  213. super initialize.
  214. self buildPrefixDictionary.
  215. ! !
  216. !HLMethodClassifier methodsFor: 'private'!
  217. classifyAccessor: aMethod
  218. | names selector |
  219. names := aMethod methodClass allInstanceVariableNames.
  220. selector := aMethod selector.
  221. (selector last = ':')
  222. ifTrue: [ "selector might be a setter"
  223. selector := selector allButLast ].
  224. (names includes: selector)
  225. ifFalse: [ ^ false ].
  226. aMethod protocol: 'accessing'.
  227. ^ true.
  228. !
  229. classifyByKnownPrefix: aMethod
  230. prefixMapping keysAndValuesDo: [ :prefix :protocol |
  231. (aMethod selector beginsWith: prefix)
  232. ifTrue: [
  233. aMethod protocol: protocol.
  234. ^ true ]].
  235. ^ false.
  236. !
  237. classifyByOtherImplementors: aMethod
  238. | protocolBag methods protocolToUse counter |
  239. protocolBag := Dictionary new.
  240. methods := HLReferencesModel new implementorsOf: aMethod selector.
  241. methods
  242. ifEmpty: [ ^ false ]
  243. ifNotEmpty: [
  244. methods
  245. do: [ :method || protocol |
  246. protocol := method method protocol.
  247. (aMethod methodClass = method methodClass)
  248. ifFalse: [
  249. ((protocol first = '*') or: [ protocol = aMethod defaultProtocol ])
  250. ifFalse: [
  251. protocolBag
  252. at: protocol
  253. put: (protocolBag at: protocol ifAbsent: [ 0 ]) + 1 ] ] ] ].
  254. protocolBag ifEmpty: [ ^ false ].
  255. protocolToUse := nil.
  256. counter := 0.
  257. protocolBag keysAndValuesDo: [ :key :value | value > counter
  258. ifTrue: [
  259. counter := value.
  260. protocolToUse := key ] ].
  261. aMethod protocol: protocolToUse.
  262. ^ true
  263. !
  264. classifyInSuperclassProtocol: aMethod
  265. | currentClass |
  266. currentClass := aMethod methodClass.
  267. [ currentClass superclass isNil ] whileFalse: [
  268. currentClass := currentClass superclass.
  269. (currentClass includesSelector: aMethod selector)
  270. ifTrue: [
  271. aMethod protocol: (currentClass >> aMethod selector) protocol.
  272. ^ true ]].
  273. ^ false.
  274. ! !
  275. !HLMethodClassifier methodsFor: 'protocol'!
  276. classify: aMethod
  277. (self classifyAccessor: aMethod)
  278. ifTrue: [ ^ aMethod category ].
  279. (self classifyInSuperclassProtocol: aMethod)
  280. ifTrue: [ ^ aMethod category ].
  281. (self classifyByKnownPrefix: aMethod)
  282. ifTrue: [ ^ aMethod category ].
  283. (self classifyByOtherImplementors: aMethod)
  284. ifTrue: [ ^ aMethod category ].
  285. !
  286. classifyAll: aCollectionOfMethods
  287. aCollectionOfMethods do: [ :method |
  288. self classify: method ]
  289. ! !
  290. Object subclass: #HLMethodSourceCode
  291. instanceVariableNames: 'selector sourceCode'
  292. package: 'Helios-Helpers'!
  293. !HLMethodSourceCode commentStamp!
  294. I am a simple data object keeping track of the information about a method that will be compiled at the end of the generation process!
  295. !HLMethodSourceCode methodsFor: 'accessing'!
  296. selector
  297. ^ selector
  298. !
  299. selector: aSelector
  300. selector := aSelector
  301. !
  302. sourceCode
  303. ^ sourceCode
  304. !
  305. sourceCode: aString
  306. sourceCode := aString
  307. ! !