Browse Source

ASTInterpreter: fix super send.

Herby Vojčík 5 years ago
parent
commit
8bf22ce470
4 changed files with 317 additions and 184 deletions
  1. 40 42
      lang/src/Compiler-Interpreter.js
  2. 3 4
      lang/src/Compiler-Interpreter.st
  3. 202 95
      lang/src/Compiler-Tests.js
  4. 72 43
      lang/src/Compiler-Tests.st

+ 40 - 42
lang/src/Compiler-Interpreter.js

@@ -2980,52 +2980,41 @@ var method;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $2,$1,$3,$receiver;
-var $early={};
-try {
+var $1,$2,$3,$receiver;
 if(!$core.assert(aBoolean)){
 return $recv(aMessage)._sendTo_(anObject);
 }
-$2=$recv(anObject)._class();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["class"]=1;
-//>>excludeEnd("ctx");
-$1=$recv($2)._superclass();
+$1=$recv($recv(anObject)._class())._superclass();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["superclass"]=1;
 //>>excludeEnd("ctx");
 if(($receiver = $1) == null || $receiver.a$nil){
-$3=$self._messageNotUnderstood_receiver_(aMessage,anObject);
+$2=$self._messageNotUnderstood_receiver_(aMessage,anObject);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["messageNotUnderstood:receiver:"]=1;
 //>>excludeEnd("ctx");
-return $3;
+return $2;
 } else {
 $1;
 }
-method=$recv($recv($recv($recv(anObject)._class())._superclass())._methodDictionary())._at_ifAbsent_($recv(aMessage)._selector(),(function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-throw $early=[$self._messageNotUnderstood_receiver_(aMessage,anObject)];
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,3)});
-//>>excludeEnd("ctx");
-}));
-return $recv(method)._sendTo_arguments_(anObject,$recv(aMessage)._arguments());
+$3=$recv($recv($recv($recv($self._context())._method())._methodClass())._superclass())._lookupSelector_($recv(aMessage)._selector());
+if(($receiver = $3) == null || $receiver.a$nil){
+return $self._messageNotUnderstood_receiver_(aMessage,anObject);
+} else {
+method=$3;
 }
-catch(e) {if(e===$early)return e[0]; throw e}
+return $recv(method)._sendTo_arguments_(anObject,$recv(aMessage)._arguments());
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"sendMessage:to:superSend:",{aMessage:aMessage,anObject:anObject,aBoolean:aBoolean,method:method})});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aMessage", "anObject", "aBoolean"],
-source: "sendMessage: aMessage to: anObject superSend: aBoolean\x0a\x09| method |\x0a\x09\x0a\x09aBoolean ifFalse: [ ^ aMessage sendTo: anObject ].\x0a\x09anObject class superclass ifNil: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].\x0a\x09\x0a\x09method := anObject class superclass methodDictionary\x0a\x09\x09at: aMessage selector\x0a\x09\x09ifAbsent: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].\x0a\x09\x09\x0a\x09^ method sendTo: anObject arguments: aMessage arguments",
+source: "sendMessage: aMessage to: anObject superSend: aBoolean\x0a\x09| method |\x0a\x09\x0a\x09aBoolean ifFalse: [ ^ aMessage sendTo: anObject ].\x0a\x09anObject class superclass ifNil: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].\x0a\x09\x0a\x09method := (self context method methodClass superclass lookupSelector: aMessage selector)\x0a\x09\x09ifNil: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].\x0a\x09\x09\x0a\x09^ method sendTo: anObject arguments: aMessage arguments",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["ifFalse:", "sendTo:", "ifNil:", "superclass", "class", "messageNotUnderstood:receiver:", "at:ifAbsent:", "methodDictionary", "selector", "sendTo:arguments:", "arguments"]
+messageSends: ["ifFalse:", "sendTo:", "ifNil:", "superclass", "class", "messageNotUnderstood:receiver:", "lookupSelector:", "methodClass", "method", "context", "selector", "sendTo:arguments:", "arguments"]
 }),
 $globals.ASTInterpreter);
 
@@ -3563,7 +3552,7 @@ var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $2,$1,$5,$6,$4,$3,$8,$10,$9,$11,$12,$13,$15,$14,$16,$17,$7;
+var $2,$1,$5,$6,$4,$3,$9,$8,$11,$10,$12,$13,$15,$14,$17,$16,$18,$19,$7;
 $2=$recv(aNode)._binding();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["binding"]=1;
