Browse Source

Compiler: redesign receiver boxing.

Deprecate #needsBoxingAsReceiver.
Herby Vojčík 5 years ago
parent
commit
9de7736644
5 changed files with 188 additions and 148 deletions
  1. 20 0
      lang/API-CHANGES.txt
  2. 75 81
      lang/src/Compiler-IR.js
  3. 29 29
      lang/src/Compiler-IR.st
  4. 49 30
      lang/src/Compiler-Semantic.js
  5. 15 8
      lang/src/Compiler-Semantic.st

+ 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 >>

+ 75 - 81
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);

+ 29 - 29
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

+ 49 - 30
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\x09#{#self -> '$self'. #super -> '$self'}\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:", "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($globals.HashedCollection._newFromPairs_(["self","$self","super","$self"]))._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,24 +1590,19 @@ $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);
 

+ 15 - 8
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,16 +369,19 @@ alias
 
 !PseudoVar methodsFor: 'testing'!
 
+asReceiver
+	#{#self -> '$self'. #super -> '$self'}
+		at: self name
+		ifPresent: [ :newName | ^ self copy name: newName; yourself ]
+		ifAbsent: [ ^ self ]
+!
+
 isImmutable
 	^ true
 !
 
 isPseudoVar
 	^ true
-!
-
-isSelf
-	^ name = 'self'
 ! !
 
 PseudoVar class slots: {#dictionary}!