Browse Source

- Interpreter can now (naively) evaluate inlined JS statements
- Fixed boot.js popContext()

Nicolas Petton 11 years ago
parent
commit
759c07143f

+ 50 - 11
js/Compiler-Interpreter.deploy.js

@@ -23,6 +23,17 @@ return self}, self, "initializeFromMethodContext:", [aMethodContext], smalltalk.
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+"_localAt_put_",
+smalltalk.method({
+selector: "localAt:put:",
+fn: function (aString,anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(_st(self)._locals())._at_put_(aString,anObject);
+return self}, self, "localAt:put:", [aString,anObject], smalltalk.AIContext)}
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 "_locals",
 smalltalk.method({
@@ -169,7 +180,15 @@ smalltalk.method({
 selector: "context",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@context"];
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=self["@context"];
+if(($receiver = $2) == nil || $receiver == undefined){
+self["@context"]=_st((smalltalk.AIContext || AIContext))._new();
+$1=self["@context"];
+} else {
+$1=$2;
+};
+return $1;
 }, self, "context", [], smalltalk.ASTInterpreter)}
 }),
 smalltalk.ASTInterpreter);
@@ -185,6 +204,19 @@ return self}, self, "context:", [anAIContext], smalltalk.ASTInterpreter)}
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_eval_",
+smalltalk.method({
+selector: "eval:",
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st((smalltalk.Compiler || Compiler))._new())._eval_(_st(_st("(function() { ").__comma(aString)).__comma(" })()"));
+return $1;
+}, self, "eval:", [aString], smalltalk.ASTInterpreter)}
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
@@ -296,8 +328,11 @@ smalltalk.method({
 selector: "visitJSStatementNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(self)._halt();
-return self}, self, "visitJSStatementNode:", [aNode], smalltalk.ASTInterpreter)}
+return smalltalk.withContext(function($ctx1) { 
var $1;
+self["@shouldReturn"]=true;
+$1=_st(self)._eval_(_st(aNode)._source());
+return $1;
+}, self, "visitJSStatementNode:", [aNode], smalltalk.ASTInterpreter)}
 }),
 smalltalk.ASTInterpreter);
 
@@ -334,19 +369,23 @@ smalltalk.method({
 selector: "visitSequenceNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
+return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2,$5;
 var $early={};
 try {
-_st(_st(_st(aNode)._nodes())._allButLast())._do_((function(each){
-return smalltalk.withContext(function($ctx2) { 
$ctx2.value=nil;
+$1=_st(_st(aNode)._nodes())._allButLast();
+$2=(function(each){
+return smalltalk.withContext(function($ctx2) { 
$ctx2.locals.value=nil;
 $ctx2.locals.value=_st(self)._interpretNode_(each);
 $ctx2.locals.value;
-if(smalltalk.assert(self["@shouldReturn"])){
-throw $early=[$ctx2.locals.value];
+$3=self["@shouldReturn"];
+if(smalltalk.assert($3)){
+$4=$ctx2.locals.value;
+throw $early=[$4];
 };
-})}));
-$1=_st(self)._interpretNode_(_st(_st(aNode)._nodes())._last());
-return $1;
+})});
+_st($1)._do_($2);
+$5=_st(self)._interpretNode_(_st(_st(aNode)._nodes())._last());
+return $5;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
 }, self, "visitSequenceNode:", [aNode], smalltalk.ASTInterpreter)}

+ 66 - 17
js/Compiler-Interpreter.js

@@ -28,6 +28,22 @@ referencedClasses: []
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+"_localAt_put_",
+smalltalk.method({
+selector: "localAt:put:",
+category: 'accessing',
+fn: function (aString,anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(_st(self)._locals())._at_put_(aString,anObject);
+return self}, self, "localAt:put:", [aString,anObject], smalltalk.AIContext)},
+args: ["aString", "anObject"],
+source: "localAt: aString put: anObject\x0a\x09self locals at: aString put: anObject",
+messageSends: ["at:put:", "locals"],
+referencedClasses: []
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 "_locals",
 smalltalk.method({
@@ -225,12 +241,20 @@ selector: "context",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@context"];
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=self["@context"];
+if(($receiver = $2) == nil || $receiver == undefined){
+self["@context"]=_st((smalltalk.AIContext || AIContext))._new();
+$1=self["@context"];
+} else {
+$1=$2;
+};
+return $1;
 }, self, "context", [], smalltalk.ASTInterpreter)},
 args: [],