@@ -3596,54 +3585,63 @@ $ctx1.sendIdx["push:"]=1;
 //>>excludeEnd("ctx");
 return $3;
 }
-$8=$recv($recv(aNode)._binding())._isInstanceVar();
+$9=$recv(aNode)._binding();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["binding"]=2;
+//>>excludeEnd("ctx");
+$8=$recv($9)._isInstanceVar();
 if($core.assert($8)){
-$10=$self._context();
+$11=$self._context();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["context"]=1;
 //>>excludeEnd("ctx");
-$9=$recv($10)._receiver();
-$11=$recv(aNode)._value();
+$10=$recv($11)._receiver();
+$12=$recv(aNode)._value();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["value"]=2;
 //>>excludeEnd("ctx");
-$7=$recv($9)._instVarAt_($11);
+$7=$recv($10)._instVarAt_($12);
+} else {
+$13=$self._context();
+$15=$recv($recv(aNode)._binding())._isSuper();
+if($core.assert($15)){
+$14="self";
 } else {
-$12=$self._context();
-$13=$recv(aNode)._value();
+$14=$recv(aNode)._value();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["value"]=3;
 //>>excludeEnd("ctx");
-$7=$recv($12)._localAt_ifAbsent_($13,(function(){
+}
+$7=$recv($13)._localAt_ifAbsent_($14,(function(){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
-$15=$recv(aNode)._value();
+$17=$recv(aNode)._value();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx2.sendIdx["value"]=4;
 //>>excludeEnd("ctx");
-$14=$recv($15)._isCapitalized();
-if($core.assert($14)){
-$16=$recv($globals.Smalltalk)._globals();
+$16=$recv($17)._isCapitalized();
+if($core.assert($16)){
+$18=$recv($globals.Smalltalk)._globals();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx2.sendIdx["globals"]=2;
 //>>excludeEnd("ctx");
-$17=$recv(aNode)._value();
+$19=$recv(aNode)._value();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx2.sendIdx["value"]=5;
 //>>excludeEnd("ctx");
-return $recv($16)._at_ifAbsent_($17,(function(){
+return $recv($18)._at_ifAbsent_($19,(function(){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx3) {
 //>>excludeEnd("ctx");
 return $recv($recv($globals.Platform)._globals())._at_($recv(aNode)._value());
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx3) {$ctx3.fillBlock({},$ctx2,7)});
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,9)});
 //>>excludeEnd("ctx");
 }));
 }
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,5)});
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,7)});
 //>>excludeEnd("ctx");
 }));
 }
@@ -3655,11 +3653,11 @@ return self;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aNode"],
-source: "visitVariableNode: aNode\x0a\x09aNode binding isUnknownVar ifTrue: [\x0a\x09\x09^ self push: (Platform globals at: aNode value ifAbsent: [ self error: 'Unknown variable' ]) ].\x0a\x09\x09\x0a\x09self push: (aNode binding isInstanceVar\x0a\x09\x09ifTrue: [ self context receiver instVarAt: aNode value ]\x0a\x09\x09ifFalse: [ self context \x0a\x09\x09\x09localAt: aNode value\x0a\x09\x09\x09ifAbsent: [\x0a\x09\x09\x09\x09aNode value isCapitalized\x0a\x09\x09\x09\x09\x09ifTrue: [\x0a\x09\x09\x09\x09\x09\x09Smalltalk globals \x0a\x09\x09\x09\x09\x09\x09\x09at: aNode value \x0a\x09\x09\x09\x09\x09\x09\x09ifAbsent: [ Platform globals at: aNode value ] ] ] ])",
+source: "visitVariableNode: aNode\x0a\x09aNode binding isUnknownVar ifTrue: [\x0a\x09\x09^ self push: (Platform globals at: aNode value ifAbsent: [ self error: 'Unknown variable' ]) ].\x0a\x09\x09\x0a\x09self push: (aNode binding isInstanceVar\x0a\x09\x09ifTrue: [ self context receiver instVarAt: aNode value ]\x0a\x09\x09ifFalse: [ self context \x0a\x09\x09\x09localAt: (aNode binding isSuper ifTrue: [ 'self' ] ifFalse: [ aNode value ])\x0a\x09\x09\x09ifAbsent: [\x0a\x09\x09\x09\x09aNode value isCapitalized\x0a\x09\x09\x09\x09\x09ifTrue: [\x0a\x09\x09\x09\x09\x09\x09Smalltalk globals \x0a\x09\x09\x09\x09\x09\x09\x09at: aNode value \x0a\x09\x09\x09\x09\x09\x09\x09ifAbsent: [ Platform globals at: aNode value ] ] ] ])",
 referencedClasses: ["Platform", "Smalltalk"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["ifTrue:", "isUnknownVar", "binding", "push:", "at:ifAbsent:", "globals", "value", "error:", "ifTrue:ifFalse:", "isInstanceVar", "instVarAt:", "receiver", "context", "localAt:ifAbsent:", "isCapitalized", "at:"]
+messageSends: ["ifTrue:", "isUnknownVar", "binding", "push:", "at:ifAbsent:", "globals", "value", "error:", "ifTrue:ifFalse:", "isInstanceVar", "instVarAt:", "receiver", "context", "localAt:ifAbsent:", "isSuper", "isCapitalized", "at:"]
 }),
 $globals.ASTInterpreter);
 

