6 Commits 25f05d294d ... a73dbbd68a

Author SHA1 Message Date
  Herby Vojčík a73dbbd68a Memoize dictionary of receiver names. 4 years ago
  Herby Vojčík 3f59afc322 Compile nil receiver as `$nil`. 4 years ago
  Herby Vojčík d97910b6ec Pseudovars including nil, true, false actually parsed as such. 4 years ago
  Herby Vojčík 89aeddb62d Fix glitch in lookupContextForLocal:ifNone:. 4 years ago
  Herby Vojčík dbc29ce2b5 Compiler: cleaner super receiver handling. 4 years ago
  Herby Vojčík 9de7736644 Compiler: redesign receiver boxing. 4 years ago

+ 20 - 0
lang/API-CHANGES.txt

@@ -1,6 +1,7 @@
 0.24.1:
 
 * Deprecate Behavior >> javascriptConstructor(:)
+* Deprecate IRInstruction >> needsBoxingAsReceiver
 * UnknownVar => ExternallyKnownVar
 
 + Behavior >>
@@ -13,10 +14,19 @@
   + tryIfTrue:catch:
 + ExternallyKnownVar >>
   + isExternallyKnownVar
++ IRInstruction >>
+  + asReceiver
++ IRValue >>
+  + asReceiver
++ IRVariable >>
+  + asReceiver
++ PseudoVar
+  + asReceiver
 + SemanticAnalyzer >>
   + errorInvalidAssignment:
   + isVariableKnown:inPackage:
 + ScopeVar >>
+  + asReceiver
   + isExternallyKnownVar
 + SequenceableCollection >>
   + copyWithFirst:
@@ -36,6 +46,13 @@
 - Error >>
   - beSmalltalkError
   - isSmalltalkError
+- IRInstruction >>
+  - isSelf
+- IRValue >>
+  - needsBoxingAsReceiver
+- IRVariable >>
+  - isSelf
+  - needsBoxingAsReceiver
 - JavaScriptError >>
   - shouldBeStubbed
   - wrap
@@ -44,8 +61,11 @@
   - stubToAtMost:
 - MethodLexicalScope >>
   - unknownVariables
+- PseudoVar >>
+  - isSelf
 - ScopeVar >>
   - isArgVar
+  - isSelf
   - isUnknownVar
   - validateAssignment
 - SemanticAnalyzer >>

+ 2 - 2
lang/base/parser.js

