Compiler-Semantic.st 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. Smalltalk createPackage: 'Compiler-Semantic'!
  2. Object subclass: #LexicalScope
  3. instanceVariableNames: 'node instruction temps args outerScope blockIndex'
  4. package: 'Compiler-Semantic'!
  5. !LexicalScope commentStamp!
  6. I represent a lexical scope where variable names are associated with ScopeVars
  7. Instances are used for block scopes. Method scopes are instances of MethodLexicalScope.
  8. I am attached to a ScopeVar and method/block nodes.
  9. Each context (method/closure) get a fresh scope that inherits from its outer scope.!
  10. !LexicalScope methodsFor: 'accessing'!
  11. alias
  12. ^ '$ctx', self scopeLevel asString
  13. !
  14. allVariableNames
  15. ^ self args keys, self temps keys
  16. !
  17. args
  18. ^ args ifNil: [ args := Dictionary new ]
  19. !
  20. bindingFor: aStringOrNode
  21. ^ self pseudoVars at: aStringOrNode value ifAbsent: [
  22. self args at: aStringOrNode value ifAbsent: [
  23. self temps at: aStringOrNode value ifAbsent: [ nil ]]]
  24. !
  25. blockIndex
  26. ^ blockIndex ifNil: [ 0 ]
  27. !
  28. blockIndex: anInteger
  29. blockIndex := anInteger
  30. !
  31. instruction
  32. ^ instruction
  33. !
  34. instruction: anIRInstruction
  35. instruction := anIRInstruction
  36. !
  37. lookupVariable: aNode
  38. | lookup |
  39. lookup := (self bindingFor: aNode).
  40. lookup ifNil: [
  41. lookup := self outerScope ifNotNil: [
  42. (self outerScope lookupVariable: aNode) ]].
  43. ^ lookup
  44. !
  45. methodScope
  46. ^ self outerScope ifNotNil: [
  47. self outerScope methodScope ]
  48. !
  49. node
  50. "Answer the node in which I am defined"
  51. ^ node
  52. !
  53. node: aNode
  54. node := aNode
  55. !
  56. outerScope
  57. ^ outerScope
  58. !
  59. outerScope: aLexicalScope
  60. outerScope := aLexicalScope
  61. !
  62. pseudoVars
  63. ^ self methodScope pseudoVars
  64. !
  65. scopeLevel
  66. self outerScope ifNil: [ ^ 1 ].
  67. self isInlined ifTrue: [ ^ self outerScope scopeLevel ].
  68. ^ self outerScope scopeLevel + 1
  69. !
  70. temps
  71. ^ temps ifNil: [ temps := Dictionary new ]
  72. ! !
  73. !LexicalScope methodsFor: 'adding'!
  74. addArg: aString
  75. self args at: aString put: (ArgVar on: aString).
  76. (self args at: aString) scope: self
  77. !
  78. addTemp: aString
  79. self temps at: aString put: (TempVar on: aString).
  80. (self temps at: aString) scope: self
  81. ! !
  82. !LexicalScope methodsFor: 'testing'!
  83. canInlineNonLocalReturns
  84. ^ self isInlined and: [ self outerScope canInlineNonLocalReturns ]
  85. !
  86. isBlockScope
  87. ^ self isMethodScope not
  88. !
  89. isInlined
  90. ^ self instruction notNil and: [
  91. self instruction isInlined ]
  92. !
  93. isMethodScope
  94. ^ false
  95. ! !
  96. LexicalScope subclass: #MethodLexicalScope
  97. instanceVariableNames: 'iVars pseudoVars unknownVariables localReturn nonLocalReturns'
  98. package: 'Compiler-Semantic'!
  99. !MethodLexicalScope commentStamp!
  100. I represent a method scope.!
  101. !MethodLexicalScope methodsFor: 'accessing'!
  102. allVariableNames
  103. ^ super allVariableNames, self iVars keys
  104. !
  105. bindingFor: aNode
  106. ^ (super bindingFor: aNode) ifNil: [
  107. self iVars at: aNode value ifAbsent: [ nil ]]
  108. !
  109. iVars
  110. ^ iVars ifNil: [ iVars := Dictionary new ]
  111. !
  112. localReturn
  113. ^ localReturn ifNil: [ false ]
  114. !
  115. localReturn: aBoolean
  116. localReturn := aBoolean
  117. !
  118. methodScope
  119. ^ self
  120. !
  121. nonLocalReturns
  122. ^ nonLocalReturns ifNil: [ nonLocalReturns := OrderedCollection new ]
  123. !
  124. pseudoVars
  125. pseudoVars ifNil: [
  126. pseudoVars := Dictionary new.
  127. Smalltalk pseudoVariableNames do: [ :each |
  128. pseudoVars at: each put: ((PseudoVar on: each)
  129. scope: self methodScope;
  130. yourself) ]].
  131. ^ pseudoVars
  132. !
  133. unknownVariables
  134. ^ unknownVariables ifNil: [ unknownVariables := OrderedCollection new ]
  135. ! !
  136. !MethodLexicalScope methodsFor: 'adding'!
  137. addIVar: aString
  138. self iVars at: aString put: (InstanceVar on: aString).
  139. (self iVars at: aString) scope: self
  140. !
  141. addNonLocalReturn: aScope
  142. self nonLocalReturns add: aScope
  143. !
  144. removeNonLocalReturn: aScope
  145. self nonLocalReturns remove: aScope ifAbsent: []
  146. ! !
  147. !MethodLexicalScope methodsFor: 'testing'!
  148. canInlineNonLocalReturns
  149. ^ true
  150. !
  151. hasLocalReturn
  152. ^ self localReturn
  153. !
  154. hasNonLocalReturn
  155. ^ self nonLocalReturns notEmpty
  156. !
  157. isMethodScope
  158. ^ true
  159. ! !
  160. Object subclass: #ScopeVar
  161. instanceVariableNames: 'scope name'
  162. package: 'Compiler-Semantic'!
  163. !ScopeVar commentStamp!
  164. I am an entry in a LexicalScope that gets associated with variable nodes of the same name.
  165. There are 4 different subclasses of vars: temp vars, local vars, args, and unknown/global vars.!
  166. !ScopeVar methodsFor: 'accessing'!
  167. alias
  168. ^ self name asVariableName
  169. !
  170. name
  171. ^ name
  172. !
  173. name: aString
  174. name := aString
  175. !
  176. scope
  177. ^ scope
  178. !
  179. scope: aScope
  180. scope := aScope
  181. ! !
  182. !ScopeVar methodsFor: 'testing'!
  183. isArgVar
  184. ^ false
  185. !
  186. isClassRefVar
  187. ^ false
  188. !
  189. isImmutable
  190. ^ false
  191. !
  192. isInstanceVar
  193. ^ false
  194. !
  195. isPseudoVar
  196. ^ false
  197. !
  198. isTempVar
  199. ^ false
  200. !
  201. isUnknownVar
  202. ^ false
  203. !
  204. validateAssignment
  205. (self isArgVar or: [ self isPseudoVar ]) ifTrue: [
  206. InvalidAssignmentError new
  207. variableName: self name;
  208. signal]
  209. ! !
  210. !ScopeVar class methodsFor: 'instance creation'!
  211. on: aString
  212. ^ self new
  213. name: aString;
  214. yourself
  215. ! !
  216. ScopeVar subclass: #AliasVar
  217. instanceVariableNames: 'node'
  218. package: 'Compiler-Semantic'!
  219. !AliasVar commentStamp!
  220. I am an internally defined variable by the compiler!
  221. !AliasVar methodsFor: 'accessing'!
  222. node
  223. ^ node
  224. !
  225. node: aNode
  226. node := aNode
  227. ! !
  228. ScopeVar subclass: #ArgVar
  229. instanceVariableNames: ''
  230. package: 'Compiler-Semantic'!
  231. !ArgVar commentStamp!
  232. I am an argument of a method or block.!
  233. !ArgVar methodsFor: 'testing'!
  234. isArgVar
  235. ^ true
  236. !
  237. isImmutable
  238. ^ true
  239. ! !
  240. ScopeVar subclass: #ClassRefVar
  241. instanceVariableNames: ''
  242. package: 'Compiler-Semantic'!
  243. !ClassRefVar commentStamp!
  244. I am an class reference variable!
  245. !ClassRefVar methodsFor: 'accessing'!
  246. alias
  247. "Fixes issue #190.
  248. A function is created in the method definition, answering the class or nil.
  249. See JSStream >> #nextPutClassRefFunction:"
  250. ^ '$', self name, '()'
  251. ! !
  252. !ClassRefVar methodsFor: 'testing'!
  253. isClassRefVar
  254. ^ true
  255. !
  256. isImmutable
  257. ^ true
  258. ! !
  259. ScopeVar subclass: #InstanceVar
  260. instanceVariableNames: ''
  261. package: 'Compiler-Semantic'!
  262. !InstanceVar commentStamp!
  263. I am an instance variable of a method or block.!
  264. !InstanceVar methodsFor: 'testing'!
  265. alias
  266. ^ 'self["@', self name, '"]'
  267. !
  268. isInstanceVar
  269. ^ true
  270. ! !
  271. ScopeVar subclass: #PseudoVar
  272. instanceVariableNames: ''
  273. package: 'Compiler-Semantic'!
  274. !PseudoVar commentStamp!
  275. I am an pseudo variable.
  276. The five Smalltalk pseudo variables are: 'self', 'super', 'nil', 'true' and 'false'!
  277. !PseudoVar methodsFor: 'accessing'!
  278. alias
  279. ^ self name
  280. ! !
  281. !PseudoVar methodsFor: 'testing'!
  282. isImmutable
  283. ^ true
  284. !
  285. isPseudoVar
  286. ^ true
  287. ! !
  288. ScopeVar subclass: #TempVar
  289. instanceVariableNames: ''
  290. package: 'Compiler-Semantic'!
  291. !TempVar commentStamp!
  292. I am an temporary variable of a method or block.!
  293. !TempVar methodsFor: 'testing'!
  294. isTempVar
  295. ^ true
  296. ! !
  297. ScopeVar subclass: #UnknownVar
  298. instanceVariableNames: ''
  299. package: 'Compiler-Semantic'!
  300. !UnknownVar commentStamp!
  301. I am an unknown variable. Amber uses unknown variables as JavaScript globals!
  302. !UnknownVar methodsFor: 'testing'!
  303. isUnknownVar
  304. ^ true
  305. ! !
  306. NodeVisitor subclass: #SemanticAnalyzer
  307. instanceVariableNames: 'currentScope blockIndex theClass classReferences messageSends superSends'
  308. package: 'Compiler-Semantic'!
  309. !SemanticAnalyzer commentStamp!
  310. I semantically analyze the abstract syntax tree and annotate it with informations such as non local returns and variable scopes.!
  311. !SemanticAnalyzer methodsFor: 'accessing'!
  312. classReferences
  313. ^ classReferences ifNil: [ classReferences := Set new ]
  314. !
  315. messageSends
  316. ^ messageSends ifNil: [ messageSends := Dictionary new ]
  317. !
  318. superSends
  319. ^ superSends ifNil: [ superSends := Dictionary new ]
  320. !
  321. theClass
  322. ^ theClass
  323. !
  324. theClass: aClass
  325. theClass := aClass
  326. ! !
  327. !SemanticAnalyzer methodsFor: 'error handling'!
  328. errorShadowingVariable: aString
  329. ShadowingVariableError new
  330. variableName: aString;
  331. signal
  332. !
  333. errorUnknownVariable: aNode
  334. "Throw an error if the variable is undeclared in the global JS scope (i.e. window).
  335. We allow all variables listed by Smalltalk>>#globalJsVariables.
  336. This list includes: `jQuery`, `window`, `document`, `process` and `global`
  337. for nodejs and browser environments.
  338. This is only to make sure compilation works on both browser-based and nodejs environments.
  339. The ideal solution would be to use a pragma instead"
  340. | identifier |
  341. identifier := aNode value.
  342. ((Smalltalk globalJsVariables includes: identifier) not
  343. and: [ self isVariableGloballyUndefined: identifier ])
  344. ifTrue: [
  345. UnknownVariableError new
  346. variableName: aNode value;
  347. signal ]
  348. ifFalse: [
  349. currentScope methodScope unknownVariables add: aNode value ]
  350. ! !
  351. !SemanticAnalyzer methodsFor: 'factory'!
  352. newBlockScope
  353. ^ self newScopeOfClass: LexicalScope
  354. !
  355. newMethodScope
  356. ^ self newScopeOfClass: MethodLexicalScope
  357. !
  358. newScopeOfClass: aLexicalScopeClass
  359. ^ aLexicalScopeClass new
  360. outerScope: currentScope;
  361. yourself
  362. ! !
  363. !SemanticAnalyzer methodsFor: 'private'!
  364. nextBlockIndex
  365. blockIndex ifNil: [ blockIndex := 0 ].
  366. blockIndex := blockIndex + 1.
  367. ^ blockIndex
  368. ! !
  369. !SemanticAnalyzer methodsFor: 'scope'!
  370. popScope
  371. currentScope ifNotNil: [
  372. currentScope := currentScope outerScope ]
  373. !
  374. pushScope: aScope
  375. aScope outerScope: currentScope.
  376. currentScope := aScope
  377. !
  378. validateVariableScope: aString
  379. "Validate the variable scope in by doing a recursive lookup, up to the method scope"
  380. (currentScope lookupVariable: aString) ifNotNil: [
  381. self errorShadowingVariable: aString ]
  382. ! !
  383. !SemanticAnalyzer methodsFor: 'testing'!
  384. isVariableGloballyUndefined: aString
  385. <return eval('typeof ' + aString + ' == "undefined"')>
  386. ! !
  387. !SemanticAnalyzer methodsFor: 'visiting'!
  388. visitAssignmentNode: aNode
  389. super visitAssignmentNode: aNode.
  390. aNode left beAssigned
  391. !
  392. visitBlockNode: aNode
  393. self pushScope: self newBlockScope.
  394. aNode scope: currentScope.
  395. currentScope node: aNode.
  396. currentScope blockIndex: self nextBlockIndex.
  397. aNode parameters do: [ :each |
  398. self validateVariableScope: each.
  399. currentScope addArg: each ].
  400. super visitBlockNode: aNode.
  401. self popScope
  402. !
  403. visitCascadeNode: aNode
  404. super visitCascadeNode: aNode.
  405. aNode nodes first superSend ifTrue: [
  406. aNode nodes do: [ :each | each superSend: true ] ]
  407. !
  408. visitMethodNode: aNode
  409. self pushScope: self newMethodScope.
  410. aNode scope: currentScope.
  411. currentScope node: aNode.
  412. self theClass allInstanceVariableNames do: [ :each |
  413. currentScope addIVar: each ].
  414. aNode arguments do: [ :each |
  415. self validateVariableScope: each.
  416. currentScope addArg: each ].
  417. super visitMethodNode: aNode.
  418. aNode
  419. classReferences: self classReferences;
  420. sendIndexes: self messageSends;
  421. superSends: self superSends keys.
  422. self popScope
  423. !
  424. visitReturnNode: aNode
  425. aNode scope: currentScope.
  426. currentScope isMethodScope
  427. ifTrue: [ currentScope localReturn: true ]
  428. ifFalse: [ currentScope methodScope addNonLocalReturn: currentScope ].
  429. super visitReturnNode: aNode
  430. !
  431. visitSendNode: aNode
  432. aNode receiver value = 'super'
  433. ifTrue: [
  434. aNode superSend: true.
  435. aNode receiver value: 'self'.
  436. self superSends at: aNode selector ifAbsentPut: [ Set new ].
  437. (self superSends at: aNode selector) add: aNode ]
  438. ifFalse: [ (IRSendInliner inlinedSelectors includes: aNode selector) ifTrue: [
  439. aNode shouldBeInlined: true.
  440. aNode receiver ifNotNil: [ :receiver |
  441. receiver shouldBeAliased: true ] ] ].
  442. self messageSends at: aNode selector ifAbsentPut: [ Set new ].
  443. (self messageSends at: aNode selector) add: aNode.
  444. aNode index: (self messageSends at: aNode selector) size.
  445. super visitSendNode: aNode
  446. !
  447. visitSequenceNode: aNode
  448. aNode temps do: [ :each |
  449. self validateVariableScope: each.
  450. currentScope addTemp: each ].
  451. super visitSequenceNode: aNode
  452. !
  453. visitVariableNode: aNode
  454. "Bind a ScopeVar to aNode by doing a lookup in the current scope.
  455. If no ScopeVar is found, bind a UnknowVar and throw an error."
  456. | binding |
  457. binding := currentScope lookupVariable: aNode.
  458. binding ifNil: [
  459. aNode value isCapitalized
  460. ifTrue: [ "Capital letter variables might be globals."
  461. binding := ClassRefVar new name: aNode value; yourself.
  462. self classReferences add: aNode value]
  463. ifFalse: [
  464. self errorUnknownVariable: aNode.
  465. binding := UnknownVar new name: aNode value; yourself ] ].
  466. aNode binding: binding.
  467. ! !
  468. !SemanticAnalyzer class methodsFor: 'instance creation'!
  469. on: aClass
  470. ^ self new
  471. theClass: aClass;
  472. yourself
  473. ! !