+ 3 - 4
lang/src/Compiler-Interpreter.st

@@ -752,9 +752,8 @@ sendMessage: aMessage to: anObject superSend: aBoolean
 	aBoolean ifFalse: [ ^ aMessage sendTo: anObject ].
 	anObject class superclass ifNil: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].
 	
-	method := anObject class superclass methodDictionary
-		at: aMessage selector
-		ifAbsent: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].
+	method := (self context method methodClass superclass lookupSelector: aMessage selector)
+		ifNil: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].
 		
 	^ method sendTo: anObject arguments: aMessage arguments
 ! !
@@ -901,7 +900,7 @@ visitVariableNode: aNode
 	self push: (aNode binding isInstanceVar
 		ifTrue: [ self context receiver instVarAt: aNode value ]
 		ifFalse: [ self context 
-			localAt: aNode value
+			localAt: (aNode binding isSuper ifTrue: [ 'self' ] ifFalse: [ aNode value ])
 			ifAbsent: [
 				aNode value isCapitalized
 					ifTrue: [

+ 202 - 95
lang/src/Compiler-Tests.js

@@ -425,6 +425,36 @@ messageSends: ["new"]
 }),
 $globals.CodeGeneratorTest);
 
+$core.addMethod(
+$core.method({
+selector: "should:class:receiver:return:",
+protocol: "testing",
+fn: function (aString,aClass,anObject,aResult){
+var self=this,$self=this;
+var method,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self.receiver=anObject;
+method=$recv($self._compiler())._install_forClass_protocol_(aString,aClass,"tests");
+result=$recv($self.receiver)._perform_($recv(method)._selector());
+$recv(aClass)._removeCompiledMethod_(method);
+$self._assert_equals_(aResult,result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"should:class:receiver:return:",{aString:aString,aClass:aClass,anObject:anObject,aResult:aResult,method:method,result:result})});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aString", "aClass", "anObject", "aResult"],
+source: "should: aString class: aClass receiver: anObject return: aResult\x0a\x09| method result |\x0a\x0a\x09receiver := anObject.\x0a\x09method := self compiler install: aString forClass: aClass protocol: 'tests'.\x0a\x09result := receiver perform: method selector.\x0a\x09aClass removeCompiledMethod: method.\x0a\x09self assert: aResult equals: result",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["install:forClass:protocol:", "compiler", "perform:", "selector", "removeCompiledMethod:", "assert:equals:"]
+}),
+$globals.CodeGeneratorTest);
+
 $core.addMethod(
 $core.method({
 selector: "should:receiver:raise:",
@@ -494,33 +524,21 @@ selector: "should:receiver:return:",
 protocol: "testing",
 fn: function (aString,anObject,aResult){
 var self=this,$self=this;
-var method,result;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1,$2;
-$self.receiver=anObject;
-$1=$self._compiler();
-$2=$recv(anObject)._class();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["class"]=1;
-//>>excludeEnd("ctx");
-method=$recv($1)._install_forClass_protocol_(aString,$2,"tests");
-result=$recv($self.receiver)._perform_($recv(method)._selector());
-$recv($recv(anObject)._class())._removeCompiledMethod_(method);
-$self._assert_equals_(aResult,result);
-return self;
+return $self._should_class_receiver_return_(aString,$recv(anObject)._class(),anObject,aResult);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"should:receiver:return:",{aString:aString,anObject:anObject,aResult:aResult,method:method,result:result})});
+}, function($ctx1) {$ctx1.fill(self,"should:receiver:return:",{aString:aString,anObject:anObject,aResult:aResult})});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aString", "anObject", "aResult"],
-source: "should: aString receiver: anObject return: aResult\x0a\x09| method result |\x0a\x0a\x09receiver := anObject.\x0a\x09method := self compiler install: aString forClass: anObject class protocol: 'tests'.\x0a\x09result := receiver perform: method selector.\x0a\x09anObject class removeCompiledMethod: method.\x0a\x09self assert: aResult equals: result",
+source: "should: aString receiver: anObject return: aResult\x0a\x09^ self should: aString class: anObject class receiver: anObject return: aResult",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["install:forClass:protocol:", "compiler", "class", "perform:", "selector", "removeCompiledMethod:", "assert:equals:"]
+messageSends: ["should:class:receiver:return:", "class"]
 }),
 $globals.CodeGeneratorTest);
 
