Browse Source

Local-return-aware `Promise new:`. Fixes #1257.

Herby Vojčík 3 years ago
parent
commit
8394923393
4 changed files with 55 additions and 10 deletions
  1. 24 0
      lang/src/Compiler-Tests.js
  2. 6 0
      lang/src/Compiler-Tests.st
  3. 13 6
      lang/src/Kernel-Promises.js
  4. 12 4
      lang/src/Kernel-Promises.st

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

@@ -1062,6 +1062,30 @@ return self;
 }; }),
 }; }),
 $globals.AbstractCompilerTest);
 $globals.AbstractCompilerTest);
 
 
+$core.addMethod(
+$core.method({
+selector: "testPromiseWithAsyncExecutorAndLocalReturn",
+protocol: "tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseWithAsyncExecutorAndLocalReturn\x0a\x09self\x0a\x09\x09should: 'foo ^ Promise new: [ :m | [ 3 + 4 ] fork. ^ 5 ]'\x0a\x09\x09return: 5",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["should:return:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._should_return_("foo ^ Promise new: [ :m | [ 3 + 4 ] fork. ^ 5 ]",(5));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseWithAsyncExecutorAndLocalReturn",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.AbstractCompilerTest);
+
 $core.addMethod(
 $core.addMethod(
 $core.method({
 $core.method({
 selector: "testReceiverEvaluatedOnceInSpecials",
 selector: "testReceiverEvaluatedOnceInSpecials",

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

@@ -245,6 +245,12 @@ testPragmaJSStatement
 	self should: 'foo < inlineJS: ''return 2+3'' >' return: 5
 	self should: 'foo < inlineJS: ''return 2+3'' >' return: 5
 !
 !
 
 
+testPromiseWithAsyncExecutorAndLocalReturn
+	self
+		should: 'foo ^ Promise new: [ :m | [ 3 + 4 ] fork. ^ 5 ]'
+		return: 5
+!
+
 testReceiverEvaluatedOnceInSpecials
 testReceiverEvaluatedOnceInSpecials
 	self should: 'foo |x| x := 1. ^ {[ x := x+1 ] value ifNil: []. x}' return: {2. 2}.
 	self should: 'foo |x| x := 1. ^ {[ x := x+1 ] value ifNil: []. x}' return: {2. 2}.
 	self should: 'foo |xs| xs := {nil. nil}. ^ {[ xs removeLast ] value ifNotNil: []. xs}' return: {nil. {nil}}.
 	self should: 'foo |xs| xs := {nil. nil}. ^ {[ xs removeLast ] value ifNotNil: []. xs}' return: {nil. {nil}}.

+ 13 - 6
lang/src/Kernel-Promises.js

@@ -145,20 +145,27 @@ selector: "new:",
 protocol: "instance creation",
 protocol: "instance creation",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aBlock"],
 args: ["aBlock"],
-source: "new: aBlock\x0a\x22Returns a Promise that is eventually resolved or rejected.\x0aPass a block that is called with one argument, model.\x0aYou should call model value: ... to resolve the promise\x0aand model signal: ... to reject the promise.\x0aIf error happens during run of the block,\x0apromise is rejected with that error as well.\x22\x0a<inlineJS: 'return new Promise(function (resolve, reject) {\x0a    var model = $globals.PromiseExecution._resolveBlock_rejectBlock_(resolve, reject);\x0a    aBlock._value_(model);\x0a})'>",
+source: "new: aBlock\x0a\x22Returns a Promise that is eventually resolved or rejected.\x0aPass a block that is called with one argument, model.\x0aYou should call model value: ... to resolve the promise\x0aand model signal: ... to reject the promise.\x0aIf error happens during run of the block,\x0apromise is rejected with that error as well.\x22\x0a<inlineJS: '\x0a\x09var localReturn = null,\x0a\x09\x09promise = new Promise(function (resolve, reject) {\x0a\x09\x09    var model = $globals.PromiseExecution._resolveBlock_rejectBlock_(resolve, reject);\x0a\x09\x09    try { aBlock._value_(model); }\x0a\x09\x09\x09catch (ex) {\x0a\x09\x09\x09\x09if (Array.isArray(ex) && ex.length === 1) localReturn = ex;\x0a\x09\x09\x09\x09else reject(ex);\x0a\x09\x09\x09}\x0a\x09\x09});\x0a\x09if (localReturn) throw localReturn; else return promise;\x0a'>",
 referencedClasses: [],
 referencedClasses: [],
 //>>excludeEnd("ide");
 //>>excludeEnd("ide");
-pragmas: [["inlineJS:", ["return new Promise(function (resolve, reject) {\x0a    var model = $globals.PromiseExecution._resolveBlock_rejectBlock_(resolve, reject);\x0a    aBlock._value_(model);\x0a})"]]],
+pragmas: [["inlineJS:", ["\x0a\x09var localReturn = null,\x0a\x09\x09promise = new Promise(function (resolve, reject) {\x0a\x09\x09    var model = $globals.PromiseExecution._resolveBlock_rejectBlock_(resolve, reject);\x0a\x09\x09    try { aBlock._value_(model); }\x0a\x09\x09\x09catch (ex) {\x0a\x09\x09\x09\x09if (Array.isArray(ex) && ex.length === 1) localReturn = ex;\x0a\x09\x09\x09\x09else reject(ex);\x0a\x09\x09\x09}\x0a\x09\x09});\x0a\x09if (localReturn) throw localReturn; else return promise;"]]],
 messageSends: []
 messageSends: []
 }, function ($methodClass){ return function (aBlock){
 }, function ($methodClass){ return function (aBlock){
 var self=this,$self=this;
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
 //>>excludeEnd("ctx");
-return new Promise(function (resolve, reject) {
-    var model = $globals.PromiseExecution._resolveBlock_rejectBlock_(resolve, reject);
-    aBlock._value_(model);
-});
+
+	var localReturn = null,
+		promise = new Promise(function (resolve, reject) {
+		    var model = $globals.PromiseExecution._resolveBlock_rejectBlock_(resolve, reject);
+		    try { aBlock._value_(model); }
+			catch (ex) {
+				if (Array.isArray(ex) && ex.length === 1) localReturn = ex;
+				else reject(ex);
+			}
+		});
+	if (localReturn) throw localReturn; else return promise;;
 return self;
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"new:",{aBlock:aBlock})});
 }, function($ctx1) {$ctx1.fill(self,"new:",{aBlock:aBlock})});

+ 12 - 4
lang/src/Kernel-Promises.st

@@ -39,10 +39,18 @@ You should call model value: ... to resolve the promise
 and model signal: ... to reject the promise.
 and model signal: ... to reject the promise.
 If error happens during run of the block,
 If error happens during run of the block,
 promise is rejected with that error as well."
 promise is rejected with that error as well."
-<inlineJS: 'return new Promise(function (resolve, reject) {
-    var model = $globals.PromiseExecution._resolveBlock_rejectBlock_(resolve, reject);
-    aBlock._value_(model);
-})'>
+<inlineJS: '
+	var localReturn = null,
+		promise = new Promise(function (resolve, reject) {
+		    var model = $globals.PromiseExecution._resolveBlock_rejectBlock_(resolve, reject);
+		    try { aBlock._value_(model); }
+			catch (ex) {
+				if (Array.isArray(ex) && ex.length === 1) localReturn = ex;
+				else reject(ex);
+			}
+		});
+	if (localReturn) throw localReturn; else return promise;
+'>
 !
 !
 
 
 signal: anObject
 signal: anObject