@@ -3349,9 +3349,9 @@ define(["./boot"], function(__boot) {
         return cached.result;
       }
 
-      s0 = peg$parseliteral();
+      s0 = peg$parsevariable();
       if (s0 === peg$FAILED) {
-        s0 = peg$parsevariable();
+        s0 = peg$parseliteral();
         if (s0 === peg$FAILED) {
           s0 = peg$parsesubexpression();
         }

+ 1 - 1
lang/base/parser.pegjs

@@ -194,7 +194,7 @@ block = '[' params:wsBlockParamList? sequence:wsBlockSequenceWs ']' {
 	return newNode($globals.BlockNode)._parameters_(params || [])._dagChildren_([sequence]);
 }
 
-operand = literal / reference / subexpression
+operand = reference / literal / subexpression
 
 wsUnaryMessage = ws selector:unarySelector !':' {
 	return newNode($globals.SendNode)._selector_(selector);

+ 78 - 84
lang/src/Compiler-IR.js

@@ -1155,29 +1155,29 @@ $globals.IRInstruction);
 
 $core.addMethod(
 $core.method({
-selector: "isClosure",
-protocol: "testing",
+selector: "asReceiver",
+protocol: "converting",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isClosure\x0a\x09^ false",
+source: "asReceiver\x0a\x09\x22Return customized form to act as receiver.\x0a\x09Return self to use standard $recv(...) boxing.\x22\x0a\x09^ nil",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
 messageSends: []
 }, function ($methodClass){ return function (){
 var self=this,$self=this;
-return false;
+return nil;
 
 }; }),
 $globals.IRInstruction);
 
 $core.addMethod(
 $core.method({
-selector: "isInlined",
+selector: "isClosure",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isInlined\x0a\x09^ false",
+source: "isClosure\x0a\x09^ false",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1191,11 +1191,11 @@ $globals.IRInstruction);
 
 $core.addMethod(
 $core.method({
-selector: "isMethod",
+selector: "isInlined",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isMethod\x0a\x09^ false",
+source: "isInlined\x0a\x09^ false",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1209,11 +1209,11 @@ $globals.IRInstruction);
 
 $core.addMethod(
 $core.method({
-selector: "isSelf",
+selector: "isMethod",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isSelf\x0a\x09^ false",
+source: "isMethod\x0a\x09^ false",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1344,15 +1344,21 @@ selector: "needsBoxingAsReceiver",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "needsBoxingAsReceiver\x0a\x09^ true",
+source: "needsBoxingAsReceiver\x0a\x09self deprecatedAPI: 'Use asReceiver isNil instead.'.\x0a\x09^ self asReceiver isNil",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: []
+messageSends: ["deprecatedAPI:", "isNil", "asReceiver"]
 }, function ($methodClass){ return function (){
 var self=this,$self=this;
-return true;
-
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._deprecatedAPI_("Use asReceiver isNil instead.");
+return $recv($self._asReceiver())._isNil();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"needsBoxingAsReceiver",{})});
+//>>excludeEnd("ctx");
 }; }),
 $globals.IRInstruction);
 
@@ -2932,18 +2938,18 @@ $globals.IRValue);
 
 $core.addMethod(
 $core.method({
-selector: "needsBoxingAsReceiver",
-protocol: "testing",
+selector: "asReceiver",
+protocol: "converting",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "needsBoxingAsReceiver\x0a\x09^ false",
+source: "asReceiver\x0a\x09^ self",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
 messageSends: []
 }, function ($methodClass){ return function (){
 var self=this,$self=this;
-return false;
+return self;
 
 }; }),
 $globals.IRValue);
@@ -3016,23 +3022,53 @@ $globals.IRVariable);
 
 $core.addMethod(
 $core.method({
-selector: "isSelf",
-protocol: "testing",
+selector: "asReceiver",
+protocol: "converting",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isSelf\x0a\x09^ self variable isSelf",
+source: "asReceiver\x0a\x09self variable asReceiver\x0a\x09\x09ifNil: [ ^ super asReceiver ]\x0a\x09\x09ifNotNil: [ :receiverVar |\x0a\x09\x09\x09self variable == receiverVar ifTrue: [ ^ self ].\x0a\x09\x09\x09^ self copy variable: receiverVar; yourself ]",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["isSelf", "variable"]
+messageSends: ["ifNil:ifNotNil:", "asReceiver", "variable", "ifTrue:", "==", "variable:", "copy", "yourself"]
 }, function ($methodClass){ return function (){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-return $recv($self._variable())._isSelf();
+var $2,$1,$3,$4,$5,$receiver;
+$2=$self._variable();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["variable"]=1;
+//>>excludeEnd("ctx");
+$1=$recv($2)._asReceiver();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"isSelf",{})});
+$ctx1.sendIdx["asReceiver"]=1;
+//>>excludeEnd("ctx");
+if(($receiver = $1) == null || $receiver.a$nil){
+$3=(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true,
+//>>excludeEnd("ctx");
+($methodClass.superclass||$boot.nilAsClass).fn.prototype._asReceiver.call($self));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = false;
+//>>excludeEnd("ctx");;
+return $3;
+} else {
+var receiverVar;
+receiverVar=$receiver;
+$4=$recv($self._variable()).__eq_eq(receiverVar);
+if($core.assert($4)){
+return self;
+}
+$5=$self._copy();
+$recv($5)._variable_(receiverVar);
+return $recv($5)._yourself();
+}
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"asReceiver",{})});
 //>>excludeEnd("ctx");
 }; }),
 $globals.IRVariable);
@@ -3078,29 +3114,6 @@ return true;
 }; }),
 $globals.IRVariable);
 