@@ -1487,6 +1505,106 @@ messageSends: ["should:receiver:return:"]
 }),
 $globals.CodeGeneratorTest);
 
+$core.addMethod(
+$core.method({
+selector: "testSuperSend2",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._should_receiver_return_("foo ^ super isNil",nil,false);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testSuperSend2",{})});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testSuperSend2\x0a\x09self \x0a\x09\x09should: 'foo ^ super isNil'\x0a\x09\x09receiver: nil\x0a\x09\x09return: false",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["should:receiver:return:"]
+}),
+$globals.CodeGeneratorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testSuperSend3",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._should_class_receiver_return_("doo ^ super isNil",$globals.Object,nil,false);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testSuperSend3",{})});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testSuperSend3\x0a\x09self \x0a\x09\x09should: 'doo ^ super isNil'\x0a\x09\x09class: Object\x0a\x09\x09receiver: nil\x0a\x09\x09return: false",
+referencedClasses: ["Object"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["should:class:receiver:return:"]
+}),
+$globals.CodeGeneratorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testSuperSend4",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._should_receiver_return_("foo ^ super asJavaScriptObject","me",["m", "e"]);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testSuperSend4",{})});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testSuperSend4\x0a\x09self \x0a\x09\x09should: 'foo ^ super asJavaScriptObject'\x0a\x09\x09receiver: 'me'\x0a\x09\x09return: #('m' 'e')",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["should:receiver:return:"]
+}),
+$globals.CodeGeneratorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testSuperSend5",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._should_class_receiver_return_("foo [super addLast: 4] on: Error do: [ self add: 5 ]. ^ self",$globals.SequenceableCollection,[(1), (2), (3)],[(1), (2), (3), (5)]);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testSuperSend5",{})});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testSuperSend5\x0a\x09self \x0a\x09\x09should: 'foo [super addLast: 4] on: Error do: [ self add: 5 ]. ^ self'\x0a\x09\x09class: SequenceableCollection\x0a\x09\x09receiver: #(1 2 3)\x0a\x09\x09return: #(1 2 3 5)",
+referencedClasses: ["SequenceableCollection"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["should:class:receiver:return:"]
+}),
+$globals.CodeGeneratorTest);
+
 $core.addMethod(
 $core.method({
 selector: "testTempVariables",
@@ -1895,23 +2013,52 @@ $globals.CodeGeneratorTest);
 $core.addClass("ASTInterpreterTest", $globals.CodeGeneratorTest, [], "Compiler-Tests");
 $core.addMethod(
 $core.method({
-selector: "interpret:receiver:withArguments:",
+selector: "interpret:forClass:receiver:withArguments:",
 protocol: "private",
-fn: function (aString,anObject,aDictionary){
+fn: function (aString,aClass,anObject,aDictionary){
 var self=this,$self=this;
-var ctx,ast,interpreter;
+var ctx;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+ctx=$self._prepareContextFor_class_receiver_withArguments_(aString,aClass,anObject,aDictionary);
+$1=$recv(ctx)._interpreter();
+$recv($1)._proceed();
+return $recv($1)._result();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"interpret:forClass:receiver:withArguments:",{aString:aString,aClass:aClass,anObject:anObject,aDictionary:aDictionary,ctx:ctx})});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aString", "aClass", "anObject", "aDictionary"],
+source: "interpret: aString forClass: aClass receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a\x09\x0a\x09| ctx |\x0a\x09\x0a\x09ctx := self prepareContextFor: aString class: aClass receiver: anObject withArguments: aDictionary.\x0a\x09\x0a\x09^ ctx interpreter proceed; result",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["prepareContextFor:class:receiver:withArguments:", "proceed", "interpreter", "result"]
+}),
+$globals.ASTInterpreterTest);
+
+$core.addMethod(
+$core.method({
+selector: "prepareContextFor:class:receiver:withArguments:",
+protocol: "private",
+fn: function (aString,aClass,anObject,aDictionary){
+var self=this,$self=this;
+var ctx,ast;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
 var $1,$2,$3,$receiver;
-interpreter=$recv($globals.ASTInterpreter)._new();
+ast=$self._parse_forClass_(aString,aClass);
+$1=$recv($globals.AIContext)._new();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["new"]=1;
 //>>excludeEnd("ctx");
-ast=$self._parse_forClass_(aString,$recv(anObject)._class());
-$1=$recv($globals.AIContext)._new();
 $recv($1)._receiver_(anObject);
-$recv($1)._interpreter_(interpreter);
+$recv($1)._selector_($recv(ast)._selector());
+$recv($1)._interpreter_($recv($globals.ASTInterpreter)._new());
 ctx=$recv($1)._yourself();
 $2=$recv(ast)._sequenceNode();
 if(($receiver = $2) == null || $receiver.a$nil){
@@ -1938,48 +2085,52 @@ return $recv(ctx)._localAt_put_(key,value);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,3)});
 //>>excludeEnd("ctx");
 }));
