Herby Vojčík 4 лет назад
Родитель
Сommit
ca5c674ea2

+ 79 - 17
lang/src/Compiler-Core.js

@@ -319,6 +319,29 @@ return $globals.IRJSTranslator;
 }; }),
 $globals.CodeGenerator);
 
+$core.addMethod(
+$core.method({
+selector: "lateIRPragmator",
+protocol: "compiling",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "lateIRPragmator\x0a\x09^ IRLatePragmator new",
+referencedClasses: ["IRLatePragmator"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["new"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv($globals.IRLatePragmator)._new();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"lateIRPragmator",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.CodeGenerator);
+
 $core.addMethod(
 $core.method({
 selector: "semanticAnalyzer",
@@ -351,11 +374,11 @@ selector: "transformersDictionary",
 protocol: "compiling",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "transformersDictionary\x0a\x09^ transformersDictionary ifNil: [ transformersDictionary := Dictionary new\x0a\x09\x09at: '1000-earlyPragmas' put: self earlyAstPragmator;\x0a\x09\x09at: '2000-semantic' put: self semanticAnalyzer;\x0a\x09\x09at: '5000-astToIr' put: self translator;\x0a\x09\x09at: '8000-irToJs' put: self irTranslator;\x0a\x09\x09yourself ]",
+source: "transformersDictionary\x0a\x09^ transformersDictionary ifNil: [ transformersDictionary := Dictionary new\x0a\x09\x09at: '1000-earlyPragmas' put: self earlyAstPragmator;\x0a\x09\x09at: '2000-semantic' put: self semanticAnalyzer;\x0a\x09\x09at: '5000-astToIr' put: self translator;\x0a\x09\x09at: '8000-irToJs' put: self irTranslator;\x0a\x09\x09at: '9000-latePragmas' put: self lateIRPragmator;\x0a\x09\x09yourself ]",
 referencedClasses: ["Dictionary"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["ifNil:", "at:put:", "new", "earlyAstPragmator", "semanticAnalyzer", "translator", "irTranslator", "yourself"]
+messageSends: ["ifNil:", "at:put:", "new", "earlyAstPragmator", "semanticAnalyzer", "translator", "irTranslator", "lateIRPragmator", "yourself"]
 }, function ($methodClass){ return function (){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
@@ -378,6 +401,10 @@ $recv($2)._at_put_("5000-astToIr",$self._translator());
 $ctx1.sendIdx["at:put:"]=3;
 //>>excludeEnd("ctx");
 $recv($2)._at_put_("8000-irToJs",$self._irTranslator());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["at:put:"]=4;
+//>>excludeEnd("ctx");
+$recv($2)._at_put_("9000-latePragmas",$self._lateIRPragmator());
 $self.transformersDictionary=$recv($2)._yourself();
 return $self.transformersDictionary;
 } else {
@@ -589,42 +616,77 @@ selector: "compile:forClass:protocol:",
 protocol: "compiling",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aString", "aClass", "anotherString"],
-source: "compile: aString forClass: aClass protocol: anotherString\x0a\x09| compilationResult result pragmas closureFactory |\x0a\x09compilationResult := self\x0a\x09\x09start: aString forClass: aClass protocol: anotherString;\x0a\x09\x09compileNode: (self parse: aString).\x0a\x09closureFactory := self\x0a\x09\x09eval: '(function ($methodClass){ return ', compilationResult compiledSource, '; })'\x0a\x09\x09forPackage: self currentPackage.\x0a\x09result := Smalltalk core method: #{\x0a\x09\x09#selector -> compilationResult selector.\x0a\x09\x09#protocol -> anotherString.\x0a\x09\x09#source -> compilationResult source.\x0a\x09\x09#messageSends -> compilationResult messageSends asArray.\x0a\x09\x09#args -> compilationResult arguments asArray.\x0a\x09\x09#referencedClasses -> compilationResult classReferences asArray.\x0a\x09} withFactory: closureFactory.\x0a\x09result pragmas: compilationResult pragmas.\x0a\x09^ result",
+source: "compile: aString forClass: aClass protocol: anotherString\x0a\x09| compilationResult wrappedJs result pragmas closureFactory |\x0a\x09compilationResult := self\x0a\x09\x09start: aString forClass: aClass protocol: anotherString;\x0a\x09\x09compileNode: (self parse: aString).\x0a\x09wrappedJs := compilationResult attachments\x0a\x09\x09ifEmpty: [ '(function ($methodClass){ return ', compilationResult compiledSource, '; })' ]\x0a\x09\x09ifNotEmpty: [ :attachments | '(function ($methodClass){ return (function(method){Object.defineProperty(method,\x22a$atx\x22,{enumerable:false,configurable:true,writable:true,value:', attachments asJavaScriptSource, '});return method})(', compilationResult compiledSource, '); })' ].\x0a\x09closureFactory := self\x0a\x09\x09eval: wrappedJs\x0a\x09\x09forPackage: self currentPackage.\x0a\x09result := Smalltalk core method: #{\x0a\x09\x09#selector -> compilationResult selector.\x0a\x09\x09#protocol -> anotherString.\x0a\x09\x09#source -> compilationResult source.\x0a\x09\x09#messageSends -> compilationResult messageSends asArray.\x0a\x09\x09#args -> compilationResult arguments asArray.\x0a\x09\x09#referencedClasses -> compilationResult classReferences asArray.\x0a\x09} withFactory: closureFactory.\x0a\x09result pragmas: compilationResult pragmas.\x0a\x09^ result",
 referencedClasses: ["Smalltalk"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["start:forClass:protocol:", "compileNode:", "parse:", "eval:forPackage:", ",", "compiledSource", "currentPackage", "method:withFactory:", "core", "selector", "source", "asArray", "messageSends", "arguments", "classReferences", "pragmas:", "pragmas"]
+messageSends: ["start:forClass:protocol:", "compileNode:", "parse:", "ifEmpty:ifNotEmpty:", "attachments", ",", "compiledSource", "asJavaScriptSource", "eval:forPackage:", "currentPackage", "method:withFactory:", "core", "selector", "source", "asArray", "messageSends", "arguments", "classReferences", "pragmas:", "pragmas"]
 }, function ($methodClass){ return function (aString,aClass,anotherString){
 var self=this,$self=this;
-var compilationResult,result,pragmas,closureFactory;
+var compilationResult,wrappedJs,result,pragmas,closureFactory;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1,$2,$4,$5,$6,$7,$3;
+var $2,$1,$4,$3,$5,$7,$8,$9,$10,$6;
 $self._start_forClass_protocol_(aString,aClass,anotherString);
 compilationResult=$self._compileNode_($self._parse_(aString));
-$1=$recv("(function ($methodClass){ return ".__comma($recv(compilationResult)._compiledSource())).__comma("; })");
+wrappedJs=$recv($recv(compilationResult)._attachments())._ifEmpty_ifNotEmpty_((function(){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx[","]=1;
+return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
-closureFactory=$self._eval_forPackage_($1,$self._currentPackage());
-$2=$recv($globals.Smalltalk)._core();
-$4=$recv(compilationResult)._selector();
-$5=$recv(compilationResult)._source();
-$6=$recv($recv(compilationResult)._messageSends())._asArray();
+$2=$recv(compilationResult)._compiledSource();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["compiledSource"]=1;
+//>>excludeEnd("ctx");
+$1="(function ($methodClass){ return ".__comma($2);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx[","]=2;
+//>>excludeEnd("ctx");
+return $recv($1).__comma("; })");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx[","]=1;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),(function(attachments){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$4=$recv("(function ($methodClass){ return (function(method){Object.defineProperty(method,\x22a$atx\x22,{enumerable:false,configurable:true,writable:true,value:".__comma($recv(attachments)._asJavaScriptSource())).__comma("});return method})(");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx[","]=5;
+//>>excludeEnd("ctx");
+$3=$recv($4).__comma($recv(compilationResult)._compiledSource());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx[","]=4;
+//>>excludeEnd("ctx");
+return $recv($3).__comma("); })");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx[","]=3;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({attachments:attachments},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+closureFactory=$self._eval_forPackage_(wrappedJs,$self._currentPackage());
+$5=$recv($globals.Smalltalk)._core();
+$7=$recv(compilationResult)._selector();
+$8=$recv(compilationResult)._source();
+$9=$recv($recv(compilationResult)._messageSends())._asArray();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["asArray"]=1;
 //>>excludeEnd("ctx");
-$7=$recv($recv(compilationResult)._arguments())._asArray();
+$10=$recv($recv(compilationResult)._arguments())._asArray();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["asArray"]=2;
 //>>excludeEnd("ctx");
-$3=$globals.HashedCollection._newFromPairs_(["selector",$4,"protocol",anotherString,"source",$5,"messageSends",$6,"args",$7,"referencedClasses",$recv($recv(compilationResult)._classReferences())._asArray()]);
-result=$recv($2)._method_withFactory_($3,closureFactory);
+$6=$globals.HashedCollection._newFromPairs_(["selector",$7,"protocol",anotherString,"source",$8,"messageSends",$9,"args",$10,"referencedClasses",$recv($recv(compilationResult)._classReferences())._asArray()]);
+result=$recv($5)._method_withFactory_($6,closureFactory);
 $recv(result)._pragmas_($recv(compilationResult)._pragmas());
 return result;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"compile:forClass:protocol:",{aString:aString,aClass:aClass,anotherString:anotherString,compilationResult:compilationResult,result:result,pragmas:pragmas,closureFactory:closureFactory})});
+}, function($ctx1) {$ctx1.fill(self,"compile:forClass:protocol:",{aString:aString,aClass:aClass,anotherString:anotherString,compilationResult:compilationResult,wrappedJs:wrappedJs,result:result,pragmas:pragmas,closureFactory:closureFactory})});
 //>>excludeEnd("ctx");
 }; }),
 $globals.Compiler);

+ 10 - 2
lang/src/Compiler-Core.st

@@ -77,6 +77,10 @@ irTranslatorClass
 	^ IRJSTranslator
 !
 
+lateIRPragmator
+	^ IRLatePragmator new
+!
+
 semanticAnalyzer
 	^ (SemanticAnalyzer on: self currentClass)
 		thePackage: self currentPackage;
@@ -89,6 +93,7 @@ transformersDictionary
 		at: '2000-semantic' put: self semanticAnalyzer;
 		at: '5000-astToIr' put: self translator;
 		at: '8000-irToJs' put: self irTranslator;
+		at: '9000-latePragmas' put: self lateIRPragmator;
 		yourself ]
 !
 
@@ -149,12 +154,15 @@ ast: aString forClass: aClass protocol: anotherString
 !
 
 compile: aString forClass: aClass protocol: anotherString
-	| compilationResult result pragmas closureFactory |
+	| compilationResult wrappedJs result pragmas closureFactory |
 	compilationResult := self
 		start: aString forClass: aClass protocol: anotherString;
 		compileNode: (self parse: aString).
+	wrappedJs := compilationResult attachments
+		ifEmpty: [ '(function ($methodClass){ return ', compilationResult compiledSource, '; })' ]
+		ifNotEmpty: [ :attachments | '(function ($methodClass){ return (function(method){Object.defineProperty(method,"a$atx",{enumerable:false,configurable:true,writable:true,value:', attachments asJavaScriptSource, '});return method})(', compilationResult compiledSource, '); })' ].
 	closureFactory := self
-		eval: '(function ($methodClass){ return ', compilationResult compiledSource, '; })'
+		eval: wrappedJs
 		forPackage: self currentPackage.
 	result := Smalltalk core method: #{
 		#selector -> compilationResult selector.

+ 133 - 2
lang/src/Compiler-IR.js

@@ -1,4 +1,4 @@
-define(["amber/boot", "require", "amber/core/Compiler-AST", "amber/core/Kernel-Dag", "amber/core/Kernel-Methods", "amber/core/Kernel-Objects"], function($boot,requirejs){"use strict";
+define(["amber/boot", "require", "amber/core/Compiler-AST", "amber/core/Kernel-Dag", "amber/core/Kernel-Helpers", "amber/core/Kernel-Methods", "amber/core/Kernel-Objects"], function($boot,requirejs){"use strict";
 var $core=$boot.api,nil=$boot.nilAsValue,$nil=$boot.nilAsReceiver,$recv=$boot.asReceiver,$globals=$boot.globals;
 var $pkg = $core.addPackage("Compiler-IR");
 $pkg.innerEval = function (expr) { return eval(expr); };
@@ -1969,7 +1969,7 @@ $globals.IRClosure);
 
 
 
-$core.addClass("IRMethod", $globals.IRClosureInstruction, ["theClass", "source", "compiledSource", "selector", "pragmas", "classReferences", "sendIndexes", "internalVariables"], "Compiler-IR");
+$core.addClass("IRMethod", $globals.IRClosureInstruction, ["theClass", "source", "compiledSource", "attachments", "selector", "pragmas", "classReferences", "sendIndexes", "internalVariables"], "Compiler-IR");
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.IRMethod.comment="I am a method instruction";
 //>>excludeEnd("ide");
@@ -1996,6 +1996,36 @@ return $recv(aVisitor)._visitIRMethod_(self);
 }; }),
 $globals.IRMethod);
 
+$core.addMethod(
+$core.method({
+selector: "attachments",
+protocol: "accessing",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "attachments\x0a\x09^ attachments ifNil: [ attachments := #{} ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["ifNil:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$receiver;
+$1=$self.attachments;
+if(($receiver = $1) == null || $receiver.a$nil){
+$self.attachments=$globals.HashedCollection._newFromPairs_([]);
+return $self.attachments;
+} else {
+return $1;
+}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"attachments",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.IRMethod);
+
 $core.addMethod(
 $core.method({
 selector: "classReferences",
@@ -4462,6 +4492,105 @@ $globals.IRJSTranslator);
 
 
 
+$core.addClass("IRPragmator", $globals.IRVisitor, ["irMethod"], "Compiler-IR");
+$core.addMethod(
+$core.method({
+selector: "irMethod",
+protocol: "accessing",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "irMethod\x0a\x09^ irMethod",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: []
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+return $self.irMethod;
+
+}; }),
+$globals.IRPragmator);
+
+$core.addMethod(
+$core.method({
+selector: "irMethod:",
+protocol: "accessing",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject"],
+source: "irMethod: anObject\x0a\x09irMethod := anObject",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: []
+}, function ($methodClass){ return function (anObject){
+var self=this,$self=this;
+$self.irMethod=anObject;
+return self;
+
+}; }),
+$globals.IRPragmator);
+
+$core.addMethod(
+$core.method({
+selector: "visitIRMethod:",
+protocol: "visiting",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anIRMethod"],
+source: "visitIRMethod: anIRMethod\x0a\x09self irMethod: anIRMethod.\x0a\x09self processPragmas: anIRMethod pragmas.\x0a\x09^ anIRMethod",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["irMethod:", "processPragmas:", "pragmas"]
+}, function ($methodClass){ return function (anIRMethod){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._irMethod_(anIRMethod);
+$self._processPragmas_($recv(anIRMethod)._pragmas());
+return anIRMethod;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitIRMethod:",{anIRMethod:anIRMethod})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.IRPragmator);
+
+
+
+$core.addClass("IRLatePragmator", $globals.IRPragmator, [], "Compiler-IR");
+$core.addMethod(
+$core.method({
+selector: "jsOverride:",
+protocol: "as yet unclassified",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aString"],
+source: "jsOverride: aString\x0a\x09self irMethod attachments\x0a\x09\x09at: aString\x0a\x09\x09put: (NativeFunction\x0a\x09\x09\x09constructorNamed: #Function\x0a\x09\x09\x09value: 'return this.', irMethod selector asJavaScriptMethodName, '()')",
+referencedClasses: ["NativeFunction"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["at:put:", "attachments", "irMethod", "constructorNamed:value:", ",", "asJavaScriptMethodName", "selector"]
+}, function ($methodClass){ return function (aString){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$3,$2;
+$1=$recv($self._irMethod())._attachments();
+$3=$recv("return this.".__comma($recv($recv($self.irMethod)._selector())._asJavaScriptMethodName())).__comma("()");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx[","]=1;
+//>>excludeEnd("ctx");
+$2=$recv($globals.NativeFunction)._constructorNamed_value_("Function",$3);
+$recv($1)._at_put_(aString,$2);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"jsOverride:",{aString:aString})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.IRLatePragmator);
+
+
+
 $core.addClass("JSStream", $globals.Object, ["stream", "omitSemicolon"], "Compiler-IR");
 $core.addMethod(
 $core.method({
@@ -5574,6 +5703,8 @@ return self;
 $globals.JSStream);
 
 
+$core.setTraitComposition([{trait: $globals.TPragmator}], $globals.IRPragmator);
+
 $core.addMethod(
 $core.method({
 selector: "isReferenced",

+ 44 - 1
lang/src/Compiler-IR.st

@@ -480,13 +480,17 @@ acceptDagVisitor: aVisitor
 ! !
 
 IRClosureInstruction subclass: #IRMethod
-	slots: {#theClass. #source. #compiledSource. #selector. #pragmas. #classReferences. #sendIndexes. #internalVariables}
+	slots: {#theClass. #source. #compiledSource. #attachments. #selector. #pragmas. #classReferences. #sendIndexes. #internalVariables}
 	package: 'Compiler-IR'!
 !IRMethod commentStamp!
 I am a method instruction!
 
 !IRMethod methodsFor: 'accessing'!
 
+attachments
+	^ attachments ifNil: [ attachments := #{} ]
+!
+
 classReferences
 	^ classReferences
 !
@@ -1067,6 +1071,42 @@ visitSuperSend: anIRSend
 			enclosedBetween: '(' and: ')' ]
 ! !
 
+IRVisitor subclass: #IRPragmator
+	slots: {#irMethod}
+	package: 'Compiler-IR'!
+
+!IRPragmator methodsFor: 'accessing'!
+
+irMethod
+	^ irMethod
+!
+
+irMethod: anObject
+	irMethod := anObject
+! !
+
+!IRPragmator methodsFor: 'visiting'!
+
+visitIRMethod: anIRMethod
+	self irMethod: anIRMethod.
+	self processPragmas: anIRMethod pragmas.
+	^ anIRMethod
+! !
+
+IRPragmator subclass: #IRLatePragmator
+	slots: {}
+	package: 'Compiler-IR'!
+
+!IRLatePragmator methodsFor: 'as yet unclassified'!
+
+jsOverride: aString
+	self irMethod attachments
+		at: aString
+		put: (NativeFunction
+			constructorNamed: #Function
+			value: 'return this.', irMethod selector asJavaScriptMethodName, '()')
+! !
+
 Object subclass: #JSStream
 	slots: {#stream. #omitSemicolon}
 	package: 'Compiler-IR'!
@@ -1284,6 +1324,9 @@ nextPutVars: aCollection
 		stream nextPutAll: ';'; lf ]
 ! !
 
+IRPragmator setTraitComposition: {TPragmator} asTraitComposition!
+! !
+
 !ASTNode methodsFor: '*Compiler-IR'!
 
 isReferenced

+ 51 - 0
lang/src/Compiler-Tests.js

@@ -2088,6 +2088,57 @@ return self;
 }; }),
 $globals.CodeGeneratorInstallTest);
 
+$core.addMethod(
+$core.method({
+selector: "testNiladicJSOverride",
+protocol: "tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testNiladicJSOverride\x0a\x09receiver := ObjectMock new.\x0a\x09receiver foo: 4.\x0a\x09self while: 'baz <jsOverride: #baz> ^ (foo := foo + 3)' should: [\x0a\x09\x09self assert: receiver baz equals: 7.\x0a\x09\x09self assert: (receiver basicPerform: #baz) equals: 10.\x0a\x09\x09self assert: receiver baz equals: 13.\x0a\x09\x09self assert: receiver foo equals: 13 ]",
+referencedClasses: ["ObjectMock"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["new", "foo:", "while:should:", "assert:equals:", "baz", "basicPerform:", "foo"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$self.receiver=$recv($globals.ObjectMock)._new();
+$recv($self.receiver)._foo_((4));
+$self._while_should_("baz <jsOverride: #baz> ^ (foo := foo + 3)",(function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$1=$recv($self.receiver)._baz();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["baz"]=1;
+//>>excludeEnd("ctx");
+$self._assert_equals_($1,(7));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["assert:equals:"]=1;
+//>>excludeEnd("ctx");
+$self._assert_equals_($recv($self.receiver)._basicPerform_("baz"),(10));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["assert:equals:"]=2;
+//>>excludeEnd("ctx");
+$self._assert_equals_($recv($self.receiver)._baz(),(13));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["assert:equals:"]=3;
+//>>excludeEnd("ctx");
+return $self._assert_equals_($recv($self.receiver)._foo(),(13));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testNiladicJSOverride",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.CodeGeneratorInstallTest);
+
 $core.addMethod(
 $core.method({
 selector: "testPragmaInBlock",

+ 10 - 0
lang/src/Compiler-Tests.st

@@ -562,6 +562,16 @@ testMistypedPragmaJSStatement
 	self shouldntInstall: 'foo < inlineJS: ''return ''foo'''' >'
 !
 
+testNiladicJSOverride
+	receiver := ObjectMock new.
+	receiver foo: 4.
+	self while: 'baz <jsOverride: #baz> ^ (foo := foo + 3)' should: [
+		self assert: receiver baz equals: 7.
+		self assert: (receiver basicPerform: #baz) equals: 10.
+		self assert: receiver baz equals: 13.
+		self assert: receiver foo equals: 13 ]
+!
+
 testPragmaInBlock
 	self shouldntInstall: 'foo ^ [ < fooBar > 4 ] value'
 ! !

+ 62 - 0
lang/src/Kernel-Collections.js

@@ -3347,6 +3347,68 @@ $core.addClass("HashedCollection", $globals.AssociativeCollection, [], "Kernel-C
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.HashedCollection.comment="I am a traditional JavaScript object, or a Smalltalk `Dictionary`.\x0a\x0aUnlike a `Dictionary`, I can only have strings as keys.";
 //>>excludeEnd("ide");
+$core.addMethod(
+$core.method({
+selector: "asJavaScriptSource",
+protocol: "accessing",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "asJavaScriptSource\x0a\x09^ String streamContents: [ :str |\x0a\x09\x09str nextPut: '{'.\x0a\x09\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09\x09str nextPutAll: key asJavaScriptSource; nextPut: ':'; nextPutAll: value asJavaScriptSource; nextPut: ',' ].\x0a\x09\x09str skip: -1; nextPut: '}' ]",
+referencedClasses: ["String"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["streamContents:", "nextPut:", "keysAndValuesDo:", "nextPutAll:", "asJavaScriptSource", "skip:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+return $recv($globals.String)._streamContents_((function(str){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$recv(str)._nextPut_("{");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["nextPut:"]=1;
+//>>excludeEnd("ctx");
+$self._keysAndValuesDo_((function(key,value){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+$1=$recv(key)._asJavaScriptSource();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["asJavaScriptSource"]=1;
+//>>excludeEnd("ctx");
+$recv(str)._nextPutAll_($1);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["nextPutAll:"]=1;
+//>>excludeEnd("ctx");
+$recv(str)._nextPut_(":");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["nextPut:"]=2;
+//>>excludeEnd("ctx");
+$recv(str)._nextPutAll_($recv(value)._asJavaScriptSource());
+return $recv(str)._nextPut_(",");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["nextPut:"]=3;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({key:key,value:value},$ctx2,2)});
+//>>excludeEnd("ctx");
+}));
+$recv(str)._skip_((-1));
+return $recv(str)._nextPut_("}");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({str:str},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"asJavaScriptSource",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.HashedCollection);
+
 $core.addMethod(
 $core.method({
 selector: "at:ifAbsent:",

+ 8 - 0
lang/src/Kernel-Collections.st

@@ -815,6 +815,14 @@ Unlike a `Dictionary`, I can only have strings as keys.!
 
 !HashedCollection methodsFor: 'accessing'!
 
+asJavaScriptSource
+	^ String streamContents: [ :str |
+		str nextPut: '{'.
+		self keysAndValuesDo: [ :key :value |
+			str nextPutAll: key asJavaScriptSource; nextPut: ':'; nextPutAll: value asJavaScriptSource; nextPut: ',' ].
+		str skip: -1; nextPut: '}' ]
+!
+
 at: aKey ifAbsent: aBlock
 	^ (self includesKey: aKey)
 		ifTrue: [ self basicAt: aKey ]

+ 24 - 0
lang/src/Kernel-Methods.js

@@ -56,6 +56,30 @@ return self;
 }; }),
 $globals.BlockClosure);
 
+$core.addMethod(
+$core.method({
+selector: "asJavaScriptSource",
+protocol: "converting",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "asJavaScriptSource\x0a\x09<inlineJS: 'return $self.toString();'>",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [["inlineJS:", ["return $self.toString();"]]],
+messageSends: []
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $self.toString();;
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"asJavaScriptSource",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.BlockClosure);
+
 $core.addMethod(
 $core.method({
 selector: "compiledSource",

+ 4 - 0
lang/src/Kernel-Methods.st

@@ -68,6 +68,10 @@ asCompiledMethod: aString
 	<inlineJS: 'return $core.method({selector:aString, fn:self});'>
 !
 
+asJavaScriptSource
+	<inlineJS: 'return $self.toString();'>
+!
+
 currySelf
 	"Transforms [ :selfarg :x :y | stcode ] block
 	which represents JS function (selfarg, x, y, ...) {jscode}