-source: "context\x0a\x09^ context",
-messageSends: [],
-referencedClasses: []
+source: "context\x0a\x09^ context ifNil: [ context := AIContext new ]",
+messageSends: ["ifNil:", "new"],
+referencedClasses: ["AIContext"]
 }),
 smalltalk.ASTInterpreter);
 
@@ -250,6 +274,24 @@ referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_eval_",
+smalltalk.method({
+selector: "eval:",
+category: 'interpreting',
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st((smalltalk.Compiler || Compiler))._new())._eval_(_st(_st("(function() { ").__comma(aString)).__comma(" })()"));
+return $1;
+}, self, "eval:", [aString], smalltalk.ASTInterpreter)},
+args: ["aString"],
+source: "eval: aString\x0a\x09\x22Evaluate aString as JS source inside an immediately evaluated JS function. \x0a    aString is not sandboxed.\x22\x0a    \x0a    ^ Compiler new eval: '(function() { ', aString, ' })()'",
+messageSends: ["eval:", ",", "new"],
+referencedClasses: ["Compiler"]
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
@@ -397,11 +439,14 @@ selector: "visitJSStatementNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(self)._halt();
-return self}, self, "visitJSStatementNode:", [aNode], smalltalk.ASTInterpreter)},
+return smalltalk.withContext(function($ctx1) { 
var $1;
+self["@shouldReturn"]=true;
+$1=_st(self)._eval_(_st(aNode)._source());
+return $1;
+}, self, "visitJSStatementNode:", [aNode], smalltalk.ASTInterpreter)},
 args: ["aNode"],
-source: "visitJSStatementNode: aNode\x0a\x09self halt",
-messageSends: ["halt"],
+source: "visitJSStatementNode: aNode\x0a\x09shouldReturn := true.\x0a\x09^ self eval: aNode source",
+messageSends: ["eval:", "source"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -450,24 +495,28 @@ selector: "visitSequenceNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
+return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2,$5;
 var $early={};
 try {
-_st(_st(_st(aNode)._nodes())._allButLast())._do_((function(each){
-return smalltalk.withContext(function($ctx2) { 
$ctx2.value=nil;
+$1=_st(_st(aNode)._nodes())._allButLast();
+$2=(function(each){
+return smalltalk.withContext(function($ctx2) { 
$ctx2.locals.value=nil;
 $ctx2.locals.value=_st(self)._interpretNode_(each);
 $ctx2.locals.value;
-if(smalltalk.assert(self["@shouldReturn"])){
-throw $early=[$ctx2.locals.value];
+$3=self["@shouldReturn"];
+if(smalltalk.assert($3)){
+$4=$ctx2.locals.value;
+throw $early=[$4];
 };
-})}));
-$1=_st(self)._interpretNode_(_st(_st(aNode)._nodes())._last());
-return $1;
+})});
+_st($1)._do_($2);
+$5=_st(self)._interpretNode_(_st(_st(aNode)._nodes())._last());
+return $5;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
 }, self, "visitSequenceNode:", [aNode], smalltalk.ASTInterpreter)},
 args: ["aNode"],
-source: "visitSequenceNode: aNode\x0a\x09aNode nodes allButLast do: [ :each | | value |\x0a        value := self interpretNode: each.\x0a\x09\x09shouldReturn ifTrue: [ ^ value ] ].\x0a    ^ self interpretNode: aNode nodes last",
+source: "visitSequenceNode: aNode\x0a\x0a\x09aNode nodes allButLast do: [ :each | | value |\x0a        value := self interpretNode: each.\x0a\x09\x09shouldReturn ifTrue: [ ^ value ] ].\x0a        \x0a    ^ self interpretNode: aNode nodes last",
 messageSends: ["do:", "interpretNode:", "ifTrue:", "allButLast", "nodes", "last"],
 referencedClasses: []
 }),

+ 34 - 1
js/Compiler-Tests.deploy.js

@@ -19,12 +19,33 @@ selector: "interpret:",
 fn: function (aString){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(_st((smalltalk.ASTInterpreter || ASTInterpreter))._new())._interpret_(_st(_st(_st(self)._parse_forClass_(aString,(smalltalk.Object || Object)))._nodes())._first());
+$1=_st(self)._interpret_withArguments_(aString,_st((smalltalk.Dictionary || Dictionary))._new());
 return $1;
 }, self, "interpret:", [aString], smalltalk.ASTInterpreterTest)}
 }),
 smalltalk.ASTInterpreterTest);
 