-$3=interpreter;
+$3=$recv(ctx)._interpreter();
 $recv($3)._context_(ctx);
 $recv($3)._node_(ast);
 $recv($3)._enterNode();
-$recv($3)._proceed();
-return $recv($3)._result();
+return ctx;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx,ast:ast,interpreter:interpreter})});
+}, function($ctx1) {$ctx1.fill(self,"prepareContextFor:class:receiver:withArguments:",{aString:aString,aClass:aClass,anObject:anObject,aDictionary:aDictionary,ctx:ctx,ast:ast})});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
-args: ["aString", "anObject", "aDictionary"],
-source: "interpret: aString receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a\x09\x0a\x09| ctx ast interpreter |\x0a\x09\x0a\x09interpreter := ASTInterpreter new.\x0a\x09ast := self parse: aString forClass: anObject class.\x0a\x09\x0a\x09ctx := AIContext new\x0a\x09\x09receiver: anObject;\x0a\x09\x09interpreter: interpreter;\x0a\x09\x09yourself.\x0a\x09\x09\x0a\x09\x22Define locals for the context\x22\x0a\x09ast sequenceNode ifNotNil: [ :sequence |\x0a\x09\x09sequence temps do: [ :each |\x0a\x09\x09\x09ctx defineLocal: each ] ].\x0a\x09\x09\x0a\x09aDictionary keysAndValuesDo: [ :key :value |\x0a\x09\x09ctx localAt: key put: value ].\x0a\x09\x0a\x09^ interpreter\x0a\x09\x09context: ctx;\x0a\x09\x09node: ast;\x0a\x09\x09enterNode;\x0a\x09\x09proceed;\x0a\x09\x09result",
-referencedClasses: ["ASTInterpreter", "AIContext"],
+args: ["aString", "aClass", "anObject", "aDictionary"],
+source: "prepareContextFor: aString class: aClass receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a\x09\x0a\x09| ctx ast |\x0a\x09\x0a\x09ast := self parse: aString forClass: aClass.\x0a\x09\x0a\x09ctx := AIContext new\x0a\x09\x09receiver: anObject;\x0a\x09\x09selector: ast selector;\x0a\x09\x09interpreter: ASTInterpreter new;\x0a\x09\x09yourself.\x0a\x09\x09\x0a\x09\x22Define locals for the context\x22\x0a\x09ast sequenceNode ifNotNil: [ :sequence |\x0a\x09\x09sequence temps do: [ :each |\x0a\x09\x09\x09ctx defineLocal: each ] ].\x0a\x09\x09\x0a\x09aDictionary keysAndValuesDo: [ :key :value |\x0a\x09\x09ctx localAt: key put: value ].\x0a\x09\x0a\x09ctx interpreter\x0a\x09\x09context: ctx;\x0a\x09\x09node: ast;\x0a\x09\x09enterNode.\x0a\x09\x0a\x09^ctx",
+referencedClasses: ["AIContext", "ASTInterpreter"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["new", "parse:forClass:", "class", "receiver:", "interpreter:", "yourself", "ifNotNil:", "sequenceNode", "do:", "temps", "defineLocal:", "keysAndValuesDo:", "localAt:put:", "context:", "node:", "enterNode", "proceed", "result"]
+messageSends: ["parse:forClass:", "receiver:", "new", "selector:", "selector", "interpreter:", "yourself", "ifNotNil:", "sequenceNode", "do:", "temps", "defineLocal:", "keysAndValuesDo:", "localAt:put:", "context:", "interpreter", "node:", "enterNode"]
 }),
 $globals.ASTInterpreterTest);
 
 $core.addMethod(
 $core.method({
-selector: "should:receiver:return:",
+selector: "should:class:receiver:return:",
 protocol: "testing",
-fn: function (aString,anObject,aResult){
+fn: function (aString,aClass,anObject,aResult){
 var self=this,$self=this;
+var method,result;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
 $self.receiver=anObject;
-return $self._assert_equals_($self._interpret_receiver_withArguments_(aString,$self.receiver,$globals.HashedCollection._newFromPairs_([])),aResult);
+method=$recv($self._compiler())._install_forClass_protocol_(aString,aClass,"tests");
+result=$self._interpret_forClass_receiver_withArguments_(aString,aClass,$self.receiver,$globals.HashedCollection._newFromPairs_([]));
+$recv(aClass)._removeCompiledMethod_(method);
+$self._assert_equals_(aResult,result);
+return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"should:receiver:return:",{aString:aString,anObject:anObject,aResult:aResult})});
+}, function($ctx1) {$ctx1.fill(self,"should:class:receiver:return:",{aString:aString,aClass:aClass,anObject:anObject,aResult:aResult,method:method,result:result})});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
-args: ["aString", "anObject", "aResult"],
-source: "should: aString receiver: anObject return: aResult\x0a\x09receiver := anObject.\x0a\x09\x0a\x09^ self \x0a\x09\x09assert: (self interpret: aString receiver: receiver withArguments: #{})\x0a\x09\x09equals: aResult",
+args: ["aString", "aClass", "anObject", "aResult"],
+source: "should: aString class: aClass receiver: anObject return: aResult\x0a\x09| method result |\x0a\x0a\x09receiver := anObject.\x0a\x09method := self compiler install: aString forClass: aClass protocol: 'tests'.\x0a\x09result := self interpret: aString forClass: aClass receiver: receiver withArguments: #{}.\x0a\x09aClass removeCompiledMethod: method.\x0a\x09self assert: aResult equals: result",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["assert:equals:", "interpret:receiver:withArguments:"]
+messageSends: ["install:forClass:protocol:", "compiler", "interpret:forClass:receiver:withArguments:", "removeCompiledMethod:", "assert:equals:"]
 }),
 $globals.ASTInterpreterTest);
 
