Compiler-Semantic.st 15 KB

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