+smalltalk.addMethod(
+"_interpret_withArguments_",
+smalltalk.method({
+selector: "interpret:withArguments:",
+fn: function (aString,aDictionary){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+$ctx1.locals.ctx=nil;
+$ctx1.locals.ctx=_st((smalltalk.AIContext || AIContext))._new();
+_st(aDictionary)._keysAndValuesDo_((function(key,value){
+return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.ctx)._localAt_put_(key,value);
+})}));
+$2=_st((smalltalk.ASTInterpreter || ASTInterpreter))._new();
+_st($2)._context_($ctx1.locals.ctx);
+$3=_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,(smalltalk.Object || Object)))._nodes())._first());
+$1=$3;
+return $1;
+}, self, "interpret:withArguments:", [aString,aDictionary], smalltalk.ASTInterpreterTest)}
+}),
+smalltalk.ASTInterpreterTest);
+
 smalltalk.addMethod(
 "_parse_",
 smalltalk.method({
@@ -86,6 +107,18 @@ return self}, self, "testCascade", [], smalltalk.ASTInterpreterTest)}
 }),
 smalltalk.ASTInterpreterTest);
 
+smalltalk.addMethod(
+"_testInlinedJSStatement",
+smalltalk.method({
+selector: "testInlinedJSStatement",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo <return 2+3>"),(5));
+_st(self)._assert_equals_(_st(self)._interpret_withArguments_("foo: anInteger <return 2 + anInteger>",smalltalk.HashedCollection._fromPairs_([_st("anInteger").__minus_gt((3))])),(5));
+return self}, self, "testInlinedJSStatement", [], smalltalk.ASTInterpreterTest)}
+}),
+smalltalk.ASTInterpreterTest);
+
 
 
 smalltalk.addClass('CodeGeneratorTest', smalltalk.TestCase, ['receiver'], 'Compiler-Tests');

+ 47 - 4
js/Compiler-Tests.js

@@ -25,13 +25,39 @@ category: 'accessing',
 fn: function (aString){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(_st((smalltalk.ASTInterpreter || ASTInterpreter))._new())._interpret_(_st(_st(_st(self)._parse_forClass_(aString,(smalltalk.Object || Object)))._nodes())._first());
+$1=_st(self)._interpret_withArguments_(aString,_st((smalltalk.Dictionary || Dictionary))._new());
 return $1;
 }, self, "interpret:", [aString], smalltalk.ASTInterpreterTest)},
 args: ["aString"],
-source: "interpret: aString\x0a\x09\x22the food is a methodNode. Interpret the sequenceNode only\x22\x0a    ^ ASTInterpreter new\x0a    \x09interpret: (self parse: aString forClass: Object) \x0a        \x09nodes first",
-messageSends: ["interpret:", "first", "nodes", "parse:forClass:", "new"],
-referencedClasses: ["Object", "ASTInterpreter"]
+source: "interpret: aString\x0a\x09^ self \x0a    \x09interpret: aString \x0a        withArguments: Dictionary new",
+messageSends: ["interpret:withArguments:", "new"],
+referencedClasses: ["Dictionary"]
+}),
+smalltalk.ASTInterpreterTest);
+
+smalltalk.addMethod(
+"_interpret_withArguments_",
+smalltalk.method({
+selector: "interpret:withArguments:",
+category: 'accessing',
+fn: function (aString,aDictionary){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+$ctx1.locals.ctx=nil;
+$ctx1.locals.ctx=_st((smalltalk.AIContext || AIContext))._new();
+_st(aDictionary)._keysAndValuesDo_((function(key,value){
+return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.ctx)._localAt_put_(key,value);
+})}));
+$2=_st((smalltalk.ASTInterpreter || ASTInterpreter))._new();
+_st($2)._context_($ctx1.locals.ctx);
+$3=_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,(smalltalk.Object || Object)))._nodes())._first());
+$1=$3;
+return $1;
+}, self, "interpret:withArguments:", [aString,aDictionary], smalltalk.ASTInterpreterTest)},
+args: ["aString", "aDictionary"],
+source: "interpret: aString withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a    \x0a    | ctx |\x0a    \x0a    ctx := AIContext new.\x0a    aDictionary keysAndValuesDo: [ :key :value |\x0a    \x09ctx localAt: key put: value ].\x0a    \x0a    ^ ASTInterpreter new\x0a    \x09context: ctx;\x0a    \x09interpret: (self parse: aString forClass: Object) \x0a        \x09nodes first",
+messageSends: ["new", "keysAndValuesDo:", "localAt:put:", "context:", "interpret:", "first", "nodes", "parse:forClass:"],
+referencedClasses: ["AIContext", "ASTInterpreter", "Object"]
 }),
 smalltalk.ASTInterpreterTest);
 