@@ -1988,74 +2139,30 @@ $globals.ASTInterpreterTest);
 $core.addClass("ASTDebuggerTest", $globals.ASTInterpreterTest, [], "Compiler-Tests");
 $core.addMethod(
 $core.method({
-selector: "interpret:receiver:withArguments:",
+selector: "interpret:forClass:receiver:withArguments:",
 protocol: "private",
-fn: function (aString,anObject,aDictionary){
+fn: function (aString,aClass,anObject,aDictionary){
 var self=this,$self=this;
-var ctx,ast,debugger_;
+var ctx;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1,$2,$3,$4,$5,$receiver;
-$1=$recv($globals.AIContext)._new();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["new"]=1;
-//>>excludeEnd("ctx");
-$recv($1)._receiver_(anObject);
-$recv($1)._interpreter_($recv($globals.ASTInterpreter)._new());
-ctx=$recv($1)._yourself();
-ast=$self._parse_forClass_(aString,$recv(anObject)._class());
-$2=$recv(ast)._sequenceNode();
-if(($receiver = $2) == null || $receiver.a$nil){
-$2;
-} else {
-var sequence;
-sequence=$receiver;
-$recv($recv(sequence)._temps())._do_((function(each){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(ctx)._defineLocal_(each);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,2)});
-//>>excludeEnd("ctx");
-}));
-}
-$recv(aDictionary)._keysAndValuesDo_((function(key,value){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(ctx)._localAt_put_(key,value);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,3)});
-//>>excludeEnd("ctx");
-}));
-$3=$recv(ctx)._interpreter();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["interpreter"]=1;
-//>>excludeEnd("ctx");
-$recv($3)._context_(ctx);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["context:"]=1;
-//>>excludeEnd("ctx");
-$4=$recv(ctx)._interpreter();
-$recv($4)._node_(ast);
-$recv($4)._enterNode();
-debugger_=$recv($globals.ASTDebugger)._context_(ctx);
-$5=debugger_;
-$recv($5)._proceed();
-return $recv($5)._result();
+var $1;
+ctx=$self._prepareContextFor_class_receiver_withArguments_(aString,aClass,anObject,aDictionary);
+$1=$recv($globals.ASTDebugger)._context_(ctx);
+$recv($1)._proceed();
+return $recv($1)._result();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx,ast:ast,debugger_:debugger_})});
+}, function($ctx1) {$ctx1.fill(self,"interpret:forClass:receiver:withArguments:",{aString:aString,aClass:aClass,anObject:anObject,aDictionary:aDictionary,ctx:ctx})});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
-args: ["aString", "anObject", "aDictionary"],
-source: "interpret: aString receiver: anObject withArguments: aDictionary\x0a\x09| ctx ast debugger |\x0a\x09\x0a\x09ctx := AIContext new\x0a\x09\x09receiver: anObject;\x0a\x09\x09interpreter: ASTInterpreter new;\x0a\x09\x09yourself.\x0a\x09ast := self parse: aString forClass: anObject class.\x0a\x09\x09\x0a\x09\x22Define locals for the context\x22\x0a\x09ast sequenceNode ifNotNil: [ :sequence |\x0a\x09\x09sequence temps do: [ :each |\x0a\x09\x09\x09ctx defineLocal: each ] ].\x0a\x09\x0a\x09aDictionary keysAndValuesDo: [ :key :value |\x0a\x09\x09ctx localAt: key put: value ].\x0a\x09ctx interpreter context: ctx.\x0a\x09\x0a\x09ctx interpreter node: ast; enterNode.\x0a\x09\x0a\x09debugger := ASTDebugger context: ctx.\x0a\x09\x0a\x09^ debugger \x0a\x09\x09proceed; \x0a\x09\x09result",
-referencedClasses: ["AIContext", "ASTInterpreter", "ASTDebugger"],
+args: ["aString", "aClass", "anObject", "aDictionary"],
+source: "interpret: aString forClass: aClass receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a\x09\x0a\x09| ctx |\x0a\x09\x0a\x09ctx := self prepareContextFor: aString class: aClass receiver: anObject withArguments: aDictionary.\x0a\x09\x0a\x09^ (ASTDebugger context: ctx) proceed; result",
+referencedClasses: ["ASTDebugger"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["receiver:", "new", "interpreter:", "yourself", "parse:forClass:", "class", "ifNotNil:", "sequenceNode", "do:", "temps", "defineLocal:", "keysAndValuesDo:", "localAt:put:", "context:", "interpreter", "node:", "enterNode", "proceed", "result"]
+messageSends: ["prepareContextFor:class:receiver:withArguments:", "proceed", "context:", "result"]
 }),
 $globals.ASTDebuggerTest);
 

