Helios-Helpers.st 11 KB

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