-$core.addMethod(
-$core.method({
-selector: "needsBoxingAsReceiver",
-protocol: "testing",
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "needsBoxingAsReceiver\x0a\x09^ self variable isPseudoVar not",
-referencedClasses: [],
-//>>excludeEnd("ide");
-pragmas: [],
-messageSends: ["not", "isPseudoVar", "variable"]
-}, function ($methodClass){ return function (){
-var self=this,$self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-return $recv($recv($self._variable())._isPseudoVar())._not();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"needsBoxingAsReceiver",{})});
-//>>excludeEnd("ctx");
-}; }),
-$globals.IRVariable);
-
 $core.addMethod(
 $core.method({
 selector: "variable",
@@ -4452,59 +4465,40 @@ selector: "visitReceiver:",
 protocol: "visiting",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["anIRInstruction"],
-source: "visitReceiver: anIRInstruction\x0a\x09| instr |\x0a\x0a\x09anIRInstruction isSelf\x0a\x09\x09ifTrue: [ instr := anIRInstruction copy\x0a\x09\x09\x09variable: (anIRInstruction variable copy name: '$self'; yourself);\x0a\x09\x09\x09yourself ]\x0a\x09\x09ifFalse: [ instr := anIRInstruction ].\x0a\x09\x0a\x09instr needsBoxingAsReceiver ifFalse: [ ^ self visit: instr ].\x0a\x09\x0a\x09self stream nextPutAll: '$recv('.\x0a\x09self visit: instr.\x0a\x09self stream nextPutAll: ')'",
+source: "visitReceiver: anIRInstruction\x0a\x09anIRInstruction asReceiver\x0a\x09\x09ifNotNil: [ :instr | self visit: instr ]\x0a\x09\x09ifNil: [\x0a\x09\x09\x09self stream nextPutAll: '$recv('.\x0a\x09\x09\x09self visit: anIRInstruction.\x0a\x09\x09\x09self stream nextPutAll: ')' ]",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["ifTrue:ifFalse:", "isSelf", "variable:", "copy", "name:", "variable", "yourself", "ifFalse:", "needsBoxingAsReceiver", "visit:", "nextPutAll:", "stream"]
+messageSends: ["ifNotNil:ifNil:", "asReceiver", "visit:", "nextPutAll:", "stream"]
 }, function ($methodClass){ return function (anIRInstruction){
 var self=this,$self=this;
-var instr;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1,$2,$4,$5,$3,$6,$7,$8;
-$1=$recv(anIRInstruction)._isSelf();
-if($core.assert($1)){
-$2=$recv(anIRInstruction)._copy();
+var $1,$2,$receiver;
+$1=$recv(anIRInstruction)._asReceiver();
+if(($receiver = $1) == null || $receiver.a$nil){
+$2=$self._stream();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["copy"]=1;
+$ctx1.sendIdx["stream"]=1;
 //>>excludeEnd("ctx");
-$4=$recv($recv(anIRInstruction)._variable())._copy();
-$recv($4)._name_("$self");
-$5=$recv($4)._yourself();
+$recv($2)._nextPutAll_("$recv(");
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["yourself"]=1;
+$ctx1.sendIdx["nextPutAll:"]=1;
 //>>excludeEnd("ctx");
-$3=$5;
-$recv($2)._variable_($3);
-instr=$recv($2)._yourself();
-instr;
+$self._visit_(anIRInstruction);
+$recv($self._stream())._nextPutAll_(")");
 } else {
-instr=anIRInstruction;
-instr;
-}
-$6=$recv(instr)._needsBoxingAsReceiver();
-if(!$core.assert($6)){
-$7=$self._visit_(instr);
+var instr;
+instr=$receiver;
+$self._visit_(instr);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["visit:"]=1;
 //>>excludeEnd("ctx");
-return $7;
 }
-$8=$self._stream();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["stream"]=1;
-//>>excludeEnd("ctx");
-$recv($8)._nextPutAll_("$recv(");
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["nextPutAll:"]=1;
-//>>excludeEnd("ctx");
-$self._visit_(instr);
-$recv($self._stream())._nextPutAll_(")");
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"visitReceiver:",{anIRInstruction:anIRInstruction,instr:instr})});
+}, function($ctx1) {$ctx1.fill(self,"visitReceiver:",{anIRInstruction:anIRInstruction})});
 //>>excludeEnd("ctx");
 }; }),
 $globals.IRJSTranslator);
@@ -4541,11 +4535,11 @@ selector: "visitSuperSend:",
 protocol: "visiting",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["anIRSend"],
