Compiler-Semantic.st 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. Smalltalk current 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 current 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. isInstanceVar
  190. ^ false
  191. !
  192. isPseudoVar
  193. ^ false
  194. !
  195. isTempVar
  196. ^ false
  197. !
  198. isUnknownVar
  199. ^ false
  200. !
  201. validateAssignment
  202. (self isArgVar or: [ self isPseudoVar ]) ifTrue: [
  203. InvalidAssignmentError new
  204. variableName: self name;
  205. signal]
  206. ! !
  207. !ScopeVar class methodsFor: 'instance creation'!
  208. on: aString
  209. ^ self new
  210. name: aString;
  211. yourself
  212. ! !
  213. ScopeVar subclass: #AliasVar
  214. instanceVariableNames: 'node'
  215. package: 'Compiler-Semantic'!
  216. !AliasVar commentStamp!
  217. I am an internally defined variable by the compiler!
  218. !AliasVar methodsFor: 'accessing'!
  219. node
  220. ^ node
  221. !
  222. node: aNode
  223. node := aNode
  224. ! !
  225. ScopeVar subclass: #ArgVar
  226. instanceVariableNames: ''
  227. package: 'Compiler-Semantic'!
  228. !ArgVar commentStamp!
  229. I am an argument of a method or block.!
  230. !ArgVar methodsFor: 'testing'!
  231. isArgVar
  232. ^ true
  233. ! !
  234. ScopeVar subclass: #ClassRefVar
  235. instanceVariableNames: ''
  236. package: 'Compiler-Semantic'!
  237. !ClassRefVar commentStamp!
  238. I am an class reference variable!
  239. !ClassRefVar methodsFor: 'accessing'!
  240. alias
  241. "Fixes issue #190.
  242. A function is created in the method definition, answering the class or nil.
  243. See JSStream >> #nextPutClassRefFunction:"
  244. ^ '$', self name, '()'
  245. ! !
  246. !ClassRefVar methodsFor: 'testing'!
  247. isClassRefVar
  248. ^ true
  249. ! !
  250. ScopeVar subclass: #InstanceVar
  251. instanceVariableNames: ''
  252. package: 'Compiler-Semantic'!
  253. !InstanceVar commentStamp!
  254. I am an instance variable of a method or block.!
  255. !InstanceVar methodsFor: 'testing'!
  256. alias
  257. ^ 'self["@', self name, '"]'
  258. !
  259. isInstanceVar
  260. ^ true
  261. ! !
  262. ScopeVar subclass: #PseudoVar
  263. instanceVariableNames: ''
  264. package: 'Compiler-Semantic'!
  265. !PseudoVar commentStamp!
  266. I am an pseudo variable.
  267. The five Smalltalk pseudo variables are: 'self', 'super', 'nil', 'true' and 'false'!
  268. !PseudoVar methodsFor: 'accessing'!
  269. alias
  270. ^ self name
  271. ! !
  272. !PseudoVar methodsFor: 'testing'!
  273. isPseudoVar
  274. ^ true
  275. ! !
  276. ScopeVar subclass: #TempVar
  277. instanceVariableNames: ''
  278. package: 'Compiler-Semantic'!
  279. !TempVar commentStamp!
  280. I am an temporary variable of a method or block.!
  281. !TempVar methodsFor: 'testing'!
  282. isTempVar
  283. ^ true
  284. ! !
  285. ScopeVar subclass: #UnknownVar
  286. instanceVariableNames: ''
  287. package: 'Compiler-Semantic'!
  288. !UnknownVar commentStamp!
  289. I am an unknown variable. Amber uses unknown variables as JavaScript globals!
  290. !UnknownVar methodsFor: 'testing'!
  291. isUnknownVar
  292. ^ true
  293. ! !
  294. NodeVisitor subclass: #SemanticAnalyzer
  295. instanceVariableNames: 'currentScope blockIndex theClass classReferences messageSends superSends'
  296. package: 'Compiler-Semantic'!
  297. !SemanticAnalyzer commentStamp!
  298. I semantically analyze the abstract syntax tree and annotate it with informations such as non local returns and variable scopes.!
  299. !SemanticAnalyzer methodsFor: 'accessing'!
  300. classReferences
  301. ^ classReferences ifNil: [ classReferences := Set new ]
  302. !
  303. messageSends
  304. ^ messageSends ifNil: [ messageSends := Dictionary new ]
  305. !
  306. superSends
  307. ^ superSends ifNil: [ superSends := Dictionary new ]
  308. !
  309. theClass
  310. ^ theClass
  311. !
  312. theClass: aClass
  313. theClass := aClass
  314. ! !
  315. !SemanticAnalyzer methodsFor: 'error handling'!
  316. errorShadowingVariable: aString
  317. ShadowingVariableError new
  318. variableName: aString;
  319. signal
  320. !
  321. errorUnknownVariable: aNode
  322. "Throw an error if the variable is undeclared in the global JS scope (i.e. window).
  323. We allow all variables listed by Smalltalk>>#globalJsVariables.
  324. This list includes: `jQuery`, `window`, `document`, `process` and `global`
  325. for nodejs and browser environments.
  326. This is only to make sure compilation works on both browser-based and nodejs environments.
  327. The ideal solution would be to use a pragma instead"
  328. | identifier |
  329. identifier := aNode value.
  330. ((Smalltalk current globalJsVariables includes: identifier) not
  331. and: [ self isVariableGloballyUndefined: identifier ])
  332. ifTrue: [
  333. UnknownVariableError new
  334. variableName: aNode value;
  335. signal ]
  336. ifFalse: [
  337. currentScope methodScope unknownVariables add: aNode value ]
  338. ! !
  339. !SemanticAnalyzer methodsFor: 'factory'!
  340. newBlockScope
  341. ^ self newScopeOfClass: LexicalScope
  342. !
  343. newMethodScope
  344. ^ self newScopeOfClass: MethodLexicalScope
  345. !
  346. newScopeOfClass: aLexicalScopeClass
  347. ^ aLexicalScopeClass new
  348. outerScope: currentScope;
  349. yourself
  350. ! !
  351. !SemanticAnalyzer methodsFor: 'private'!
  352. nextBlockIndex
  353. blockIndex ifNil: [ blockIndex := 0 ].
  354. blockIndex := blockIndex + 1.
  355. ^ blockIndex
  356. ! !
  357. !SemanticAnalyzer methodsFor: 'scope'!
  358. popScope
  359. currentScope ifNotNil: [
  360. currentScope := currentScope outerScope ]
  361. !
  362. pushScope: aScope
  363. aScope outerScope: currentScope.
  364. currentScope := aScope
  365. !
  366. validateVariableScope: aString
  367. "Validate the variable scope in by doing a recursive lookup, up to the method scope"
  368. (currentScope lookupVariable: aString) ifNotNil: [
  369. self errorShadowingVariable: aString ]
  370. ! !
  371. !SemanticAnalyzer methodsFor: 'testing'!
  372. isVariableGloballyUndefined: aString
  373. <return eval('typeof ' + aString + ' == "undefined"')>
  374. ! !
  375. !SemanticAnalyzer methodsFor: 'visiting'!
  376. visitAssignmentNode: aNode
  377. super visitAssignmentNode: aNode.
  378. aNode left beAssigned
  379. !
  380. visitBlockNode: aNode
  381. self pushScope: self newBlockScope.
  382. aNode scope: currentScope.
  383. currentScope node: aNode.
  384. currentScope blockIndex: self nextBlockIndex.
  385. aNode parameters do: [ :each |
  386. self validateVariableScope: each.
  387. currentScope addArg: each ].
  388. super visitBlockNode: aNode.
  389. self popScope
  390. !
  391. visitCascadeNode: aNode
  392. super visitCascadeNode: aNode.
  393. aNode nodes first superSend ifTrue: [
  394. aNode nodes do: [ :each | each superSend: true ] ]
  395. !
  396. visitMethodNode: aNode
  397. self pushScope: self newMethodScope.
  398. aNode scope: currentScope.
  399. currentScope node: aNode.
  400. self theClass allInstanceVariableNames do: [:each |
  401. currentScope addIVar: each ].
  402. aNode arguments do: [ :each |
  403. self validateVariableScope: each.
  404. currentScope addArg: each ].
  405. super visitMethodNode: aNode.
  406. aNode
  407. classReferences: self classReferences;
  408. messageSends: self messageSends keys;
  409. superSends: self superSends keys.
  410. self popScope
  411. !
  412. visitReturnNode: aNode
  413. aNode scope: currentScope.
  414. currentScope isMethodScope
  415. ifTrue: [ currentScope localReturn: true ]
  416. ifFalse: [ currentScope methodScope addNonLocalReturn: currentScope ].
  417. super visitReturnNode: aNode
  418. !
  419. visitSendNode: aNode
  420. aNode receiver value = 'super'
  421. ifTrue: [
  422. aNode superSend: true.
  423. aNode receiver value: 'self'.
  424. self superSends at: aNode selector ifAbsentPut: [ Set new ].
  425. (self superSends at: aNode selector) add: aNode ]
  426. ifFalse: [ (IRSendInliner inlinedSelectors includes: aNode selector) ifTrue: [
  427. aNode shouldBeInlined: true.
  428. aNode receiver shouldBeAliased: true ] ].
  429. self messageSends at: aNode selector ifAbsentPut: [ Set new ].
  430. (self messageSends at: aNode selector) add: aNode.
  431. aNode index: (self messageSends at: aNode selector) size.
  432. super visitSendNode: aNode
  433. !
  434. visitSequenceNode: aNode
  435. aNode temps do: [ :each |
  436. self validateVariableScope: each.
  437. currentScope addTemp: each ].
  438. super visitSequenceNode: aNode
  439. !
  440. visitVariableNode: aNode
  441. "Bind a ScopeVar to aNode by doing a lookup in the current scope.
  442. If no ScopeVar is found, bind a UnknowVar and throw an error."
  443. | binding |
  444. binding := currentScope lookupVariable: aNode.
  445. binding ifNil: [
  446. aNode value isCapitalized
  447. ifTrue: [ "Capital letter variables might be globals."
  448. binding := ClassRefVar new name: aNode value; yourself.
  449. self classReferences add: aNode value]
  450. ifFalse: [
  451. self errorUnknownVariable: aNode.
  452. binding := UnknownVar new name: aNode value; yourself ] ].
  453. aNode binding: binding.
  454. ! !
  455. !SemanticAnalyzer class methodsFor: 'instance creation'!
  456. on: aClass
  457. ^ self new
  458. theClass: aClass;
  459. yourself
  460. ! !