@@ -121,6 +147,23 @@ referencedClasses: ["OrderedCollection"]
 }),
 smalltalk.ASTInterpreterTest);
 
+smalltalk.addMethod(
+"_testInlinedJSStatement",
+smalltalk.method({
+selector: "testInlinedJSStatement",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo <return 2+3>"),(5));
+_st(self)._assert_equals_(_st(self)._interpret_withArguments_("foo: anInteger <return 2 + anInteger>",smalltalk.HashedCollection._fromPairs_([_st("anInteger").__minus_gt((3))])),(5));
+return self}, self, "testInlinedJSStatement", [], smalltalk.ASTInterpreterTest)},
+args: [],
+source: "testInlinedJSStatement\x0a\x09self assert: (self interpret: 'foo <return 2+3>') equals: 5.\x0a    self \x0a    \x09assert: (self \x0a    \x09\x09interpret: 'foo: anInteger <return 2 + anInteger>' \x0a        \x09withArguments: #{ 'anInteger' -> 3}) \x0a\x09\x09equals: 5",
+messageSends: ["assert:equals:", "interpret:", "interpret:withArguments:", "->"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreterTest);
+
 
 
 smalltalk.addClass('CodeGeneratorTest', smalltalk.TestCase, ['receiver'], 'Compiler-Tests');

+ 32 - 0
js/Kernel-Methods.deploy.js

@@ -747,6 +747,19 @@ return self}, self, "home", [], smalltalk.MethodContext)}
 }),
 smalltalk.MethodContext);
 
+smalltalk.addMethod(
+"_isBlockContext",
+smalltalk.method({
+selector: "isBlockContext",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(self)._selector())._isNil();
+return $1;
+}, self, "isBlockContext", [], smalltalk.MethodContext)}
+}),
+smalltalk.MethodContext);
+
 smalltalk.addMethod(
 "_locals",
 smalltalk.method({
@@ -759,6 +772,24 @@ return self}, self, "locals", [], smalltalk.MethodContext)}
 }),
 smalltalk.MethodContext);
 
+smalltalk.addMethod(
+"_methodContext",
+smalltalk.method({
+selector: "methodContext",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
+$1=_st(self)._isBlockContext();
+if(! smalltalk.assert($1)){
+$2=self;
+return $2;
+};
+$3=_st(self)._outerContext();
+return $3;
+}, self, "methodContext", [], smalltalk.MethodContext)}
+}),
+smalltalk.MethodContext);
+
 smalltalk.addMethod(
 "_outerContext",
 smalltalk.method({
@@ -834,6 +865,7 @@ selector: "temps",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
+_st(self)._deprecatedAPI();
 $1=_st(self)._locals();
 return $1;
 }, self, "temps", [], smalltalk.MethodContext)}

+ 44 - 2
js/Kernel-Methods.js

@@ -1022,6 +1022,24 @@ referencedClasses: []
 }),
 smalltalk.MethodContext);
 
+smalltalk.addMethod(
+"_isBlockContext",
+smalltalk.method({
+selector: "isBlockContext",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(self)._selector())._isNil();
+return $1;
+}, self, "isBlockContext", [], smalltalk.MethodContext)},
+args: [],
+source: "isBlockContext\x0a\x09\x22Block context do not have selectors.\x22\x0a    \x0a\x09^ self selector isNil",
+messageSends: ["isNil", "selector"],
+referencedClasses: []
+}),
+smalltalk.MethodContext);
+
 smalltalk.addMethod(
 "_locals",
 smalltalk.method({
@@ -1039,6 +1057,29 @@ referencedClasses: []
 }),
 smalltalk.MethodContext);
 
+smalltalk.addMethod(
+"_methodContext",
+smalltalk.method({
+selector: "methodContext",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
+$1=_st(self)._isBlockContext();
+if(! smalltalk.assert($1)){
+$2=self;
+return $2;
+};
+$3=_st(self)._outerContext();
+return $3;
+}, self, "methodContext", [], smalltalk.MethodContext)},
+args: [],
+source: "methodContext\x0a\x09self isBlockContext ifFalse: [ ^ self ].\x0a    \x0a    ^ self outerContext",
+messageSends: ["ifFalse:", "isBlockContext", "outerContext"],
+referencedClasses: []
+}),
+smalltalk.MethodContext);
+
 smalltalk.addMethod(
 "_outerContext",
 smalltalk.method({
@@ -1140,12 +1181,13 @@ category: 'accessing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
+_st(self)._deprecatedAPI();
 $1=_st(self)._locals();
 return $1;
 }, self, "temps", [], smalltalk.MethodContext)},
 args: [],