-source: "visitSuperSend: anIRSend\x0a\x09self stream nextPutSupercallFor: anIRSend with: [\x0a\x09\x09self stream\x0a\x09\x09\x09nextPutAll: anIRSend receiver variable lookupAsJavaScriptSource, '.';\x0a\x09\x09\x09nextPutAll: anIRSend javaScriptSelector, '.call'.\x0a\x09\x09self\x0a\x09\x09\x09visitInstructionList: {anIRSend receiver}, anIRSend arguments\x0a\x09\x09\x09enclosedBetween: '(' and: ')' ]",
+source: "visitSuperSend: anIRSend\x0a\x09self stream nextPutSupercallFor: anIRSend with: [\x0a\x09\x09self stream\x0a\x09\x09\x09nextPutAll: anIRSend receiver variable lookupAsJavaScriptSource, '.';\x0a\x09\x09\x09nextPutAll: anIRSend javaScriptSelector, '.call'.\x0a\x09\x09self\x0a\x09\x09\x09visitInstructionList: {anIRSend receiver asReceiver}, anIRSend arguments\x0a\x09\x09\x09enclosedBetween: '(' and: ')' ]",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["nextPutSupercallFor:with:", "stream", "nextPutAll:", ",", "lookupAsJavaScriptSource", "variable", "receiver", "javaScriptSelector", "visitInstructionList:enclosedBetween:and:", "arguments"]
+messageSends: ["nextPutSupercallFor:with:", "stream", "nextPutAll:", ",", "lookupAsJavaScriptSource", "variable", "receiver", "javaScriptSelector", "visitInstructionList:enclosedBetween:and:", "asReceiver", "arguments"]
 }, function ($methodClass){ return function (anIRSend){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
@@ -4580,7 +4574,7 @@ $7=$recv($recv(anIRSend)._javaScriptSelector()).__comma(".call");
 $ctx2.sendIdx[","]=2;
 //>>excludeEnd("ctx");
 $recv($2)._nextPutAll_($7);
-return $self._visitInstructionList_enclosedBetween_and_($recv([$recv(anIRSend)._receiver()]).__comma($recv(anIRSend)._arguments()),"(",")");
+return $self._visitInstructionList_enclosedBetween_and_($recv([$recv($recv(anIRSend)._receiver())._asReceiver()]).__comma($recv(anIRSend)._arguments()),"(",")");
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
 //>>excludeEnd("ctx");

+ 30 - 30
lang/src/Compiler-IR.st

@@ -307,6 +307,14 @@ replaceWith: anIRInstruction
 	self parent replace: self with: anIRInstruction
 ! !
 
+!IRInstruction methodsFor: 'converting'!
+
+asReceiver
+	"Return customized form to act as receiver.
+	Return self to use standard $recv(...) boxing."
+	^ nil
+! !
+
 !IRInstruction methodsFor: 'testing'!
 
 isClosure
@@ -321,10 +329,6 @@ isMethod
 	^ false
 !
 
-isSelf
-	^ false
-!
-
 isSend
 	^ false
 !
@@ -346,7 +350,8 @@ isVariable
 !
 
 needsBoxingAsReceiver
-	^ true
+	self deprecatedAPI: 'Use asReceiver isNil instead.'.
+	^ self asReceiver isNil
 !
 
 yieldsValue
@@ -740,10 +745,10 @@ value: aString
 	value := aString
 ! !
 
-!IRValue methodsFor: 'testing'!
+!IRValue methodsFor: 'converting'!
 
-needsBoxingAsReceiver
-	^ false
+asReceiver
+	^ self
 ! !
 
 !IRValue methodsFor: 'visiting'!
@@ -768,11 +773,17 @@ variable: aScopeVariable
 	variable := aScopeVariable
 ! !
 
-!IRVariable methodsFor: 'testing'!
+!IRVariable methodsFor: 'converting'!
 
-isSelf
-	^ self variable isSelf
-!
+asReceiver
+	self variable asReceiver
+		ifNil: [ ^ super asReceiver ]
+		ifNotNil: [ :receiverVar |
+			self variable == receiverVar ifTrue: [ ^ self ].
+			^ self copy variable: receiverVar; yourself ]
+! !
+
+!IRVariable methodsFor: 'testing'!
 
 isSuper
 	^ self variable isSuper
@@ -780,10 +791,6 @@ isSuper
 
 isVariable
 	^ true
-!
-
-needsBoxingAsReceiver
-	^ self variable isPseudoVar not
 ! !
 
 !IRVariable methodsFor: 'visiting'!
@@ -1068,19 +1075,12 @@ visitInstructionList: anArray enclosedBetween: aString and: anotherString
 !
 
 visitReceiver: anIRInstruction
-	| instr |
-
-	anIRInstruction isSelf
-		ifTrue: [ instr := anIRInstruction copy
-			variable: (anIRInstruction variable copy name: '$self'; yourself);
-			yourself ]
-		ifFalse: [ instr := anIRInstruction ].
-	
-	instr needsBoxingAsReceiver ifFalse: [ ^ self visit: instr ].
-	
-	self stream nextPutAll: '$recv('.
-	self visit: instr.
-	self stream nextPutAll: ')'
+	anIRInstruction asReceiver
+		ifNotNil: [ :instr | self visit: instr ]
+		ifNil: [
+			self stream nextPutAll: '$recv('.
+			self visit: anIRInstruction.
+			self stream nextPutAll: ')' ]
 !
 
 visitSend: anIRSend
@@ -1097,7 +1097,7 @@ visitSuperSend: anIRSend
 			nextPutAll: anIRSend receiver variable lookupAsJavaScriptSource, '.';
 			nextPutAll: anIRSend javaScriptSelector, '.call'.
 		self
-			visitInstructionList: {anIRSend receiver}, anIRSend arguments
+			visitInstructionList: {anIRSend receiver asReceiver}, anIRSend arguments
 			enclosedBetween: '(' and: ')' ]
 ! !
 

+ 45 - 12
lang/src/Compiler-Interpreter.js

@@ -1071,11 +1071,11 @@ selector: "lookupContextForLocal:ifNone:",
 protocol: "private",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aString", "aBlock"],
-source: "lookupContextForLocal: aString ifNone: aBlock\x0a\x09\x22Lookup the context defining the local named `aString` \x0a\x09up to the method context\x22\x0a\x0a\x09^ self locals \x0a\x09\x09at: aString\x0a\x09\x09ifPresent: [ self ]\x0a\x09\x09ifAbsent: [ \x0a\x09\x09\x09self outerContext \x0a\x09\x09\x09\x09ifNil: aBlock\x0a\x09\x09\x09\x09ifNotNil: [ :context | \x0a\x09\x09\x09\x09\x09context lookupContextForLocal: aString ] ]",
+source: "lookupContextForLocal: aString ifNone: aBlock\x0a\x09\x22Lookup the context defining the local named `aString` \x0a\x09up to the method context\x22\x0a\x0a\x09^ self locals \x0a\x09\x09at: aString\x0a\x09\x09ifPresent: [ self ]\x0a\x09\x09ifAbsent: [ \x0a\x09\x09\x09self outerContext \x0a\x09\x09\x09\x09ifNil: aBlock\x0a\x09\x09\x09\x09ifNotNil: [ :context | \x0a\x09\x09\x09\x09\x09context lookupContextForLocal: aString ifNone: aBlock ] ]",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["at:ifPresent:ifAbsent:", "locals", "ifNil:ifNotNil:", "outerContext", "lookupContextForLocal:"]
+messageSends: ["at:ifPresent:ifAbsent:", "locals", "ifNil:ifNotNil:", "outerContext", "lookupContextForLocal:ifNone:"]
 }, function ($methodClass){ return function (aString,aBlock){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
@@ -1094,7 +1094,7 @@ return $recv($1)._ifNil_ifNotNil_(aBlock,(function(context){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx3) {
 //>>excludeEnd("ctx");
-return $recv(context)._lookupContextForLocal_(aString);
+return $recv(context)._lookupContextForLocal_ifNone_(aString,aBlock);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx3) {$ctx3.fillBlock({context:context},$ctx2,3)});
 //>>excludeEnd("ctx");
@@ -3404,17 +3404,17 @@ selector: "visitVariableNode:",
 protocol: "visiting",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aNode"],
-source: "visitVariableNode: aNode\x0a\x09aNode binding isExternallyKnownVar 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 ] ] ] ])",
+source: "visitVariableNode: aNode\x0a\x09aNode binding isExternallyKnownVar 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 = 'nil' ifTrue: [ nil ] ifFalse: [\x0a\x09\x09\x09\x09aNode value = 'true' ifTrue: [ true ] ifFalse: [\x0a\x09\x09\x09\x09aNode value = 'false' ifTrue: [ false ] ifFalse: [\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:", "isExternallyKnownVar", "binding", "push:", "at:ifAbsent:", "globals", "value", "error:", "ifTrue:ifFalse:", "isInstanceVar", "instVarAt:", "receiver", "context", "localAt:ifAbsent:", "isSuper", "isCapitalized", "at:"]
+messageSends: ["ifTrue:", "isExternallyKnownVar", "binding", "push:", "at:ifAbsent:", "globals", "value", "error:", "ifTrue:ifFalse:", "isInstanceVar", "instVarAt:", "receiver", "context", "localAt:ifAbsent:", "isSuper", "=", "isCapitalized", "at:"]
 }, function ($methodClass){ return function (aNode){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $2,$1,$5,$6,$4,$3,$9,$8,$11,$10,$12,$13,$15,$14,$17,$16,$18,$19,$7;
+var $2,$1,$5,$6,$4,$3,$9,$8,$11,$10,$12,$13,$15,$14,$17,$16,$19,$18,$21,$20,$23,$22,$24,$25,$7;
 $2=$recv(aNode)._binding();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["binding"]=1;
@@ -3482,26 +3482,59 @@ $17=$recv(aNode)._value();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx2.sendIdx["value"]=4;
 //>>excludeEnd("ctx");
-$16=$recv($17)._isCapitalized();
-if($core.assert($16)){
-$18=$recv($globals.Smalltalk)._globals();
+$16=$recv($17).__eq("nil");
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx2.sendIdx["globals"]=2;
+$ctx2.sendIdx["="]=1;
 //>>excludeEnd("ctx");
+if($core.assert($16)){
+return nil;
+} else {
 $19=$recv(aNode)._value();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx2.sendIdx["value"]=5;
 //>>excludeEnd("ctx");
-return $recv($18)._at_ifAbsent_($19,(function(){
+$18=$recv($19).__eq("true");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["="]=2;
+//>>excludeEnd("ctx");
+if($core.assert($18)){
+return true;
+} else {
+$21=$recv(aNode)._value();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["value"]=6;
+//>>excludeEnd("ctx");
+$20=$recv($21).__eq("false");
+if($core.assert($20)){
+return false;
+} else {
+$23=$recv(aNode)._value();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["value"]=7;
+//>>excludeEnd("ctx");
+$22=$recv($23)._isCapitalized();
+if($core.assert($22)){
+$24=$recv($globals.Smalltalk)._globals();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["globals"]=2;
+//>>excludeEnd("ctx");
+$25=$recv(aNode)._value();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["value"]=8;
+//>>excludeEnd("ctx");
+return $recv($24)._at_ifAbsent_($25,(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,9)});
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,15)});
 //>>excludeEnd("ctx");
 }));
 }
+}
+}
+}
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,7)});
 //>>excludeEnd("ctx");

