Compiler-Semantic.st 11 KB

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