+ 72 - 43
lang/src/Compiler-Tests.st

@@ -138,6 +138,16 @@ tearDown
 
 !CodeGeneratorTest methodsFor: 'testing'!
 
+should: aString class: aClass receiver: anObject return: aResult
+	| method result |
+
+	receiver := anObject.
+	method := self compiler install: aString forClass: aClass protocol: 'tests'.
+	result := receiver perform: method selector.
+	aClass removeCompiledMethod: method.
+	self assert: aResult equals: result
+!
+
 should: aString receiver: anObject raise: anErrorClass
 	| method result |
 
@@ -149,13 +159,7 @@ should: aString receiver: anObject raise: anErrorClass
 !
 
 should: aString receiver: anObject return: aResult
-	| method result |
-
-	receiver := anObject.
-	method := self compiler install: aString forClass: anObject class protocol: 'tests'.
-	result := receiver perform: method selector.
-	anObject class removeCompiledMethod: method.
-	self assert: aResult equals: result
+	^ self should: aString class: anObject class receiver: anObject return: aResult
 !
 
 should: aString return: anObject
@@ -386,6 +390,36 @@ testSuperSend
 		return: false
 !
 
+testSuperSend2
+	self 
+		should: 'foo ^ super isNil'
+		receiver: nil
+		return: false
+!
+
+testSuperSend3
+	self 
+		should: 'doo ^ super isNil'
+		class: Object
+		receiver: nil
+		return: false
+!
+
+testSuperSend4
+	self 
+		should: 'foo ^ super asJavaScriptObject'
+		receiver: 'me'
+		return: #('m' 'e')
+!
+
+testSuperSend5
+	self 
+		should: 'foo [super addLast: 4] on: Error do: [ self add: 5 ]. ^ self'
+		class: SequenceableCollection
+		receiver: #(1 2 3)
+		return: #(1 2 3 5)
+!
+
 testTempVariables
 	self should: 'foo | a | ^ a' return: nil.
 	self should: 'foo | AVariable | ^ AVariable' return: nil.