+ 5 - 2
lang/src/Compiler-Interpreter.st

@@ -372,7 +372,7 @@ lookupContextForLocal: aString ifNone: aBlock
 			self outerContext 
 				ifNil: aBlock
 				ifNotNil: [ :context | 
-					context lookupContextForLocal: aString ] ]
+					context lookupContextForLocal: aString ifNone: aBlock ] ]
 ! !
 
 !AIContext methodsFor: 'testing'!
@@ -895,11 +895,14 @@ visitVariableNode: aNode
 		ifFalse: [ self context 
 			localAt: (aNode binding isSuper ifTrue: [ 'self' ] ifFalse: [ aNode value ])
 			ifAbsent: [
+				aNode value = 'nil' ifTrue: [ nil ] ifFalse: [
+				aNode value = 'true' ifTrue: [ true ] ifFalse: [
+				aNode value = 'false' ifTrue: [ false ] ifFalse: [
 				aNode value isCapitalized
 					ifTrue: [
 						Smalltalk globals 
 							at: aNode value 
-							ifAbsent: [ Platform globals at: aNode value ] ] ] ])
+							ifAbsent: [ Platform globals at: aNode value ] ] ] ] ] ] ])
 ! !
 
 Error subclass: #ASTInterpreterError

+ 84 - 43
lang/src/Compiler-Semantic.js

