Compiler-Semantic.st 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  1. Smalltalk createPackage: 'Compiler-Semantic'!
  2. NodeVisitor subclass: #JSSuperSendVisitor
  3. slots: {#selector. #arguments. #property. #args}
  4. package: 'Compiler-Semantic'!
  5. !JSSuperSendVisitor methodsFor: 'accessing'!
  6. args
  7. ^ args
  8. !
  9. args: aCollection
  10. args := aCollection
  11. !
  12. arguments
  13. ^ arguments
  14. !
  15. arguments: aCollection
  16. arguments := aCollection
  17. !
  18. property
  19. ^ property
  20. !
  21. property: anObject
  22. property := anObject
  23. !
  24. selector
  25. ^ selector
  26. !
  27. selector: anObject
  28. selector := anObject
  29. !
  30. switcherFrom: aCollection to: anotherCollection
  31. ^ NativeFunction
  32. constructorNamed: #Function
  33. value: (',' join: aCollection)
  34. value: 'return [', (',' join: anotherCollection), ']'
  35. !
  36. visitMethodNode: aNode
  37. self selector: aNode selector.
  38. self arguments: aNode arguments.
  39. ^ super visitMethodNode: aNode
  40. !
  41. visitSendNode: aNode
  42. | receiver |
  43. receiver := aNode receiver.
  44. receiver isSuper ifTrue: [
  45. aNode selector = self selector ifTrue: [
  46. | old |
  47. old := receiver binding.
  48. receiver binding: (
  49. JavaScriptSuperVar new
  50. scope: old scope;
  51. name: old name;
  52. yourself ).
  53. self args ifNotNil: [ :myArgs |
  54. myArgs = self arguments ifFalse: [
  55. aNode argumentSwitcher:
  56. (self switcherFrom: self arguments to: myArgs) ] ].
  57. aNode javaScriptSelector: self property ] ].
  58. ^ super visitSendNode: aNode
  59. ! !
  60. Object subclass: #LexicalScope
  61. slots: {#node. #instruction. #temps. #args. #outerScope. #blockIndex}
  62. package: 'Compiler-Semantic'!
  63. !LexicalScope commentStamp!
  64. I represent a lexical scope where variable names are associated with ScopeVars
  65. Instances are used for block scopes. Method scopes are instances of MethodLexicalScope.
  66. I am attached to a ScopeVar and method/block nodes.
  67. Each context (method/closure) get a fresh scope that inherits from its outer scope.!
  68. !LexicalScope methodsFor: 'accessing'!
  69. alias
  70. ^ '$ctx', self scopeLevel asString
  71. !
  72. allVariableNames
  73. ^ self args keys, self temps keys
  74. !
  75. args
  76. ^ args ifNil: [ args := Dictionary new ]
  77. !
  78. bindingFor: aString
  79. ^ self pseudoVars at: aString ifAbsent: [
  80. self args at: aString ifAbsent: [
  81. self temps at: aString ifAbsent: [ nil ]]]
  82. !
  83. blockIndex
  84. ^ blockIndex ifNil: [ 0 ]
  85. !
  86. blockIndex: anInteger
  87. blockIndex := anInteger
  88. !
  89. instruction
  90. ^ instruction
  91. !
  92. instruction: anIRInstruction
  93. instruction := anIRInstruction
  94. !
  95. lookupVariable: aString
  96. | lookup |
  97. lookup := (self bindingFor: aString).
  98. lookup ifNil: [
  99. lookup := self outerScope ifNotNil: [
  100. (self outerScope lookupVariable: aString) ]].
  101. ^ lookup
  102. !
  103. methodScope
  104. ^ self outerScope ifNotNil: [
  105. self outerScope methodScope ]
  106. !
  107. node
  108. "Answer the node in which I am defined"
  109. ^ node
  110. !
  111. node: aNode
  112. node := aNode
  113. !
  114. outerScope
  115. ^ outerScope
  116. !
  117. outerScope: aLexicalScope
  118. outerScope := aLexicalScope
  119. !
  120. pseudoVars
  121. ^ self methodScope pseudoVars
  122. !
  123. scopeLevel
  124. self outerScope ifNil: [ ^ 1 ].
  125. self isInlined ifTrue: [ ^ self outerScope scopeLevel ].
  126. ^ self outerScope scopeLevel + 1
  127. !
  128. temps
  129. ^ temps ifNil: [ temps := Dictionary new ]
  130. ! !
  131. !LexicalScope methodsFor: 'adding'!
  132. addArg: aString
  133. self args at: aString put: (ArgVar on: aString).
  134. (self args at: aString) scope: self
  135. !
  136. addTemp: aString
  137. self temps at: aString put: (TempVar on: aString).
  138. (self temps at: aString) scope: self
  139. ! !
  140. !LexicalScope methodsFor: 'testing'!
  141. canFlattenNonLocalReturns
  142. ^ self isInlined and: [ self outerScope canFlattenNonLocalReturns ]
  143. !
  144. isBlockScope
  145. ^ self isMethodScope not
  146. !
  147. isInlined
  148. ^ self instruction ifNil: [ false ] ifNotNil: [ :instr | instr isInlined ]
  149. !
  150. isMethodScope
  151. ^ false
  152. ! !
  153. LexicalScope subclass: #MethodLexicalScope
  154. slots: {#iVars. #pseudoVars. #localReturn. #nonLocalReturns}
  155. package: 'Compiler-Semantic'!
  156. !MethodLexicalScope commentStamp!
  157. I represent a method scope.!
  158. !MethodLexicalScope methodsFor: 'accessing'!
  159. allVariableNames
  160. ^ super allVariableNames, self iVars keys
  161. !
  162. bindingFor: aString
  163. ^ (super bindingFor: aString) ifNil: [
  164. self iVars at: aString ifAbsent: [ nil ]]
  165. !
  166. iVars
  167. ^ iVars ifNil: [ iVars := Dictionary new ]
  168. !
  169. localReturn
  170. ^ localReturn ifNil: [ false ]
  171. !
  172. localReturn: aBoolean
  173. localReturn := aBoolean
  174. !
  175. methodScope
  176. ^ self
  177. !
  178. nonLocalReturns
  179. ^ nonLocalReturns ifNil: [ nonLocalReturns := OrderedCollection new ]
  180. !
  181. pseudoVars
  182. pseudoVars ifNil: [
  183. pseudoVars := Dictionary new.
  184. PseudoVar dictionary keysAndValuesDo: [ :each :impl |
  185. pseudoVars at: each put: ((impl on: each)
  186. scope: self methodScope;
  187. yourself) ] ].
  188. ^ pseudoVars
  189. ! !
  190. !MethodLexicalScope methodsFor: 'adding'!
  191. addIVar: aString
  192. self iVars at: aString put: (InstanceVar on: aString).
  193. (self iVars at: aString) scope: self
  194. !
  195. addNonLocalReturn: aScope
  196. self nonLocalReturns add: aScope
  197. !
  198. removeNonLocalReturn: aScope
  199. self nonLocalReturns remove: aScope ifAbsent: []
  200. ! !
  201. !MethodLexicalScope methodsFor: 'testing'!
  202. canFlattenNonLocalReturns
  203. ^ true
  204. !
  205. hasLocalReturn
  206. ^ self localReturn
  207. !
  208. hasNonLocalReturn
  209. ^ self nonLocalReturns notEmpty
  210. !
  211. isMethodScope
  212. ^ true
  213. ! !
  214. Object subclass: #ScopeVar
  215. slots: {#scope. #name}
  216. package: 'Compiler-Semantic'!
  217. !ScopeVar commentStamp!
  218. I am an entry in a LexicalScope that gets associated with variable nodes of the same name.
  219. There are 4 different subclasses of vars: temp vars, local vars, args, and unknown/global vars.!
  220. !ScopeVar methodsFor: 'accessing'!
  221. alias
  222. ^ self name asVariableName
  223. !
  224. name
  225. ^ name
  226. !
  227. name: aString
  228. name := aString
  229. !
  230. scope
  231. ^ scope
  232. !
  233. scope: aScope
  234. scope := aScope
  235. ! !
  236. !ScopeVar methodsFor: 'testing'!
  237. isAssignable
  238. ^ false
  239. !
  240. isIdempotent
  241. ^ false
  242. !
  243. isImmutable
  244. self deprecatedAPI: 'Use #isIdempotent / #isAssignable not instead.'.
  245. ^ self isIdempotent
  246. !
  247. isSuper
  248. ^ false
  249. ! !
  250. !ScopeVar class methodsFor: 'instance creation'!
  251. on: aString
  252. ^ self new
  253. name: aString;
  254. yourself
  255. ! !
  256. ScopeVar subclass: #AliasVar
  257. slots: {}
  258. package: 'Compiler-Semantic'!
  259. !AliasVar commentStamp!
  260. I am an internally defined variable by the compiler!
  261. !AliasVar methodsFor: 'testing'!
  262. isAssignable
  263. self error: 'Alias variable is internal, it should never appear in normal variable context.'
  264. !
  265. isIdempotent
  266. ^ true
  267. ! !
  268. ScopeVar subclass: #ArgVar
  269. slots: {}
  270. package: 'Compiler-Semantic'!
  271. !ArgVar commentStamp!
  272. I am an argument of a method or block.!
  273. !ArgVar methodsFor: 'testing'!
  274. isIdempotent
  275. ^ true
  276. ! !
  277. ScopeVar subclass: #ClassRefVar
  278. slots: {}
  279. package: 'Compiler-Semantic'!
  280. !ClassRefVar commentStamp!
  281. I am an class reference variable!
  282. !ClassRefVar methodsFor: 'accessing'!
  283. alias
  284. ^ '$globals.', self name
  285. ! !
  286. ScopeVar subclass: #ExternallyKnownVar
  287. slots: {}
  288. package: 'Compiler-Semantic'!
  289. !ExternallyKnownVar commentStamp!
  290. I am a variable known externally (not in method scope).!
  291. ScopeVar subclass: #InstanceVar
  292. slots: {}
  293. package: 'Compiler-Semantic'!
  294. !InstanceVar commentStamp!
  295. I am an instance variable of a method or block.!
  296. !InstanceVar methodsFor: 'testing'!
  297. alias
  298. ^ '$self.', self name
  299. !
  300. isAssignable
  301. ^ true
  302. ! !
  303. ScopeVar subclass: #PseudoVar
  304. slots: {}
  305. package: 'Compiler-Semantic'!
  306. !PseudoVar commentStamp!
  307. I am an pseudo variable.
  308. The five Smalltalk pseudo variables are: 'self', 'super', 'nil', 'true' and 'false'!
  309. !PseudoVar methodsFor: 'accessing'!
  310. alias
  311. ^ self name
  312. ! !
  313. !PseudoVar methodsFor: 'testing'!
  314. isIdempotent
  315. ^ true
  316. ! !
  317. PseudoVar class slots: {#dictionary. #receiverNames}!
  318. !PseudoVar class methodsFor: 'accessing'!
  319. dictionary
  320. ^ dictionary ifNil: [ dictionary := Dictionary new
  321. at: #self put: PseudoVar;
  322. at: #super put: SuperVar;
  323. at: #nil put: PseudoVar;
  324. at: #false put: PseudoVar;
  325. at: #true put: PseudoVar;
  326. at: #thisContext put: ThisContextVar;
  327. yourself ]
  328. !
  329. receiverNames
  330. ^ receiverNames ifNil: [ receiverNames := Dictionary new
  331. at: #self put: '$self';
  332. at: #super put: '$self';
  333. at: #nil put: '$nil';
  334. yourself ]
  335. ! !
  336. PseudoVar subclass: #SuperVar
  337. slots: {}
  338. package: 'Compiler-Semantic'!
  339. !SuperVar commentStamp!
  340. I am a 'super' pseudo variable.!
  341. !SuperVar methodsFor: 'accessing'!
  342. lookupAsJavaScriptSource
  343. ^ '($methodClass.superclass||$boot.nilAsClass).fn.prototype'
  344. ! !
  345. !SuperVar methodsFor: 'testing'!
  346. isSuper
  347. ^ true
  348. ! !
  349. SuperVar subclass: #JavaScriptSuperVar
  350. slots: {}
  351. package: 'Compiler-Semantic'!
  352. !JavaScriptSuperVar methodsFor: 'accessing'!
  353. lookupAsJavaScriptSource
  354. ^ 'Object.getPrototypeOf($methodClass.fn.prototype)'
  355. ! !
  356. PseudoVar subclass: #ThisContextVar
  357. slots: {}
  358. package: 'Compiler-Semantic'!
  359. !ThisContextVar commentStamp!
  360. I am a 'thisContext' pseudo variable.!
  361. !ThisContextVar methodsFor: 'accessing'!
  362. alias
  363. ^ '$core.getThisContext()'
  364. ! !
  365. ScopeVar subclass: #TempVar
  366. slots: {}
  367. package: 'Compiler-Semantic'!
  368. !TempVar commentStamp!
  369. I am an temporary variable of a method or block.!
  370. !TempVar methodsFor: 'testing'!
  371. isAssignable
  372. ^ true
  373. ! !
  374. NodeVisitor subclass: #SemanticAnalyzer
  375. slots: {#currentScope. #blockIndex. #thePackage. #theClass. #classReferences. #messageSends}
  376. package: 'Compiler-Semantic'!
  377. !SemanticAnalyzer commentStamp!
  378. I semantically analyze the abstract syntax tree and annotate it with informations such as non local returns and variable scopes.!
  379. !SemanticAnalyzer methodsFor: 'accessing'!
  380. classReferences
  381. ^ classReferences ifNil: [ classReferences := Set new ]
  382. !
  383. messageSends
  384. ^ messageSends ifNil: [ messageSends := Dictionary new ]
  385. !
  386. theClass
  387. ^ theClass
  388. !
  389. theClass: aClass
  390. theClass := aClass
  391. !
  392. thePackage
  393. ^ thePackage
  394. !
  395. thePackage: aPackage
  396. thePackage := aPackage
  397. ! !
  398. !SemanticAnalyzer methodsFor: 'error handling'!
  399. errorInvalidAssignment: aString
  400. InvalidAssignmentError new
  401. variableName: aString;
  402. signal
  403. !
  404. errorShadowingVariable: aString
  405. ShadowingVariableError new
  406. variableName: aString;
  407. signal
  408. !
  409. errorUnknownVariable: aString
  410. UnknownVariableError new
  411. variableName: aString;
  412. signal
  413. ! !
  414. !SemanticAnalyzer methodsFor: 'factory'!
  415. newBlockScope
  416. ^ self newScopeOfClass: LexicalScope
  417. !
  418. newMethodScope
  419. ^ self newScopeOfClass: MethodLexicalScope
  420. !
  421. newScopeOfClass: aLexicalScopeClass
  422. ^ aLexicalScopeClass new
  423. outerScope: currentScope;
  424. yourself
  425. ! !
  426. !SemanticAnalyzer methodsFor: 'private'!
  427. bindUnscopedVariable: aString
  428. aString isCapitalized ifTrue: [ "Capital letter variables might be globals."
  429. self classReferences add: aString.
  430. ^ ClassRefVar new name: aString; yourself ].
  431. "Throw an error if the variable is undeclared in the global JS scope (i.e. window).
  432. We allow all variables listed by Smalltalk>>#globalJsVariables.
  433. This list includes: `window`, `document`, `process` and `global`
  434. for nodejs and browser environments.
  435. This is only to make sure compilation works on both browser-based and nodejs environments.
  436. The ideal solution would be to use a pragma instead"
  437. ((Smalltalk globalJsVariables includes: aString)
  438. or: [ self isVariableKnown: aString inPackage: self thePackage ]) ifTrue: [
  439. ^ ExternallyKnownVar new name: aString; yourself ].
  440. self errorUnknownVariable: aString
  441. !
  442. nextBlockIndex
  443. blockIndex ifNil: [ blockIndex := 0 ].
  444. blockIndex := blockIndex + 1.
  445. ^ blockIndex
  446. ! !
  447. !SemanticAnalyzer methodsFor: 'scope'!
  448. popScope
  449. currentScope ifNotNil: [
  450. currentScope := currentScope outerScope ]
  451. !
  452. pushScope: aScope
  453. aScope outerScope: currentScope.
  454. currentScope := aScope
  455. !
  456. validateVariableScope: aString
  457. "Validate the variable scope in by doing a recursive lookup, up to the method scope"
  458. (currentScope lookupVariable: aString) ifNotNil: [
  459. self errorShadowingVariable: aString ]
  460. ! !
  461. !SemanticAnalyzer methodsFor: 'testing'!
  462. isVariableKnown: aString inPackage: aPackage
  463. ^ Compiler new
  464. eval: 'typeof(', aString, ')!!== "undefined"||(function(){try{return(', aString, ',true)}catch(_){return false}})()'
  465. forPackage: aPackage
  466. ! !
  467. !SemanticAnalyzer methodsFor: 'visiting'!
  468. visitAssignmentNode: aNode
  469. | lhs |
  470. super visitAssignmentNode: aNode.
  471. lhs := aNode left.
  472. lhs isAssignable ifFalse: [ self errorInvalidAssignment: lhs identifier ].
  473. lhs assigned: true
  474. !
  475. visitBlockNode: aNode
  476. self pushScope: self newBlockScope.
  477. aNode scope: currentScope.
  478. currentScope node: aNode.
  479. currentScope blockIndex: self nextBlockIndex.
  480. aNode parameters do: [ :each |
  481. self validateVariableScope: each.
  482. currentScope addArg: each ].
  483. super visitBlockNode: aNode.
  484. self popScope
  485. !
  486. visitCascadeNode: aNode
  487. aNode receiver: aNode dagChildren first receiver.
  488. aNode dagChildren allButLast do: [ :each | each beSideEffect ].
  489. super visitCascadeNode: aNode
  490. !
  491. visitMethodNode: aNode
  492. self pushScope: self newMethodScope.
  493. aNode scope: currentScope.
  494. currentScope node: aNode.
  495. self theClass allInstanceVariableNames do: [ :each |
  496. currentScope addIVar: each ].
  497. aNode arguments do: [ :each |
  498. self validateVariableScope: each.
  499. currentScope addArg: each ].
  500. super visitMethodNode: aNode.
  501. aNode
  502. classReferences: self classReferences;
  503. sendIndexes: self messageSends.
  504. self popScope.
  505. ^ aNode
  506. !
  507. visitReturnNode: aNode
  508. aNode scope: currentScope.
  509. currentScope isMethodScope
  510. ifTrue: [ currentScope localReturn: true ]
  511. ifFalse: [ currentScope methodScope addNonLocalReturn: currentScope ].
  512. super visitReturnNode: aNode
  513. !
  514. visitSendNode: aNode
  515. | sends |
  516. sends := self messageSends at: aNode selector ifAbsentPut: [ OrderedCollection new ].
  517. sends add: aNode.
  518. aNode index: sends size.
  519. super visitSendNode: aNode
  520. !
  521. visitSequenceNode: aNode
  522. aNode temps do: [ :each |
  523. self validateVariableScope: each.
  524. currentScope addTemp: each ].
  525. super visitSequenceNode: aNode
  526. !
  527. visitVariableNode: aNode
  528. "Bind a ScopeVar to aNode by doing a lookup in the current scope.
  529. If no var is found in scope, represent an externally known variable or throw an error."
  530. aNode binding:
  531. ((currentScope lookupVariable: aNode identifier) ifNil: [ self bindUnscopedVariable: aNode identifier ])
  532. ! !
  533. !SemanticAnalyzer class methodsFor: 'instance creation'!
  534. on: aClass
  535. ^ self new
  536. theClass: aClass;
  537. yourself
  538. ! !
  539. CompilerError subclass: #SemanticError
  540. slots: {}
  541. package: 'Compiler-Semantic'!
  542. !SemanticError commentStamp!
  543. I represent an abstract semantic error thrown by the SemanticAnalyzer.
  544. Semantic errors can be unknown variable errors, etc.
  545. See my subclasses for concrete errors.
  546. The IDE should catch instances of Semantic error to deal with them when compiling!
  547. SemanticError subclass: #InvalidAssignmentError
  548. slots: {#variableName}
  549. package: 'Compiler-Semantic'!
  550. !InvalidAssignmentError commentStamp!
  551. I get signaled when a pseudo variable gets assigned.!
  552. !InvalidAssignmentError methodsFor: 'accessing'!
  553. messageText
  554. ^ ' Invalid assignment to variable: ', self variableName
  555. !
  556. variableName
  557. ^ variableName
  558. !
  559. variableName: aString
  560. variableName := aString
  561. ! !
  562. SemanticError subclass: #ShadowingVariableError
  563. slots: {#variableName}
  564. package: 'Compiler-Semantic'!
  565. !ShadowingVariableError commentStamp!
  566. I get signaled when a variable in a block or method scope shadows a variable of the same name in an outer scope.!
  567. !ShadowingVariableError methodsFor: 'accessing'!
  568. messageText
  569. ^ 'Variable shadowing error: ', self variableName, ' is already defined'
  570. !
  571. variableName
  572. ^ variableName
  573. !
  574. variableName: aString
  575. variableName := aString
  576. ! !
  577. SemanticError subclass: #UnknownVariableError
  578. slots: {#variableName}
  579. package: 'Compiler-Semantic'!
  580. !UnknownVariableError commentStamp!
  581. I get signaled when a variable is not defined.
  582. The default behavior is to allow it, as this is how Amber currently is able to seamlessly send messages to JavaScript objects.!
  583. !UnknownVariableError methodsFor: 'accessing'!
  584. messageText
  585. ^ 'Unknown Variable error: ', self variableName, ' is not defined'
  586. !
  587. variableName
  588. ^ variableName
  589. !
  590. variableName: aString
  591. variableName := aString
  592. ! !
  593. !AstSemanticPragmator methodsFor: '*Compiler-Semantic'!
  594. jsOverride: aString
  595. (JSSuperSendVisitor new property: aString; yourself)
  596. visit: self methodNode
  597. !
  598. jsOverride: aString args: aCollection
  599. (JSSuperSendVisitor new property: aString; args: aCollection; yourself)
  600. visit: self methodNode
  601. ! !