-source: "temps\x0a\x09^ self locals",
-messageSends: ["locals"],
+source: "temps\x0a\x09self deprecatedAPI.\x0a    \x0a\x09^ self locals",
+messageSends: ["deprecatedAPI", "locals"],
 referencedClasses: []
 }),
 smalltalk.MethodContext);

+ 0 - 1
js/boot.js

@@ -636,7 +636,6 @@ function Smalltalk() {
 
 	function popContext(context) {
 		st.thisContext = context.homeContext;
-		context.homeContext = undefined;
 	}
 
 	/* Convert a Smalltalk selector into a JS selector */

+ 16 - 2
st/Compiler-Interpreter.st

@@ -15,6 +15,10 @@ initializeFromMethodContext: aMethodContext
     	self locals at: key put: value ]
 !
 
+localAt: aString put: anObject
+	self locals at: aString put: anObject
+!
+
 locals
 	^ locals ifNil: [ locals := Dictionary new ]
 !
@@ -66,7 +70,7 @@ NodeVisitor subclass: #ASTInterpreter
 !ASTInterpreter methodsFor: 'accessing'!
 
 context
-	^ context
+	^ context ifNil: [ context := AIContext new ]
 !
 
 context: anAIContext
@@ -82,6 +86,13 @@ initialize
 
 !ASTInterpreter methodsFor: 'interpreting'!
 
+eval: aString
+	"Evaluate aString as JS source inside an immediately evaluated JS function. 
+    aString is not sandboxed."
+    
+    ^ Compiler new eval: '(function() { ', aString, ' })()'
+!
+
 interpret: aNode
 	shouldReturn := false.
     ^ self interpretNode: aNode
@@ -126,7 +137,8 @@ visitClassReferenceNode: aNode
 !
 
 visitJSStatementNode: aNode
-	self halt
+	shouldReturn := true.
+	^ self eval: aNode source
 !
 
 visitReturnNode: aNode
@@ -142,9 +154,11 @@ visitSendNode: aNode
 !
 
 visitSequenceNode: aNode
+
 	aNode nodes allButLast do: [ :each | | value |
         value := self interpretNode: each.
 		shouldReturn ifTrue: [ ^ value ] ].
+        
     ^ self interpretNode: aNode nodes last
 !
 

+ 24 - 1
st/Compiler-Tests.st

@@ -11,8 +11,22 @@ analyze: aNode forClass: aClass
 !
 
 interpret: aString
-	"the food is a methodNode. Interpret the sequenceNode only"
+	^ self 
+    	interpret: aString 
+        withArguments: Dictionary new
+!
+
+interpret: aString withArguments: aDictionary
+	"The food is a methodNode. Interpret the sequenceNode only"
+    
+    | ctx |
+    
+    ctx := AIContext new.
+    aDictionary keysAndValuesDo: [ :key :value |
+    	ctx localAt: key put: value ].
+    
     ^ ASTInterpreter new
+    	context: ctx;
     	interpret: (self parse: aString forClass: Object) 
         	nodes first
 !
@@ -39,6 +53,15 @@ testBlockLiteral
 
 testCascade
 	self assert: (self interpret: 'foo ^ OrderedCollection new add: 2; add: 3; yourself') equals: (OrderedCollection with: 2 with: 3)
+!
+
+testInlinedJSStatement
+	self assert: (self interpret: 'foo <return 2+3>') equals: 5.
+    self 
+    	assert: (self 
+    		interpret: 'foo: anInteger <return 2 + anInteger>' 
+        	withArguments: #{ 'anInteger' -> 3}) 
+		equals: 5
 ! !
 
 TestCase subclass: #CodeGeneratorTest

+ 16 - 0
st/Kernel-Methods.st

@@ -367,6 +367,12 @@ locals
 	<return self.locals>
 !
 
+methodContext
+	self isBlockContext ifFalse: [ ^ self ].
+    
+    ^ self outerContext
+!
+
 outerContext
 	^ self home
 !
@@ -394,6 +400,16 @@ selector
 !
 
 temps
+	self deprecatedAPI.
+    
 	^ self locals
 ! !
 
+!MethodContext methodsFor: 'testing'!
+
+isBlockContext
+	"Block context do not have selectors."
+    
+	^ self selector isNil
+! !
+