Helios-Helpers.st 8.8 KB

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