@@ -1073,29 +1073,29 @@ $globals.ScopeVar);
 
 $core.addMethod(
 $core.method({
-selector: "isClassRefVar",
-protocol: "testing",
+selector: "asReceiver",
+protocol: "converting",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isClassRefVar\x0a\x09^ false",
+source: "asReceiver\x0a\x09\x22Return customized copy to use as receiver,\x0a\x09or self if suffices.\x22\x0a\x09^ nil",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
 messageSends: []
 }, function ($methodClass){ return function (){
 var self=this,$self=this;
-return false;
+return nil;
 
 }; }),
 $globals.ScopeVar);
 
 $core.addMethod(
 $core.method({
-selector: "isExternallyKnownVar",
+selector: "isClassRefVar",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isExternallyKnownVar\x0a\x09^ false",
+source: "isClassRefVar\x0a\x09^ false",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1109,11 +1109,11 @@ $globals.ScopeVar);
 
 $core.addMethod(
 $core.method({
-selector: "isImmutable",
+selector: "isExternallyKnownVar",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isImmutable\x0a\x09^ false",
+source: "isExternallyKnownVar\x0a\x09^ false",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1127,11 +1127,11 @@ $globals.ScopeVar);
 
 $core.addMethod(
 $core.method({
-selector: "isInstanceVar",
+selector: "isImmutable",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isInstanceVar\x0a\x09^ false",
+source: "isImmutable\x0a\x09^ false",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1145,11 +1145,11 @@ $globals.ScopeVar);
 
 $core.addMethod(
 $core.method({
-selector: "isPseudoVar",
+selector: "isInstanceVar",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isPseudoVar\x0a\x09^ false",
+source: "isInstanceVar\x0a\x09^ false",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1163,11 +1163,11 @@ $globals.ScopeVar);
 
 $core.addMethod(
 $core.method({
-selector: "isSelf",
+selector: "isPseudoVar",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isSelf\x0a\x09^ false",
+source: "isPseudoVar\x0a\x09^ false",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1530,29 +1530,53 @@ $globals.PseudoVar);
 
 $core.addMethod(
 $core.method({
-selector: "isImmutable",
+selector: "asReceiver",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isImmutable\x0a\x09^ true",
+source: "asReceiver\x0a\x09self class receiverNames\x0a\x09\x09at: self name\x0a\x09\x09ifPresent: [ :newName | ^ self copy name: newName; yourself ]\x0a\x09\x09ifAbsent: [ ^ self ]",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: []
+messageSends: ["at:ifPresent:ifAbsent:", "receiverNames", "class", "name", "name:", "copy", "yourself"]
 }, function ($methodClass){ return function (){
 var self=this,$self=this;
-return true;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+var $early={};
+try {
+$recv($recv($self._class())._receiverNames())._at_ifPresent_ifAbsent_($self._name(),(function(newName){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$1=$self._copy();
+$recv($1)._name_(newName);
+throw $early=[$recv($1)._yourself()];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({newName:newName},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),(function(){
+throw $early=[self];
 
+}));
+return self;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"asReceiver",{})});
+//>>excludeEnd("ctx");
 }; }),
 $globals.PseudoVar);
 
 $core.addMethod(
 $core.method({
-selector: "isPseudoVar",
+selector: "isImmutable",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isPseudoVar\x0a\x09^ true",
+source: "isImmutable\x0a\x09^ true",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1566,29 +1590,24 @@ $globals.PseudoVar);
 
 $core.addMethod(
 $core.method({
-selector: "isSelf",
+selector: "isPseudoVar",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "isSelf\x0a\x09^ name = 'self'",
+source: "isPseudoVar\x0a\x09^ true",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["="]
+messageSends: []
 }, function ($methodClass){ return function (){
 var self=this,$self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-return $recv($self.name).__eq("self");
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"isSelf",{})});
-//>>excludeEnd("ctx");
+return true;
+
 }; }),
 $globals.PseudoVar);
 
 
-$core.setSlots($globals.PseudoVar.a$cls, ["dictionary"]);
+$core.setSlots($globals.PseudoVar.a$cls, ["dictionary", "receiverNames"]);
 $core.addMethod(
 $core.method({
 selector: "dictionary",
@@ -1641,29 +1660,51 @@ return $1;
 }; }),
 $globals.PseudoVar.a$cls);
 
-
-$core.addClass("SuperVar", $globals.PseudoVar, [], "Compiler-Semantic");
-//>>excludeStart("ide", pragmas.excludeIdeData);
-$globals.SuperVar.comment="I am a 'super' pseudo variable.";
-//>>excludeEnd("ide");
 $core.addMethod(
 $core.method({
-selector: "alias",
+selector: "receiverNames",
 protocol: "accessing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "alias\x0a\x09^ '$self'",
-referencedClasses: [],
+source: "receiverNames\x0a\x09^ receiverNames ifNil: [ receiverNames := Dictionary new\x0a\x09\x09at: #self put: '$self';\x0a\x09\x09at: #super put: '$self';\x0a\x09\x09at: #nil put: '$nil';\x0a\x09\x09yourself ]",
+referencedClasses: ["Dictionary"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: []
+messageSends: ["ifNil:", "at:put:", "new", "yourself"]
 }, function ($methodClass){ return function (){
 var self=this,$self=this;
-return "$self";
-
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2,$receiver;
+$1=$self.receiverNames;
+if(($receiver = $1) == null || $receiver.a$nil){
+$2=$recv($globals.Dictionary)._new();
+$recv($2)._at_put_("self","$self");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["at:put:"]=1;
+//>>excludeEnd("ctx");
+$recv($2)._at_put_("super","$self");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["at:put:"]=2;
+//>>excludeEnd("ctx");
+$recv($2)._at_put_("nil","$nil");
+$self.receiverNames=$recv($2)._yourself();
+return $self.receiverNames;
+} else {
+return $1;
+}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"receiverNames",{})});
+//>>excludeEnd("ctx");
 }; }),
-$globals.SuperVar);
+$globals.PseudoVar.a$cls);
 
+
+$core.addClass("SuperVar", $globals.PseudoVar, [], "Compiler-Semantic");
+//>>excludeStart("ide", pragmas.excludeIdeData);
+$globals.SuperVar.comment="I am a 'super' pseudo variable.";
+//>>excludeEnd("ide");
 $core.addMethod(
 $core.method({
 selector: "isSuper",

+ 24 - 13
lang/src/Compiler-Semantic.st

@@ -233,6 +233,14 @@ scope: aScope
 	scope := aScope
 ! !
 
+!ScopeVar methodsFor: 'converting'!
+
+asReceiver
+	"Return customized copy to use as receiver,
+	or self if suffices."
+	^ nil
+! !
+
 !ScopeVar methodsFor: 'testing'!
 
 isClassRefVar
@@ -255,10 +263,6 @@ isPseudoVar
 	^ false
 !
 
-isSelf
-	^ false
-!
-
 isSuper
 	^ false
 !
@@ -365,19 +369,22 @@ alias
 
 !PseudoVar methodsFor: 'testing'!
 
+asReceiver
+	self class receiverNames
+		at: self name
+		ifPresent: [ :newName | ^ self copy name: newName; yourself ]
+		ifAbsent: [ ^ self ]
+!
+
 isImmutable
 	^ true
 !
 
 isPseudoVar
 	^ true
-!
-
-isSelf
-	^ name = 'self'
 ! !
 
-PseudoVar class slots: {#dictionary}!
+PseudoVar class slots: {#dictionary. #receiverNames}!
 
 !PseudoVar class methodsFor: 'accessing'!
 
@@ -390,6 +397,14 @@ dictionary
 		at: #true put: PseudoVar;
 		at: #thisContext put: ThisContextVar;
 		yourself ]
+!
+
+receiverNames
+	^ receiverNames ifNil: [ receiverNames := Dictionary new
+		at: #self put: '$self';
+		at: #super put: '$self';
+		at: #nil put: '$nil';
+		yourself ]
 ! !
 
 PseudoVar subclass: #SuperVar
@@ -400,10 +415,6 @@ I am a 'super' pseudo variable.!
 
 !SuperVar methodsFor: 'accessing'!
 
-alias
-	^ '$self'
-!
-
 lookupAsJavaScriptSource
 	^ '($methodClass.superclass||$boot.nilAsClass).fn.prototype'
 ! !

+ 1 - 1
lang/src/Kernel-Infrastructure.js

@@ -1701,7 +1701,7 @@ basicEval=$recv(anObject)._at_ifAbsent_("innerEval",(function(){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
-return nil._asJavaScriptObject();
+return $nil._asJavaScriptObject();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
 //>>excludeEnd("ctx");

+ 4 - 4
lang/src/Kernel-Tests.js

@@ -16957,7 +16957,7 @@ var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-$self._assert_equals_(nil._copy(),nil);
+$self._assert_equals_($nil._copy(),nil);
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"testCopying",{})});
@@ -16981,7 +16981,7 @@ var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-$self._assert_($recv(nil._deepCopy()).__eq(nil));
+$self._assert_($recv($nil._deepCopy()).__eq(nil));
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"testDeepCopy",{})});
@@ -17064,8 +17064,8 @@ var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-$self._assert_(nil._isNil());
-$self._deny_(nil._notNil());
+$self._assert_($nil._isNil());
+$self._deny_($nil._notNil());
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"testIsNil",{})});