@@ -478,17 +512,27 @@ CodeGeneratorTest subclass: #ASTInterpreterTest
 
 !ASTInterpreterTest methodsFor: 'private'!
 
-interpret: aString receiver: anObject withArguments: aDictionary
+interpret: aString forClass: aClass receiver: anObject withArguments: aDictionary
+	"The food is a methodNode. Interpret the sequenceNode only"
+	
+	| ctx |
+	
+	ctx := self prepareContextFor: aString class: aClass receiver: anObject withArguments: aDictionary.
+	
+	^ ctx interpreter proceed; result
+!
+
+prepareContextFor: aString class: aClass receiver: anObject withArguments: aDictionary
 	"The food is a methodNode. Interpret the sequenceNode only"
 	
-	| ctx ast interpreter |
+	| ctx ast |
 	
-	interpreter := ASTInterpreter new.
-	ast := self parse: aString forClass: anObject class.
+	ast := self parse: aString forClass: aClass.
 	
 	ctx := AIContext new
 		receiver: anObject;
-		interpreter: interpreter;
+		selector: ast selector;
+		interpreter: ASTInterpreter new;
 		yourself.
 		
 	"Define locals for the context"
@@ -499,22 +543,24 @@ interpret: aString receiver: anObject withArguments: aDictionary
 	aDictionary keysAndValuesDo: [ :key :value |
 		ctx localAt: key put: value ].
 	
-	^ interpreter
+	ctx interpreter
 		context: ctx;
 		node: ast;
-		enterNode;
-		proceed;
-		result
+		enterNode.
+	
+	^ctx
 ! !
 
 !ASTInterpreterTest methodsFor: 'testing'!
 
-should: aString receiver: anObject return: aResult
+should: aString class: aClass receiver: anObject return: aResult
+	| method result |
+
 	receiver := anObject.
-	
-	^ self 
-		assert: (self interpret: aString receiver: receiver withArguments: #{})
-		equals: aResult
+	method := self compiler install: aString forClass: aClass protocol: 'tests'.
+	result := self interpret: aString forClass: aClass receiver: receiver withArguments: #{}.
+	aClass removeCompiledMethod: method.
+	self assert: aResult equals: result
 ! !
 
 ASTInterpreterTest subclass: #ASTDebuggerTest
@@ -523,31 +569,14 @@ ASTInterpreterTest subclass: #ASTDebuggerTest
 
 !ASTDebuggerTest methodsFor: 'private'!
 
-interpret: aString receiver: anObject withArguments: aDictionary
-	| ctx ast debugger |
-	
-	ctx := AIContext new
-		receiver: anObject;
-		interpreter: ASTInterpreter new;
-		yourself.
-	ast := self parse: aString forClass: anObject class.
-		
-	"Define locals for the context"
-	ast sequenceNode ifNotNil: [ :sequence |
-		sequence temps do: [ :each |
-			ctx defineLocal: each ] ].
-	
-	aDictionary keysAndValuesDo: [ :key :value |
-		ctx localAt: key put: value ].
-	ctx interpreter context: ctx.
+interpret: aString forClass: aClass receiver: anObject withArguments: aDictionary
+	"The food is a methodNode. Interpret the sequenceNode only"
 	
-	ctx interpreter node: ast; enterNode.
+	| ctx |
 	
-	debugger := ASTDebugger context: ctx.
+	ctx := self prepareContextFor: aString class: aClass receiver: anObject withArguments: aDictionary.
 	
-	^ debugger 
-		proceed; 
-		result
+	^ (ASTDebugger context: ctx) proceed; result
 ! !
 
 CodeGeneratorTest subclass: #InliningCodeGeneratorTest