Compiler-Semantic.st 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  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: {#slotVars. #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 slotVars keys
  161. !
  162. bindingFor: aString
  163. ^ (super bindingFor: aString) ifNil: [
  164. self slotVars at: aString ifAbsent: [ nil ]]
  165. !
  166. localReturn
  167. ^ localReturn ifNil: [ false ]
  168. !
  169. localReturn: aBoolean
  170. localReturn := aBoolean
  171. !
  172. methodScope
  173. ^ self
  174. !
  175. nonLocalReturns
  176. ^ nonLocalReturns ifNil: [ nonLocalReturns := OrderedCollection new ]
  177. !
  178. pseudoVars
  179. pseudoVars ifNil: [
  180. pseudoVars := Dictionary new.
  181. PseudoVar dictionary keysAndValuesDo: [ :each :impl |
  182. pseudoVars at: each put: ((impl on: each)
  183. scope: self methodScope;
  184. yourself) ] ].
  185. ^ pseudoVars
  186. !
  187. slotVars
  188. ^ slotVars ifNil: [ slotVars := Dictionary new ]
  189. ! !
  190. !MethodLexicalScope methodsFor: 'adding'!
  191. addNonLocalReturn: aScope
  192. self nonLocalReturns add: aScope
  193. !
  194. addSlotVar: aString
  195. self slotVars at: aString put: (SlotVar on: aString).
  196. (self slotVars at: aString) scope: self
  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: #PseudoVar
  292. slots: {}
  293. package: 'Compiler-Semantic'!
  294. !PseudoVar commentStamp!
  295. I am an pseudo variable.
  296. The five Smalltalk pseudo variables are: 'self', 'super', 'nil', 'true' and 'false'!
  297. !PseudoVar methodsFor: 'accessing'!
  298. alias
  299. ^ self name
  300. ! !
  301. !PseudoVar methodsFor: 'testing'!
  302. isIdempotent
  303. ^ true
  304. ! !
  305. PseudoVar class slots: {#dictionary. #receiverNames}!
  306. !PseudoVar class methodsFor: 'accessing'!
  307. dictionary
  308. ^ dictionary ifNil: [ dictionary := Dictionary new
  309. at: #self put: PseudoVar;
  310. at: #super put: SuperVar;
  311. at: #nil put: PseudoVar;
  312. at: #false put: PseudoVar;
  313. at: #true put: PseudoVar;
  314. at: #thisContext put: ThisContextVar;
  315. yourself ]
  316. !
  317. receiverNames
  318. ^ receiverNames ifNil: [ receiverNames := Dictionary new
  319. at: #self put: '$self';
  320. at: #super put: '$self';
  321. at: #nil put: '$nil';
  322. yourself ]
  323. ! !
  324. PseudoVar subclass: #SuperVar
  325. slots: {}
  326. package: 'Compiler-Semantic'!
  327. !SuperVar commentStamp!
  328. I am a 'super' pseudo variable.!
  329. !SuperVar methodsFor: 'accessing'!
  330. lookupAsJavaScriptSource
  331. ^ '($methodClass.superclass||$boot.nilAsClass).fn.prototype'
  332. ! !
  333. !SuperVar methodsFor: 'testing'!
  334. isSuper
  335. ^ true
  336. ! !
  337. SuperVar subclass: #JavaScriptSuperVar
  338. slots: {}
  339. package: 'Compiler-Semantic'!
  340. !JavaScriptSuperVar methodsFor: 'accessing'!
  341. lookupAsJavaScriptSource
  342. ^ 'Object.getPrototypeOf($methodClass.fn.prototype)'
  343. ! !
  344. PseudoVar subclass: #ThisContextVar
  345. slots: {}
  346. package: 'Compiler-Semantic'!
  347. !ThisContextVar commentStamp!
  348. I am a 'thisContext' pseudo variable.!
  349. !ThisContextVar methodsFor: 'accessing'!
  350. alias
  351. ^ '$core.getThisContext()'
  352. ! !
  353. ScopeVar subclass: #SlotVar
  354. slots: {}
  355. package: 'Compiler-Semantic'!
  356. !SlotVar commentStamp!
  357. I am a slot variable of a method's class.!
  358. !SlotVar methodsFor: 'testing'!
  359. alias
  360. ^ '$self.', self name
  361. !
  362. isAssignable
  363. ^ true
  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. aPackage ifNotNil: [
  464. | packageKnownVars |
  465. packageKnownVars := (aPackage imports reject: #isString) collect: #key.
  466. (packageKnownVars includes: aString) ifTrue: [ ^ true ] ].
  467. ^ Compiler new
  468. eval: 'typeof(', aString, ')!!== "undefined"||(function(){try{return(', aString, ',true)}catch(_){return false}})()'
  469. forPackage: aPackage
  470. ! !
  471. !SemanticAnalyzer methodsFor: 'visiting'!
  472. visitAssignmentNode: aNode
  473. | lhs |
  474. super visitAssignmentNode: aNode.
  475. lhs := aNode left.
  476. lhs isAssignable ifFalse: [ self errorInvalidAssignment: lhs identifier ].
  477. lhs assigned: true
  478. !
  479. visitBlockNode: aNode
  480. self pushScope: self newBlockScope.
  481. aNode scope: currentScope.
  482. currentScope node: aNode.
  483. currentScope blockIndex: self nextBlockIndex.
  484. aNode parameters do: [ :each |
  485. self validateVariableScope: each.
  486. currentScope addArg: each ].
  487. super visitBlockNode: aNode.
  488. self popScope
  489. !
  490. visitCascadeNode: aNode
  491. aNode receiver: aNode dagChildren first receiver.
  492. aNode dagChildren allButLast do: [ :each | each beSideEffect ].
  493. super visitCascadeNode: aNode
  494. !
  495. visitMethodNode: aNode
  496. self pushScope: self newMethodScope.
  497. aNode scope: currentScope.
  498. currentScope node: aNode.
  499. self theClass allSlotNames do: [ :each |
  500. currentScope addSlotVar: each ].
  501. aNode arguments do: [ :each |
  502. self validateVariableScope: each.
  503. currentScope addArg: each ].
  504. super visitMethodNode: aNode.
  505. aNode
  506. classReferences: self classReferences;
  507. sendIndexes: self messageSends.
  508. self popScope.
  509. ^ aNode
  510. !
  511. visitReturnNode: aNode
  512. aNode scope: currentScope.
  513. currentScope isMethodScope
  514. ifTrue: [ currentScope localReturn: true ]
  515. ifFalse: [ currentScope methodScope addNonLocalReturn: currentScope ].
  516. super visitReturnNode: aNode
  517. !
  518. visitSendNode: aNode
  519. | sends |
  520. sends := self messageSends at: aNode selector ifAbsentPut: [ OrderedCollection new ].
  521. sends add: aNode.
  522. aNode index: sends size.
  523. super visitSendNode: aNode
  524. !
  525. visitSequenceNode: aNode
  526. aNode temps do: [ :each |
  527. self validateVariableScope: each.
  528. currentScope addTemp: each ].
  529. super visitSequenceNode: aNode
  530. !
  531. visitVariableNode: aNode
  532. "Bind a ScopeVar to aNode by doing a lookup in the current scope.
  533. If no var is found in scope, represent an externally known variable or throw an error."
  534. aNode binding:
  535. ((currentScope lookupVariable: aNode identifier) ifNil: [ self bindUnscopedVariable: aNode identifier ])
  536. ! !
  537. !SemanticAnalyzer class methodsFor: 'instance creation'!
  538. on: aClass
  539. ^ self new
  540. theClass: aClass;
  541. yourself
  542. ! !
  543. CompilerError subclass: #SemanticError
  544. slots: {}
  545. package: 'Compiler-Semantic'!
  546. !SemanticError commentStamp!
  547. I represent an abstract semantic error thrown by the SemanticAnalyzer.
  548. Semantic errors can be unknown variable errors, etc.
  549. See my subclasses for concrete errors.
  550. The IDE should catch instances of Semantic error to deal with them when compiling!
  551. SemanticError subclass: #InvalidAssignmentError
  552. slots: {#variableName}
  553. package: 'Compiler-Semantic'!
  554. !InvalidAssignmentError commentStamp!
  555. I get signaled when a pseudo variable gets assigned.!
  556. !InvalidAssignmentError methodsFor: 'accessing'!
  557. messageText
  558. ^ ' Invalid assignment to variable: ', self variableName
  559. !
  560. variableName
  561. ^ variableName
  562. !
  563. variableName: aString
  564. variableName := aString
  565. ! !
  566. SemanticError subclass: #ShadowingVariableError
  567. slots: {#variableName}
  568. package: 'Compiler-Semantic'!
  569. !ShadowingVariableError commentStamp!
  570. I get signaled when a variable in a block or method scope shadows a variable of the same name in an outer scope.!
  571. !ShadowingVariableError methodsFor: 'accessing'!
  572. messageText
  573. ^ 'Variable shadowing error: ', self variableName, ' is already defined'
  574. !
  575. variableName
  576. ^ variableName
  577. !
  578. variableName: aString
  579. variableName := aString
  580. ! !
  581. SemanticError subclass: #UnknownVariableError
  582. slots: {#variableName}
  583. package: 'Compiler-Semantic'!
  584. !UnknownVariableError commentStamp!
  585. I get signaled when a variable is not defined.
  586. The default behavior is to allow it, as this is how Amber currently is able to seamlessly send messages to JavaScript objects.!
  587. !UnknownVariableError methodsFor: 'accessing'!
  588. messageText
  589. ^ 'Unknown Variable error: ', self variableName, ' is not defined'
  590. !
  591. variableName
  592. ^ variableName
  593. !
  594. variableName: aString
  595. variableName := aString
  596. ! !
  597. !AstSemanticPragmator methodsFor: '*Compiler-Semantic'!
  598. jsOverride: aString
  599. (JSSuperSendVisitor new property: aString; yourself)
  600. visit: self methodNode
  601. !
  602. jsOverride: aString args: aCollection
  603. (JSSuperSendVisitor new property: aString; args: aCollection; yourself)
  604. visit: self methodNode
  605. ! !