| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 | Smalltalk current createPackage: 'Compiler-Interpreter'!NodeVisitor subclass: #AIContext	instanceVariableNames: 'outerContext pc locals method'	package: 'Compiler-Interpreter'!!AIContext commentStamp!AIContext is like a `MethodContext`, used by the `ASTInterpreter`.Unlike a `MethodContext`, it is not read-only.When debugging, `AIContext` instances are created by copying the current `MethodContext` (thisContext)!!AIContext methodsFor: 'accessing'!localAt: aString	^ self locals at: aString ifAbsent: [ nil ]!localAt: aString put: anObject	self locals at: aString put: anObject!locals	^ locals ifNil: [ locals := Dictionary new ]!method	^ method!method: aCompiledMethod	method := aCompiledMethod!outerContext	^ outerContext!outerContext: anAIContext	outerContext := anAIContext!pc	^ pc ifNil: [ pc := 0 ]!pc: anInteger	pc := anInteger!receiver	^ self localAt: 'self'!receiver: anObject	self localAt: 'self' put: anObject!selector	^ self metod    	ifNotNil: [ self method selector ]! !!AIContext methodsFor: 'initialization'!initializeFromMethodContext: aMethodContext	self pc: aMethodContext pc.    self receiver: aMethodContext receiver.    self method: aMethodContext method.    aMethodContext outerContext ifNotNil: [		self outerContext: (self class fromMethodContext: aMethodContext outerContext) ].    aMethodContext locals keysAndValuesDo: [ :key :value |    	self locals at: key put: value ]! !!AIContext class methodsFor: 'instance creation'!fromMethodContext: aMethodContext	^ self new    	initializeFromMethodContext: aMethodContext;        yourself! !Object subclass: #ASTDebugger	instanceVariableNames: 'interpreter context'	package: 'Compiler-Interpreter'!!ASTDebugger commentStamp!ASTDebugger is a debugger to Amber.It uses an AST interpreter to step through the code.ASTDebugger instances are created from a `MethodContext` with `ASTDebugger class >> context:`.They hold an `AIContext` instance internally, recursive copy of the `MethodContext`.Use the methods of the 'stepping' protocol to do stepping.!!ASTDebugger methodsFor: 'accessing'!context	^ context!context: aContext	context := AIContext new.!interpreter	^ interpreter ifNil: [ interpreter := self defaultInterpreterClass new ]!interpreter: anInterpreter	interpreter := anInterpreter!method	^ self context method! !!ASTDebugger methodsFor: 'defaults'!defaultInterpreterClass	^ ASTSteppingInterpreter! !!ASTDebugger methodsFor: 'initialization'!buildAST	"Build the AST tree from the method source code.    The AST is annotated with a SemanticAnalyzer,     to know the semantics and bindings of each node needed for later debugging"        | ast |        ast := Smalltalk current parse: self method source.    (SemanticAnalyzer on: self context receiver class)    	visit: ast.            ^ ast!initializeInterpreter	self interpreter interpret: self buildAST nodes first!initializeWithContext: aMethodContext	"TODO: do we need to handle block contexts?"        self context: (AIContext fromMethodContext: aMethodContext).    self initializeInterpreter! !!ASTDebugger methodsFor: 'stepping'!restart	self shouldBeImplemented!resume	self shouldBeImplemented!step	"The ASTSteppingInterpreter stops at each node interpretation.     One step will interpret nodes until:    - we get at the end    - the next node is a stepping node (send, assignment, etc.)"    	[ (self interpreter nextNode notNil and: [ self interpreter nextNode stopOnStepping ])		or: [ self interpreter atEnd not ] ]  			whileFalse: [				self interpreter step.                 self step ]!stepInto	self shouldBeImplemented!stepOver	self step! !!ASTDebugger class methodsFor: 'instance creation'!context: aMethodContext	^ self new    	initializeWithContext: aMethodContext;        yourself! !Object subclass: #ASTInterpreter	instanceVariableNames: 'currentNode context shouldReturn result'	package: 'Compiler-Interpreter'!!ASTInterpreter commentStamp!ASTIntepreter is like a `NodeVisitor`, interpreting nodes one after each other.It is built using Continuation Passing Style for stepping purposes.Usage example:    | ast interpreter |    ast := Smalltalk current parse: 'foo 1+2+4'.    (SemanticAnalyzer on: Object) visit: ast.    ASTInterpreter new        interpret: ast nodes first;        result "Answers 7"!!ASTInterpreter methodsFor: 'accessing'!context	^ context ifNil: [ context := AIContext new ]!context: anAIContext	context := anAIContext!currentNode	^ currentNode!result	^ result! !!ASTInterpreter methodsFor: 'initialization'!initialize	super initialize.    shouldReturn := false! !!ASTInterpreter methodsFor: 'interpreting'!interpret: aNode	shouldReturn := false.    self interpret: aNode continue: [ :value |    	result := value ]!interpret: aNode continue: aBlock	shouldReturn ifTrue: [ ^ self ].	aNode isNode     	ifTrue: [ 	        	currentNode := aNode.            self interpretNode: aNode continue: [ :value |  				self continue: aBlock value: value ] ]        ifFalse: [ self continue: aBlock value: aNode ]!interpretAssignmentNode: aNode continue: aBlock	self interpret: aNode right continue: [ :value |    	self         	continue: aBlock            value: (self assign: aNode left to: value) ]!interpretBlockNode: aNode continue: aBlock	"TODO: Context should be set"        self     	continue: aBlock         value: [ self interpret: aNode nodes first; result ]!interpretBlockSequenceNode: aNode continue: aBlock	self interpretSequenceNode: aNode continue: aBlock!interpretCascadeNode: aNode continue: aBlock	"TODO: Handle super sends"	    self interpret: aNode receiver continue: [ :receiver |		"Only interpret the receiver once"        aNode nodes do: [ :each | each receiver: receiver ].      	self         	interpretAll: aNode nodes allButLast    		continue: [              	self                 	interpret: aNode nodes last                	continue: [ :val | self continue: aBlock value: val ] ] ]!interpretClassReferenceNode: aNode continue: aBlock	self continue: aBlock value: (Smalltalk current at: aNode value)!interpretDynamicArrayNode: aNode continue: aBlock	self interpretAll: aNode nodes continue: [ :array |    	self         	continue: aBlock			value: array ]!interpretDynamicDictionaryNode: aNode continue: aBlock    self interpretAll: aNode nodes continue: [ :array | | hashedCollection |    	hashedCollection := HashedCollection new.        array do: [ :each | hashedCollection add: each ].        self 	        	continue: aBlock            value: hashedCollection ]!interpretJSStatementNode: aNode continue: aBlock	shouldReturn := true.	self continue: aBlock value: (self eval: aNode source)!interpretMethodNode: aNode continue: aBlock	self interpretAll: aNode nodes continue: [ :array |    	self continue: aBlock value: array first ]!interpretNode: aNode continue: aBlock    aNode interpreter: self continue: aBlock!interpretReturnNode: aNode continue: aBlock    self interpret: aNode nodes first continue: [ :value |    	shouldReturn := true.		self continue: aBlock value: value ]!interpretSendNode: aNode continue: aBlock	"TODO: Handle super sends"        self interpret: aNode receiver continue: [ :receiver |    	self interpretAll: aNode arguments continue: [ :args |    		self             	messageFromSendNode: aNode                 arguments: args                do: [ :message |        			self context pc: self context pc + 1.        			self             			continue: aBlock                 		value: (message sendTo: receiver) ] ] ]!interpretSequenceNode: aNode continue: aBlock	self interpretAll: aNode nodes continue: [ :array |    	self continue: aBlock value: array last ]!interpretValueNode: aNode continue: aBlock	self continue: aBlock value: aNode value!interpretVariableNode: aNode continue: aBlock    self     	continue: aBlock        value: (aNode binding isInstanceVar			ifTrue: [ self context receiver instVarAt: aNode value ]			ifFalse: [ self context localAt: aNode value ])! !!ASTInterpreter methodsFor: 'private'!assign: aNode to: anObject	^ aNode binding isInstanceVar     	ifTrue: [ self context receiver instVarAt: aNode value put: anObject ]      	ifFalse: [ self context localAt: aNode value put: anObject ]!continue: aBlock value: anObject	result := anObject.    aBlock value: anObject!eval: aString	"Evaluate aString as JS source inside an JS function.     aString is not sandboxed."        | source function |        source := String streamContents: [ :str |    	str nextPutAll: '(function('.        self context locals keys         	do: [ :each | str nextPutAll: each ]          	separatedBy: [ str nextPutAll: ',' ].        str         	nextPutAll: '){ return (function() {';        	nextPutAll: aString;            nextPutAll: '})() })' ].            	function := Compiler new eval: source.    	^ function valueWithPossibleArguments: self context locals values!interpretAll: aCollection continue: aBlock	self     	interpretAll: aCollection         continue: aBlock         result: OrderedCollection new!interpretAll: nodes continue: aBlock result: aCollection	nodes isEmpty     	ifTrue: [ self continue: aBlock value: aCollection ]    	ifFalse: [    		self interpret: nodes first continue: [:value |    			self                 	interpretAll: nodes allButFirst                     continue: aBlock  					result: aCollection, { value } ] ]!messageFromSendNode: aSendNode arguments: aCollection do: aBlock    self         continue: aBlock        value: (Message new    		selector: aSendNode selector;        	arguments: aCollection;        	yourself)! !!ASTInterpreter methodsFor: 'testing'!shouldReturn	^ shouldReturn ifNil: [ false ]! !ASTInterpreter subclass: #ASTSteppingInterpreter	instanceVariableNames: 'continuation nextNode'	package: 'Compiler-Interpreter'!!ASTSteppingInterpreter commentStamp!ASTSteppingInterpreter is an interpreter with stepping capabilities.Use `#step` to actually interpret the next node.Usage example:    | ast interpreter |    ast := Smalltalk current parse: 'foo 1+2+4'.    (SemanticAnalyzer on: Object) visit: ast.    interpreter := ASTSteppingInterpreter new        interpret: ast nodes first;        yourself.            debugger step; step.    debugger step; step.    debugger result."Answers 1"    debugger step.    debugger result. "Answers 3"    debugger step.    debugger result. "Answers 7"!!ASTSteppingInterpreter methodsFor: 'accessing'!nextNode	^ nextNode! !!ASTSteppingInterpreter methodsFor: 'initialization'!initialize	super initialize.    continuation := [  ]! !!ASTSteppingInterpreter methodsFor: 'interpreting'!interpret: aNode continue: aBlock	nextNode := aNode.	continuation := [     	super interpret: aNode continue: aBlock ]! !!ASTSteppingInterpreter methodsFor: 'stepping'!step	continuation value! !!ASTSteppingInterpreter methodsFor: 'testing'!atEnd	^ self shouldReturn or: [ self nextNode == self currentNode ]! !!Node methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretNode: self continue: aBlock!isSteppingNode	^ false! !!AssignmentNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretAssignmentNode: self continue: aBlock!isSteppingNode	^ true! !!BlockNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretBlockNode: self continue: aBlock!isSteppingNode	^ true! !!CascadeNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretCascadeNode: self continue: aBlock! !!DynamicArrayNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretDynamicArrayNode: self continue: aBlock!isSteppingNode	^ true! !!DynamicDictionaryNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretDynamicDictionaryNode: self continue: aBlock!isSteppingNode	^ true! !!JSStatementNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretJSStatementNode: self continue: aBlock!isSteppingNode	^ true! !!MethodNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretMethodNode: self continue: aBlock! !!ReturnNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretReturnNode: self continue: aBlock! !!SendNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretSendNode: self continue: aBlock!isSteppingNode	^ true! !!SequenceNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretSequenceNode: self continue: aBlock! !!BlockSequenceNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretBlockSequenceNode: self continue: aBlock! !!ValueNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretValueNode: self continue: aBlock! !!VariableNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretVariableNode: self continue: aBlock! !!ClassReferenceNode methodsFor: '*Compiler-Interpreter'!interpreter: anInterpreter continue: aBlock	^ anInterpreter interpretClassReferenceNode: self continue: aBlock! !
 |