6 Commits c589128375 ... 2651404497

Author SHA1 Message Date
  Herby Vojčík 2651404497 Local-return-aware `Promise new:`. Fixes #1257. 3 years ago
  Herby Vojčík 689851bad8 SUnit fix for promises rejected with non-Errors. 3 years ago
  Herby Vojčík 7a1336ff55 Promise tests. 3 years ago
  Herby Vojčík 1b25dc062b Clean uses of #messageText:. 3 years ago
  Herby Vojčík c65b7dcda0 Stop using tryCatch:. 3 years ago
  Herby Vojčík ead783ab1d PromiseExecution reified. 3 years ago

+ 8 - 0
CHANGELOG

@@ -1,3 +1,11 @@
+?? Oct 2020 - Release 0.29.6
+===================================
+
+* A model in `Promise new: [ :model | ... ]` reified as PromiseExecution.
+
+Commits: https://lolg.it/amber/amber/commits/0.29.6.
+
+
 7 Oct 2020 - Release 0.29.5
 ===================================
 

+ 19 - 0
lang/API-CHANGES.txt

@@ -1,3 +1,22 @@
+0.29.6:
+
+* Add TPromiseModel with unary value / signal passing to 1-arg.
+  * Promise class as well as PromiseExecution use it.
+
++ Error class >>
+  + messageText:
++ PromiseExecution >>
+  + do:
+  + signal:
+  + try:
+  + value:
++ TPromiseModel >>
+  + signal
+  + signal:
+  + value
+  + value:
+
+
 0.29.5:
 
 + (model in `Promise new: [ :model | ]`) >>

+ 18 - 19
lang/src/Compiler-Core.js

@@ -911,11 +911,11 @@ selector: "parse:",
 protocol: "compiling",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aString"],
-source: "parse: aString\x0a\x09| result |\x0a\x09\x0a\x09[ result := self basicParse: aString ] \x0a\x09\x09tryCatch: [ :ex | (self parseError: ex parsing: aString) signal ].\x0a\x09\x09\x0a\x09^ result",
+source: "parse: aString\x0a\x09| result |\x0a\x09\x0a\x09[ result := self basicParse: aString ] \x0a\x09\x09tryIfTrue: [ :err | (err basicAt: 'location') notNil ]\x0a\x09\x09catch: [ :ex | (self parseError: ex parsing: aString) signal ].\x0a\x09\x09\x0a\x09^ result",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["tryCatch:", "basicParse:", "signal", "parseError:parsing:"]
+messageSends: ["tryIfTrue:catch:", "basicParse:", "notNil", "basicAt:", "signal", "parseError:parsing:"]
 }, function ($methodClass){ return function (aString){
 var self=this,$self=this;
 var result;
@@ -931,13 +931,21 @@ return result;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
 //>>excludeEnd("ctx");
-}))._tryCatch_((function(ex){
+}))._tryIfTrue_catch_((function(err){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($recv(err)._basicAt_("location"))._notNil();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({err:err},$ctx1,2)});
+//>>excludeEnd("ctx");
+}),(function(ex){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
 return $recv($self._parseError_parsing_(ex,aString))._signal();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({ex:ex},$ctx1,2)});
+}, function($ctx2) {$ctx2.fillBlock({ex:ex},$ctx1,3)});
 //>>excludeEnd("ctx");
 }));
 return result;
@@ -953,29 +961,23 @@ selector: "parseError:parsing:",
 protocol: "error handling",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["anException", "aString"],
-source: "parseError: anException parsing: aString\x0a\x09(anException basicAt: 'location')\x0a\x09\x09ifNil: [ ^ anException pass ]\x0a\x09\x09ifNotNil: [ :loc |\x0a\x09\x09\x09^ ParseError new \x0a\x09\x09\x09\x09messageText: \x0a\x09\x09\x09\x09\x09'Parse error on line ', loc start line asString,\x0a\x09\x09\x09\x09\x09' column ' , loc start column asString,\x0a\x09\x09\x09\x09\x09' : Unexpected character ', (anException basicAt: 'found');\x0a\x09\x09\x09\x09yourself ]",
+source: "parseError: anException parsing: aString\x0a\x09| loc |\x0a\x09loc := anException basicAt: 'location'.\x0a\x09^ ParseError messageText:\x0a\x09\x09'Parse error on line ', loc start line asString,\x0a\x09\x09' column ' , loc start column asString,\x0a\x09\x09' : Unexpected character ', (anException basicAt: 'found')",
 referencedClasses: ["ParseError"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["ifNil:ifNotNil:", "basicAt:", "pass", "messageText:", "new", ",", "asString", "line", "start", "column", "yourself"]
+messageSends: ["basicAt:", "messageText:", ",", "asString", "line", "start", "column"]
 }, function ($methodClass){ return function (anException,aString){
 var self=this,$self=this;
+var loc;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1,$2;
-$1=[$recv(anException)._basicAt_("location")
+loc=[$recv(anException)._basicAt_("location")
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 ,$ctx1.sendIdx["basicAt:"]=1
 //>>excludeEnd("ctx");
 ][0];
-if($1 == null || $1.a$nil){
-return $recv(anException)._pass();
-} else {
-var loc;
-loc=$1;
-$2=$recv($globals.ParseError)._new();
-$recv($2)._messageText_([$recv([$recv([$recv([$recv("Parse error on line ".__comma([$recv($recv([$recv(loc)._start()
+return $recv($globals.ParseError)._messageText_([$recv([$recv([$recv([$recv("Parse error on line ".__comma([$recv($recv([$recv(loc)._start()
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 ,$ctx1.sendIdx["start"]=1
 //>>excludeEnd("ctx");
@@ -1000,11 +1002,8 @@ $recv($2)._messageText_([$recv([$recv([$recv([$recv("Parse error on line ".__com
 ,$ctx1.sendIdx[","]=1
 //>>excludeEnd("ctx");
 ][0]);
-return $recv($2)._yourself();
-}
-return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"parseError:parsing:",{anException:anException,aString:aString})});
+}, function($ctx1) {$ctx1.fill(self,"parseError:parsing:",{anException:anException,aString:aString,loc:loc})});
 //>>excludeEnd("ctx");
 }; }),
 $globals.Compiler);

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

@@ -233,7 +233,8 @@ parse: aString
 	| result |
 	
 	[ result := self basicParse: aString ] 
-		tryCatch: [ :ex | (self parseError: ex parsing: aString) signal ].
+		tryIfTrue: [ :err | (err basicAt: 'location') notNil ]
+		catch: [ :ex | (self parseError: ex parsing: aString) signal ].
 		
 	^ result
 !
@@ -286,15 +287,12 @@ error: aString
 !
 
 parseError: anException parsing: aString
-	(anException basicAt: 'location')
-		ifNil: [ ^ anException pass ]
-		ifNotNil: [ :loc |
-			^ ParseError new 
-				messageText: 
-					'Parse error on line ', loc start line asString,
-					' column ' , loc start column asString,
-					' : Unexpected character ', (anException basicAt: 'found');
-				yourself ]
+	| loc |
+	loc := anException basicAt: 'location'.
+	^ ParseError messageText:
+		'Parse error on line ', loc start line asString,
+		' column ' , loc start column asString,
+		' : Unexpected character ', (anException basicAt: 'found')
 ! !
 
 !Compiler methodsFor: 'private'!

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

@@ -1062,6 +1062,30 @@ return self;
 }; }),
 $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.method({
 selector: "testReceiverEvaluatedOnceInSpecials",

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

@@ -245,6 +245,12 @@ testPragmaJSStatement
 	self should: 'foo < inlineJS: ''return 2+3'' >' return: 5
 !
 
+testPromiseWithAsyncExecutorAndLocalReturn
+	self
+		should: 'foo ^ Promise new: [ :m | [ 3 + 4 ] fork. ^ 5 ]'
+		return: 5
+!
+
 testReceiverEvaluatedOnceInSpecials
 	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}}.

+ 27 - 1
lang/src/Kernel-Exceptions.js

@@ -113,7 +113,7 @@ selector: "initialize",
 protocol: "initialization",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "initialize\x0a\x09self messageText: 'Errorclass: ', (self class name).",
+source: "initialize\x0a\x09self messageText: 'Errorclass: ', self class name.",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -437,6 +437,32 @@ return "exception";
 }; }),
 $globals.Error.a$cls);
 
+$core.addMethod(
+$core.method({
+selector: "messageText:",
+protocol: "instance creation",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aString"],
+source: "messageText: aString\x0a\x09^ self new\x0a\x09\x09messageText: aString;\x0a\x09\x09yourself",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["messageText:", "new", "yourself"]
+}, function ($methodClass){ return function (aString){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=$self._new();
+$recv($1)._messageText_(aString);
+return $recv($1)._yourself();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"messageText:",{aString:aString})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.Error.a$cls);
+
 $core.addMethod(
 $core.method({
 selector: "signal",

+ 7 - 1
lang/src/Kernel-Exceptions.st

@@ -58,7 +58,7 @@ signalerContextFrom: aContext
 !Error methodsFor: 'initialization'!
 
 initialize
-	self messageText: 'Errorclass: ', (self class name).
+	self messageText: 'Errorclass: ', self class name.
 ! !
 
 !Error methodsFor: 'private'!
@@ -116,6 +116,12 @@ classTag
 
 !Error class methodsFor: 'instance creation'!
 
+messageText: aString
+	^ self new
+		messageText: aString;
+		yourself
+!
+
 signal
 	^ self new signal
 !

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

@@ -458,7 +458,7 @@ selector: "tryCatch:",
 protocol: "error handling",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aBlock"],
-source: "tryCatch: aBlock\x0a\x09<inlineJS: '\x0a\x09\x09try {\x0a\x09\x09\x09return $self._value();\x0a\x09\x09} catch(error) {\x0a\x09\x09\x09// pass non-local returns undetected\x0a\x09\x09\x09if (Array.isArray(error) && error.length === 1) throw error;\x0a\x09\x09\x09return aBlock._value_(error);\x0a\x09\x09}\x0a\x09'>",
+source: "tryCatch: aBlock\x0a\x09\x22Too low-level. Try more high-level alternatives like:\x0a\x09\x09[ ... ] on: Error do: [ ... ]\x0a\x09\x09Smalltalk try: [ ... ] ifTrue: [ ... ] catch: [ ... ]\x0a\x09\x09[ ... ] tryIfTrue: [ ... ] catch: [ ... ]\x22\x0a\x09<inlineJS: '\x0a\x09\x09try {\x0a\x09\x09\x09return $self._value();\x0a\x09\x09} catch(error) {\x0a\x09\x09\x09// pass non-local returns undetected\x0a\x09\x09\x09if (Array.isArray(error) && error.length === 1) throw error;\x0a\x09\x09\x09return aBlock._value_(error);\x0a\x09\x09}\x0a\x09'>",
 referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [["inlineJS:", ["\x0a\x09\x09try {\x0a\x09\x09\x09return $self._value();\x0a\x09\x09} catch(error) {\x0a\x09\x09\x09// pass non-local returns undetected\x0a\x09\x09\x09if (Array.isArray(error) && error.length === 1) throw error;\x0a\x09\x09\x09return aBlock._value_(error);\x0a\x09\x09}\x0a\x09"]]],

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

@@ -107,6 +107,10 @@ on: anErrorClass do: aBlock
 !
 
 tryCatch: aBlock
+	"Too low-level. Try more high-level alternatives like:
+		[ ... ] on: Error do: [ ... ]
+		Smalltalk try: [ ... ] ifTrue: [ ... ] catch: [ ... ]
+		[ ... ] tryIfTrue: [ ... ] catch: [ ... ]"
 	<inlineJS: '
 		try {
 			return $self._value();

+ 273 - 14
lang/src/Kernel-Promises.js

@@ -145,28 +145,27 @@ selector: "new:",
 protocol: "instance creation",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 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 = {\x0a\x09\x09value: resolve,\x0a\x09\x09signal: reject,\x0a\x09\x09do: function (aBlock) { resolve(this.try(aBlock)); },\x0a\x09\x09try: function (aBlock) {\x0a\x09\x09\x09try { return aBlock._value(); }\x0a\x09\x09\x09catch (e) { reject(e); }\x0a\x09\x09}\x0a\x09};\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: [],
 //>>excludeEnd("ide");
-pragmas: [["inlineJS:", ["return new Promise(function (resolve, reject) {\x0a    var model = {\x0a\x09\x09value: resolve,\x0a\x09\x09signal: reject,\x0a\x09\x09do: function (aBlock) { resolve(this.try(aBlock)); },\x0a\x09\x09try: function (aBlock) {\x0a\x09\x09\x09try { return aBlock._value(); }\x0a\x09\x09\x09catch (e) { reject(e); }\x0a\x09\x09}\x0a\x09};\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: []
 }, function ($methodClass){ return function (aBlock){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-return new Promise(function (resolve, reject) {
-    var model = {
-		value: resolve,
-		signal: reject,
-		do: function (aBlock) { resolve(this.try(aBlock)); },
-		try: function (aBlock) {
-			try { return aBlock._value(); }
-			catch (e) { reject(e); }
-		}
-	};
-    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;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"new:",{aBlock:aBlock})});
@@ -223,6 +222,264 @@ return self;
 $globals.Promise.a$cls);
 
 
+$core.addClass("PromiseExecution", $globals.Object, "Kernel-Promises");
+$core.setSlots($globals.PromiseExecution, ["resolveBlock", "rejectBlock"]);
+$core.addMethod(
+$core.method({
+selector: "do:",
+protocol: "evaluating",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aBlock"],
+source: "do: aBlock\x0a\x09self value: (self try: aBlock)",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["value:", "try:"]
+}, function ($methodClass){ return function (aBlock){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._value_($self._try_(aBlock));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"do:",{aBlock:aBlock})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseExecution);
+
+$core.addMethod(
+$core.method({
+selector: "resolveBlock:rejectBlock:",
+protocol: "accessing",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aBlock", "anotherBlock"],
+source: "resolveBlock: aBlock rejectBlock: anotherBlock\x0a\x09resolveBlock := aBlock.\x0a\x09rejectBlock := anotherBlock",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: []
+}, function ($methodClass){ return function (aBlock,anotherBlock){
+var self=this,$self=this;
+$self.resolveBlock=aBlock;
+$self.rejectBlock=anotherBlock;
+return self;
+
+}; }),
+$globals.PromiseExecution);
+
+$core.addMethod(
+$core.method({
+selector: "signal:",
+protocol: "settling",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anErrorObject"],
+source: "signal: anErrorObject\x0a\x09rejectBlock value: anErrorObject",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["value:"]
+}, function ($methodClass){ return function (anErrorObject){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv($self.rejectBlock)._value_(anErrorObject);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"signal:",{anErrorObject:anErrorObject})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseExecution);
+
+$core.addMethod(
+$core.method({
+selector: "try:",
+protocol: "evaluating",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aBlock"],
+source: "try: aBlock\x0a\x09<inlineJS: '\x0a\x09\x09try {\x0a\x09\x09\x09return aBlock._value();\x0a\x09\x09} catch(error) {\x0a\x09\x09\x09// pass non-local returns undetected\x0a\x09\x09\x09if (Array.isArray(error) && error.length === 1) throw error;\x0a\x09\x09\x09self._signal_(error);\x0a\x09\x09}\x0a\x09'>",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [["inlineJS:", ["\x0a\x09\x09try {\x0a\x09\x09\x09return aBlock._value();\x0a\x09\x09} catch(error) {\x0a\x09\x09\x09// pass non-local returns undetected\x0a\x09\x09\x09if (Array.isArray(error) && error.length === 1) throw error;\x0a\x09\x09\x09self._signal_(error);\x0a\x09\x09}\x0a\x09"]]],
+messageSends: []
+}, function ($methodClass){ return function (aBlock){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+
+		try {
+			return aBlock._value();
+		} catch(error) {
+			// pass non-local returns undetected
+			if (Array.isArray(error) && error.length === 1) throw error;
+			self._signal_(error);
+		}
+	;
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"try:",{aBlock:aBlock})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseExecution);
+
+$core.addMethod(
+$core.method({
+selector: "value:",
+protocol: "settling",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject"],
+source: "value: anObject\x0a\x09resolveBlock value: anObject",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["value:"]
+}, function ($methodClass){ return function (anObject){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv($self.resolveBlock)._value_(anObject);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"value:",{anObject:anObject})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseExecution);
+
+
+$core.addMethod(
+$core.method({
+selector: "resolveBlock:rejectBlock:",
+protocol: "instance creation",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aBlock", "anotherBlock"],
+source: "resolveBlock: aBlock rejectBlock: anotherBlock\x0a\x09^ super new\x0a\x09\x09resolveBlock: aBlock rejectBlock: anotherBlock;\x0a\x09\x09yourself",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["resolveBlock:rejectBlock:", "new", "yourself"]
+}, function ($methodClass){ return function (aBlock,anotherBlock){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=[(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true,
+//>>excludeEnd("ctx");
+($methodClass.superclass||$boot.nilAsClass).fn.prototype._new.call($self))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.supercall = false
+//>>excludeEnd("ctx");
+][0];
+$recv($1)._resolveBlock_rejectBlock_(aBlock,anotherBlock);
+return $recv($1)._yourself();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"resolveBlock:rejectBlock:",{aBlock:aBlock,anotherBlock:anotherBlock})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseExecution.a$cls);
+
+
+$core.addTrait("TPromiseModel", "Kernel-Promises");
+$core.addMethod(
+$core.method({
+selector: "signal",
+protocol: "settling",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "signal\x0a\x09^ self signal: Error new",
+referencedClasses: ["Error"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["signal:", "new"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $self._signal_($recv($globals.Error)._new());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"signal",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.TPromiseModel);
+
+$core.addMethod(
+$core.method({
+selector: "signal:",
+protocol: "settling",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anErrorObject"],
+source: "signal: anErrorObject\x0a\x09self subclassResponsibility",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["subclassResponsibility"]
+}, function ($methodClass){ return function (anErrorObject){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._subclassResponsibility();
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"signal:",{anErrorObject:anErrorObject})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.TPromiseModel);
+
+$core.addMethod(
+$core.method({
+selector: "value",
+protocol: "settling",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "value\x0a\x09^ self value: nil",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["value:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $self._value_(nil);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"value",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.TPromiseModel);
+
+$core.addMethod(
+$core.method({
+selector: "value:",
+protocol: "settling",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject"],
+source: "value: anObject\x0a\x09self subclassResponsibility",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["subclassResponsibility"]
+}, function ($methodClass){ return function (anObject){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._subclassResponsibility();
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"value:",{anObject:anObject})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.TPromiseModel);
+
+
 $core.addTrait("TThenable", "Kernel-Promises");
 $core.addMethod(
 $core.method({
@@ -425,5 +682,7 @@ return $recv($recv($self._then_(aBlockOrArray))._on_do_(aClass,aBlock))._catch_(
 $globals.TThenable);
 
 $core.setTraitComposition([{trait: $globals.TThenable}], $globals.Promise);
+$core.setTraitComposition([{trait: $globals.TPromiseModel}], $globals.Promise.a$cls);
+$core.setTraitComposition([{trait: $globals.TPromiseModel}], $globals.PromiseExecution);
 
 });

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

@@ -39,18 +39,18 @@ You should call model value: ... to resolve the promise
 and model signal: ... to reject the promise.
 If error happens during run of the block,
 promise is rejected with that error as well."
-<inlineJS: 'return new Promise(function (resolve, reject) {
-    var model = {
-		value: resolve,
-		signal: reject,
-		do: function (aBlock) { resolve(this.try(aBlock)); },
-		try: function (aBlock) {
-			try { return aBlock._value(); }
-			catch (e) { reject(e); }
-		}
-	};
-    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
@@ -63,6 +63,74 @@ value: anObject
 <inlineJS: 'return $recv(anObject)._in_(function (x) {return Promise.resolve(x)})'>
 ! !
 
+Object subclass: #PromiseExecution
+	slots: {#resolveBlock. #rejectBlock}
+	package: 'Kernel-Promises'!
+
+!PromiseExecution methodsFor: 'accessing'!
+
+resolveBlock: aBlock rejectBlock: anotherBlock
+	resolveBlock := aBlock.
+	rejectBlock := anotherBlock
+! !
+
+!PromiseExecution methodsFor: 'evaluating'!
+
+do: aBlock
+	self value: (self try: aBlock)
+!
+
+try: aBlock
+	<inlineJS: '
+		try {
+			return aBlock._value();
+		} catch(error) {
+			// pass non-local returns undetected
+			if (Array.isArray(error) && error.length === 1) throw error;
+			self._signal_(error);
+		}
+	'>
+! !
+
+!PromiseExecution methodsFor: 'settling'!
+
+signal: anErrorObject
+	rejectBlock value: anErrorObject
+!
+
+value: anObject
+	resolveBlock value: anObject
+! !
+
+!PromiseExecution class methodsFor: 'instance creation'!
+
+resolveBlock: aBlock rejectBlock: anotherBlock
+	^ super new
+		resolveBlock: aBlock rejectBlock: anotherBlock;
+		yourself
+! !
+
+Trait named: #TPromiseModel
+	package: 'Kernel-Promises'!
+
+!TPromiseModel methodsFor: 'settling'!
+
+signal
+	^ self signal: Error new
+!
+
+signal: anErrorObject
+	self subclassResponsibility
+!
+
+value
+	^ self value: nil
+!
+
+value: anObject
+	self subclassResponsibility
+! !
+
 Trait named: #TThenable
 	package: 'Kernel-Promises'!
 
@@ -129,5 +197,7 @@ isThenable
 ! !
 
 Promise setTraitComposition: {TThenable} asTraitComposition!
+Promise class setTraitComposition: {TPromiseModel} asTraitComposition!
+PromiseExecution setTraitComposition: {TPromiseModel} asTraitComposition!
 ! !
 

+ 783 - 0
lang/src/Kernel-Tests.js

@@ -15671,6 +15671,789 @@ $globals.PointTest);
 
 
 
+$core.addClass("PromiseTest", $globals.TestCase, "Kernel-Tests");
+$core.addMethod(
+$core.method({
+selector: "testPromiseExecutorAsyncNegativeDo",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseExecutorAsyncNegativeDo\x0a\x09self timeout: 40.\x0a\x09^ (Promise new: [ :m | [ m do: [ self error: 'Intentional' ] ] fork ])\x0a\x09\x09catch: [ :error | self assert: error messageText equals: 'Intentional' ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "catch:", "new:", "fork", "do:", "error:", "assert:equals:", "messageText"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((40));
+return $recv($recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(m)._do_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx4) {
+//>>excludeEnd("ctx");
+return $self._error_("Intentional");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,3)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}))._fork();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._catch_((function(error){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_($recv(error)._messageText(),"Intentional");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({error:error},$ctx1,4)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseExecutorAsyncNegativeDo",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseExecutorAsyncPositiveDo",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseExecutorAsyncPositiveDo\x0a\x09self timeout: 40.\x0a\x09^ (Promise new: [ :m | [ m do: [ 3 ] ] fork ])\x0a\x09\x09then: [ :result | self assert: result equals: 3 ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "then:", "new:", "fork", "do:", "assert:equals:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((40));
+return $recv($recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(m)._do_((function(){
+return (3);
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}))._fork();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(result,(3));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,4)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseExecutorAsyncPositiveDo",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseExecutorAsyncPositiveTry",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseExecutorAsyncPositiveTry\x0a\x09self timeout: 200.\x0a\x09^ (Promise any: {\x0a\x09\x09(Promise new: [ :m | [ m try: [ 3 ] ] fork ])\x0a\x09\x09\x09then: [ :result | self assert: result equals: 3 ].\x0a\x09\x09Promise new: [ :m | [ m value: #timeout ] valueWithTimeout: 20 ]\x0a\x09}) then: [ :result | self assert: result equals: #timeout ].",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "then:", "any:", "new:", "fork", "try:", "assert:equals:", "valueWithTimeout:", "value:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((200));
+return [$recv($recv($globals.Promise)._any_([$recv([$recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(m)._try_((function(){
+return (3);
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}))._fork();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["new:"]=1
+//>>excludeEnd("ctx");
+][0])._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return [$self._assert_equals_(result,(3))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx2.sendIdx["assert:equals:"]=1
+//>>excludeEnd("ctx");
+][0];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,4)});
+//>>excludeEnd("ctx");
+})),$recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(m)._value_("timeout");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,6)});
+//>>excludeEnd("ctx");
+}))._valueWithTimeout_((20));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,5)});
+//>>excludeEnd("ctx");
+}))]))._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(result,"timeout");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,7)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["then:"]=1
+//>>excludeEnd("ctx");
+][0];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseExecutorAsyncPositiveTry",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseExecutorNegativeDo",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseExecutorNegativeDo\x0a\x09self timeout: 40.\x0a\x09^ (Promise new: [ :m | [ m do: [ self error: 'Intentional' ] ] fork ])\x0a\x09\x09catch: [ :error | self assert: error messageText equals: 'Intentional' ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "catch:", "new:", "fork", "do:", "error:", "assert:equals:", "messageText"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((40));
+return $recv($recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(m)._do_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx4) {
+//>>excludeEnd("ctx");
+return $self._error_("Intentional");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,3)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}))._fork();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._catch_((function(error){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_($recv(error)._messageText(),"Intentional");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({error:error},$ctx1,4)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseExecutorNegativeDo",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseExecutorNegativeTry",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseExecutorNegativeTry\x0a\x09self timeout: 20.\x0a\x09^ (Promise new: [ :m | m try: [ self error: 'Intentional' ] ])\x0a\x09\x09catch: [ :error | self assert: error messageText equals: 'Intentional' ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "catch:", "new:", "try:", "error:", "assert:equals:", "messageText"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((20));
+return $recv($recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(m)._try_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $self._error_("Intentional");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._catch_((function(error){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_($recv(error)._messageText(),"Intentional");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({error:error},$ctx1,3)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseExecutorNegativeTry",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseExecutorPositiveDo",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseExecutorPositiveDo\x0a\x09self timeout: 20.\x0a\x09^ (Promise new: [ :m | m do: [ 3 ] ])\x0a\x09\x09then: [ :result | self assert: result equals: 3 ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "then:", "new:", "do:", "assert:equals:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((20));
+return $recv($recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(m)._do_((function(){
+return (3);
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(result,(3));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,3)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseExecutorPositiveDo",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseExecutorPositiveTry",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseExecutorPositiveTry\x0a\x09self timeout: 200.\x0a\x09^ (Promise any: {\x0a\x09\x09(Promise new: [ :m | m try: [ 3 ] ])\x0a\x09\x09\x09then: [ :result | self assert: result equals: 3 ].\x0a\x09\x09Promise new: [ :m | [ m value: #timeout ] valueWithTimeout: 20 ]\x0a\x09}) then: [ :result | self assert: result equals: #timeout ].",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "then:", "any:", "new:", "try:", "assert:equals:", "valueWithTimeout:", "value:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((200));
+return [$recv($recv($globals.Promise)._any_([$recv([$recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(m)._try_((function(){
+return (3);
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["new:"]=1
+//>>excludeEnd("ctx");
+][0])._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return [$self._assert_equals_(result,(3))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx2.sendIdx["assert:equals:"]=1
+//>>excludeEnd("ctx");
+][0];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,3)});
+//>>excludeEnd("ctx");
+})),$recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(m)._value_("timeout");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,5)});
+//>>excludeEnd("ctx");
+}))._valueWithTimeout_((20));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,4)});
+//>>excludeEnd("ctx");
+}))]))._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(result,"timeout");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,6)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["then:"]=1
+//>>excludeEnd("ctx");
+][0];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseExecutorPositiveTry",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseNew",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseNew\x0a\x09self timeout: 20.\x0a\x09^ Promise new\x0a\x09\x09then: [ :result | self assert: result equals: nil ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "then:", "new", "assert:equals:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((20));
+return $recv($recv($globals.Promise)._new())._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(result,nil);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseNew",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseWithAsyncPassingRejectingExecutor",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseWithAsyncPassingRejectingExecutor\x0a\x09self timeout: 60.\x0a\x09^ (Promise new: [ :m | [\x0a\x09\x09| passPromise |\x0a\x09\x09passPromise := Promise new: [ :m2 | [ m2 signal: 4 ] fork ].\x0a\x09\x09m value: passPromise\x0a\x09] fork ]) catch: [ :err | self assert: err equals: 4 ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "catch:", "new:", "fork", "signal:", "value:", "assert:equals:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((60));
+return $recv([$recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return [$recv((function(){
+var passPromise;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+passPromise=$recv($globals.Promise)._new_((function(m2){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx4) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx5) {
+//>>excludeEnd("ctx");
+return $recv(m2)._signal_((4));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx5) {$ctx5.fillBlock({},$ctx4,4)});
+//>>excludeEnd("ctx");
+}))._fork();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx4) {$ctx4.fillBlock({m2:m2},$ctx3,3)});
+//>>excludeEnd("ctx");
+}));
+return $recv(m)._value_(passPromise);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({passPromise:passPromise},$ctx2,2)});
+//>>excludeEnd("ctx");
+}))._fork()
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx2.sendIdx["fork"]=1
+//>>excludeEnd("ctx");
+][0];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["new:"]=1
+//>>excludeEnd("ctx");
+][0])._catch_((function(err){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(err,(4));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({err:err},$ctx1,5)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseWithAsyncPassingRejectingExecutor",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseWithAsyncPassingResolvingExecutor",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseWithAsyncPassingResolvingExecutor\x0a\x09self timeout: 60.\x0a\x09^ (Promise new: [ :m | [\x0a\x09\x09| passPromise |\x0a\x09\x09passPromise := Promise new: [ :m2 | [ m2 value: 3 ] fork ].\x0a\x09\x09m value: passPromise\x0a\x09] fork ]) then: [ :result | self assert: result equals: 3 ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "then:", "new:", "fork", "value:", "assert:equals:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((60));
+return $recv([$recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return [$recv((function(){
+var passPromise;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+passPromise=$recv($globals.Promise)._new_((function(m2){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx4) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx5) {
+//>>excludeEnd("ctx");
+return [$recv(m2)._value_((3))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx5.sendIdx["value:"]=1
+//>>excludeEnd("ctx");
+][0];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx5) {$ctx5.fillBlock({},$ctx4,4)});
+//>>excludeEnd("ctx");
+}))._fork();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx4) {$ctx4.fillBlock({m2:m2},$ctx3,3)});
+//>>excludeEnd("ctx");
+}));
+return $recv(m)._value_(passPromise);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({passPromise:passPromise},$ctx2,2)});
+//>>excludeEnd("ctx");
+}))._fork()
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx2.sendIdx["fork"]=1
+//>>excludeEnd("ctx");
+][0];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["new:"]=1
+//>>excludeEnd("ctx");
+][0])._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(result,(3));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,5)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseWithAsyncPassingResolvingExecutor",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseWithAsyncRejectingExecutor",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseWithAsyncRejectingExecutor\x0a\x09self timeout: 40.\x0a\x09^ (Promise new: [ :m | [ m signal: 4 ] fork ])\x0a\x09\x09catch: [ :err | self assert: err equals: 4 ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "catch:", "new:", "fork", "signal:", "assert:equals:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((40));
+return $recv($recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(m)._signal_((4));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}))._fork();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._catch_((function(err){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(err,(4));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({err:err},$ctx1,3)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseWithAsyncRejectingExecutor",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseWithAsyncResolvingExecutor",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseWithAsyncResolvingExecutor\x0a\x09self timeout: 40.\x0a\x09^ (Promise new: [ :m | [ m value: 3 ] fork ])\x0a\x09\x09then: [ :result | self assert: result equals: 3 ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "then:", "new:", "fork", "value:", "assert:equals:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((40));
+return $recv($recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(m)._value_((3));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}))._fork();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(result,(3));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,3)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseWithAsyncResolvingExecutor",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseWithRejectingExecutor",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseWithRejectingExecutor\x0a\x09self timeout: 20.\x0a\x09^ (Promise new: [ :m | m signal: 4 ])\x0a\x09\x09catch: [ :err | self assert: err equals: 4 ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "catch:", "new:", "signal:", "assert:equals:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((20));
+return $recv($recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(m)._signal_((4));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._catch_((function(err){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(err,(4));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({err:err},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseWithRejectingExecutor",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+$core.addMethod(
+$core.method({
+selector: "testPromiseWithResolvingExecutor",
+protocol: " tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testPromiseWithResolvingExecutor\x0a\x09self timeout: 20.\x0a\x09^ (Promise new: [ :m | m value: 3 ])\x0a\x09\x09then: [ :result | self assert: result equals: 3 ]",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "then:", "new:", "value:", "assert:equals:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._timeout_((20));
+return $recv($recv($globals.Promise)._new_((function(m){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(m)._value_((3));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({m:m},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._then_((function(result){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._assert_equals_(result,(3));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({result:result},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testPromiseWithResolvingExecutor",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.PromiseTest);
+
+
+
 $core.addClass("QueueTest", $globals.TestCase, "Kernel-Tests");
 $core.addMethod(
 $core.method({

+ 102 - 0
lang/src/Kernel-Tests.st

@@ -2780,6 +2780,108 @@ testTranslateBy
 	self assert: (3@3 translateBy: 3 negated @0) equals: 0@3.
 ! !
 
+TestCase subclass: #PromiseTest
+	slots: {}
+	package: 'Kernel-Tests'!
+
+!PromiseTest methodsFor: ' tests'!
+
+testPromiseExecutorAsyncNegativeDo
+	self timeout: 40.
+	^ (Promise new: [ :m | [ m do: [ self error: 'Intentional' ] ] fork ])
+		catch: [ :error | self assert: error messageText equals: 'Intentional' ]
+!
+
+testPromiseExecutorAsyncPositiveDo
+	self timeout: 40.
+	^ (Promise new: [ :m | [ m do: [ 3 ] ] fork ])
+		then: [ :result | self assert: result equals: 3 ]
+!
+
+testPromiseExecutorAsyncPositiveTry
+	self timeout: 200.
+	^ (Promise any: {
+		(Promise new: [ :m | [ m try: [ 3 ] ] fork ])
+			then: [ :result | self assert: result equals: 3 ].
+		Promise new: [ :m | [ m value: #timeout ] valueWithTimeout: 20 ]
+	}) then: [ :result | self assert: result equals: #timeout ].
+!
+
+testPromiseExecutorNegativeDo
+	self timeout: 40.
+	^ (Promise new: [ :m | [ m do: [ self error: 'Intentional' ] ] fork ])
+		catch: [ :error | self assert: error messageText equals: 'Intentional' ]
+!
+
+testPromiseExecutorNegativeTry
+	self timeout: 20.
+	^ (Promise new: [ :m | m try: [ self error: 'Intentional' ] ])
+		catch: [ :error | self assert: error messageText equals: 'Intentional' ]
+!
+
+testPromiseExecutorPositiveDo
+	self timeout: 20.
+	^ (Promise new: [ :m | m do: [ 3 ] ])
+		then: [ :result | self assert: result equals: 3 ]
+!
+
+testPromiseExecutorPositiveTry
+	self timeout: 200.
+	^ (Promise any: {
+		(Promise new: [ :m | m try: [ 3 ] ])
+			then: [ :result | self assert: result equals: 3 ].
+		Promise new: [ :m | [ m value: #timeout ] valueWithTimeout: 20 ]
+	}) then: [ :result | self assert: result equals: #timeout ].
+!
+
+testPromiseNew
+	self timeout: 20.
+	^ Promise new
+		then: [ :result | self assert: result equals: nil ]
+!
+
+testPromiseWithAsyncPassingRejectingExecutor
+	self timeout: 60.
+	^ (Promise new: [ :m | [
+		| passPromise |
+		passPromise := Promise new: [ :m2 | [ m2 signal: 4 ] fork ].
+		m value: passPromise
+	] fork ]) catch: [ :err | self assert: err equals: 4 ]
+!
+
+testPromiseWithAsyncPassingResolvingExecutor
+	self timeout: 60.
+	^ (Promise new: [ :m | [
+		| passPromise |
+		passPromise := Promise new: [ :m2 | [ m2 value: 3 ] fork ].
+		m value: passPromise
+	] fork ]) then: [ :result | self assert: result equals: 3 ]
+!
+
+testPromiseWithAsyncRejectingExecutor
+	self timeout: 40.
+	^ (Promise new: [ :m | [ m signal: 4 ] fork ])
+		catch: [ :err | self assert: err equals: 4 ]
+!
+
+testPromiseWithAsyncResolvingExecutor
+	self timeout: 40.
+	^ (Promise new: [ :m | [ m value: 3 ] fork ])
+		then: [ :result | self assert: result equals: 3 ]
+!
+
+testPromiseWithRejectingExecutor
+	self timeout: 20.
+	^ (Promise new: [ :m | m signal: 4 ])
+		catch: [ :err | self assert: err equals: 4 ]
+!
+
+testPromiseWithResolvingExecutor
+	self timeout: 20.
+	^ (Promise new: [ :m | m value: 3 ])
+		then: [ :result | self assert: result equals: 3 ]
+! !
+
 TestCase subclass: #QueueTest
 	slots: {}
 	package: 'Kernel-Tests'!

+ 3 - 37
lang/src/Platform-ImportExport.js

@@ -3130,30 +3130,27 @@ selector: "commit:",
 protocol: "committing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aPackage"],
-source: "commit: aPackage\x0a\x09self \x0a\x09\x09commit: aPackage\x0a\x09\x09onSuccess: []\x0a\x09\x09onError: [ :error |\x0a\x09\x09\x09PackageCommitError new\x0a\x09\x09\x09\x09messageText: 'Commiting failed with reason: \x22' , (error responseText) , '\x22';\x0a\x09\x09\x09\x09signal ]",
+source: "commit: aPackage\x0a\x09self \x0a\x09\x09commit: aPackage\x0a\x09\x09onSuccess: []\x0a\x09\x09onError: [ :error |\x0a\x09\x09\x09PackageCommitError\x0a\x09\x09\x09\x09signal: 'Commiting failed with reason: \x22' , error responseText , '\x22' ]",
 referencedClasses: ["PackageCommitError"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["commit:onSuccess:onError:", "messageText:", "new", ",", "responseText", "signal"]
+messageSends: ["commit:onSuccess:onError:", "signal:", ",", "responseText"]
 }, function ($methodClass){ return function (aPackage){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1;
 $self._commit_onSuccess_onError_(aPackage,(function(){
 
 }),(function(error){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
-$1=$recv($globals.PackageCommitError)._new();
-$recv($1)._messageText_([$recv("Commiting failed with reason: \x22".__comma($recv(error)._responseText())).__comma("\x22")
+return $recv($globals.PackageCommitError)._signal_([$recv("Commiting failed with reason: \x22".__comma($recv(error)._responseText())).__comma("\x22")
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 ,$ctx2.sendIdx[","]=1
 //>>excludeEnd("ctx");
 ][0]);
-return $recv($1)._signal();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx2) {$ctx2.fillBlock({error:error},$ctx1,2)});
 //>>excludeEnd("ctx");
@@ -3420,37 +3417,6 @@ return self;
 }; }),
 $globals.PackageHandler);
 
-$core.addMethod(
-$core.method({
-selector: "onCommitError:",
-protocol: "error handling",
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: ["anError"],
-source: "onCommitError: anError\x0a\x09PackageCommitError new\x0a\x09\x09messageText: 'Commiting failed with reason: \x22' , (anError responseText) , '\x22';\x0a\x09\x09signal",
-referencedClasses: ["PackageCommitError"],
-//>>excludeEnd("ide");
-pragmas: [],
-messageSends: ["messageText:", "new", ",", "responseText", "signal"]
-}, function ($methodClass){ return function (anError){
-var self=this,$self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-var $1;
-$1=$recv($globals.PackageCommitError)._new();
-$recv($1)._messageText_([$recv("Commiting failed with reason: \x22".__comma($recv(anError)._responseText())).__comma("\x22")
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-,$ctx1.sendIdx[","]=1
-//>>excludeEnd("ctx");
-][0]);
-$recv($1)._signal();
-return self;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"onCommitError:",{anError:anError})});
-//>>excludeEnd("ctx");
-}; }),
-$globals.PackageHandler);
-
 $core.addMethod(
 $core.method({
 selector: "setPath:forPackage:",

+ 2 - 11
lang/src/Platform-ImportExport.st

@@ -794,9 +794,8 @@ commit: aPackage
 		commit: aPackage
 		onSuccess: []
 		onError: [ :error |
-			PackageCommitError new
-				messageText: 'Commiting failed with reason: "' , (error responseText) , '"';
-				signal ]
+			PackageCommitError
+				signal: 'Commiting failed with reason: "' , error responseText , '"' ]
 !
 
 commit: aPackage onSuccess: aBlock onError: anotherBlock
@@ -826,14 +825,6 @@ commitStFileFor: aPackage onSuccess: aBlock onError: anotherBlock
 		onError: anotherBlock
 ! !
 
-!PackageHandler methodsFor: 'error handling'!
-
-onCommitError: anError
-	PackageCommitError new
-		messageText: 'Commiting failed with reason: "' , (anError responseText) , '"';
-		signal
-! !
-
 !PackageHandler methodsFor: 'factory'!
 
 chunkExporter

+ 7 - 10
lang/src/SUnit.js

@@ -736,20 +736,17 @@ selector: "signalFailure:",
 protocol: "private",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aString"],
-source: "signalFailure: aString\x0a\x09TestFailure new\x0a\x09\x09messageText: aString;\x0a\x09\x09signal",
+source: "signalFailure: aString\x0a\x09TestFailure signal: aString",
 referencedClasses: ["TestFailure"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["messageText:", "new", "signal"]
+messageSends: ["signal:"]
 }, function ($methodClass){ return function (aString){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1;
-$1=$recv($globals.TestFailure)._new();
-$recv($1)._messageText_(aString);
-$recv($1)._signal();
+$recv($globals.TestFailure)._signal_(aString);
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"signalFailure:",{aString:aString})});
@@ -1053,11 +1050,11 @@ selector: "execute:",
 protocol: "running",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aBlock"],
-source: "execute: aBlock\x0a\x09| failed result |\x0a\x09\x0a\x09testCase context: self.\x0a\x09[\x0a\x09\x09failed := true.\x0a\x09\x09result := aBlock value.\x0a\x09\x09testCase isAsync ifFalse: [\x0a\x09\x09\x09testCase assert: result isThenable not description: testCase asString, ' returned promise without sending #timeout:' ].\x0a\x09\x09failed := false\x0a\x09]\x0a\x09\x09ensure: [\x0a\x09\x09\x09\x22testCase context: nil.\x22\x0a\x09\x09\x09\x0a\x09\x09\x09(failed and: [ testCase isAsync ]) ifTrue: [ testCase finished ].\x0a\x09\x09\x09testCase isAsync\x0a\x09\x09\x09\x09ifFalse: [ testCase tearDown ]\x0a\x09\x09\x09\x09ifTrue: [ result isThenable ifTrue: [\x0a\x09\x09\x09\x09\x09result\x0a\x09\x09\x09\x09\x09\x09then: [ testCase isAsync ifTrue: [ self execute: [ testCase finished ] ] ]\x0a\x09\x09\x09\x09\x09\x09catch: [ :error | testCase isAsync ifTrue: [ self execute: [ error signal ] ] ] ] ] ]",
-referencedClasses: [],
+source: "execute: aBlock\x0a\x09| failed result |\x0a\x09\x0a\x09testCase context: self.\x0a\x09[\x0a\x09\x09failed := true.\x0a\x09\x09result := aBlock value.\x0a\x09\x09testCase isAsync ifFalse: [\x0a\x09\x09\x09testCase assert: result isThenable not description: testCase asString, ' returned promise without sending #timeout:' ].\x0a\x09\x09failed := false\x0a\x09]\x0a\x09\x09ensure: [\x0a\x09\x09\x09\x22testCase context: nil.\x22\x0a\x09\x09\x09\x0a\x09\x09\x09(failed and: [ testCase isAsync ]) ifTrue: [ testCase finished ].\x0a\x09\x09\x09testCase isAsync\x0a\x09\x09\x09\x09ifFalse: [ testCase tearDown ]\x0a\x09\x09\x09\x09ifTrue: [ result isThenable ifTrue: [\x0a\x09\x09\x09\x09\x09result\x0a\x09\x09\x09\x09\x09\x09then: [ testCase isAsync ifTrue: [ self execute: [ testCase finished ] ] ]\x0a\x09\x09\x09\x09\x09\x09catch: [ :error | testCase isAsync ifTrue: [ self execute: [ (Smalltalk asSmalltalkException: error) pass ] ] ] ] ] ]",
+referencedClasses: ["Smalltalk"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["context:", "ensure:", "value", "ifFalse:", "isAsync", "assert:description:", "not", "isThenable", ",", "asString", "ifTrue:", "and:", "finished", "ifFalse:ifTrue:", "tearDown", "then:catch:", "execute:", "signal"]
+messageSends: ["context:", "ensure:", "value", "ifFalse:", "isAsync", "assert:description:", "not", "isThenable", ",", "asString", "ifTrue:", "and:", "finished", "ifFalse:ifTrue:", "tearDown", "then:catch:", "execute:", "pass", "asSmalltalkException:"]
 }, function ($methodClass){ return function (aBlock){
 var self=this,$self=this;
 var failed,result;
@@ -1149,7 +1146,7 @@ return $self._execute_((function(){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx4) {
 //>>excludeEnd("ctx");
-return $recv(error)._signal();
+return $recv($recv($globals.Smalltalk)._asSmalltalkException_(error))._pass();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx4) {$ctx4.fillBlock({},$ctx3,14)});
 //>>excludeEnd("ctx");

+ 2 - 4
lang/src/SUnit.st

@@ -146,9 +146,7 @@ errorIfNotAsync: aString
 !TestCase methodsFor: 'private'!
 
 signalFailure: aString
-	TestFailure new
-		messageText: aString;
-		signal
+	TestFailure signal: aString
 ! !
 
 !TestCase methodsFor: 'running'!
@@ -299,7 +297,7 @@ execute: aBlock
 				ifTrue: [ result isThenable ifTrue: [
 					result
 						then: [ testCase isAsync ifTrue: [ self execute: [ testCase finished ] ] ]
-						catch: [ :error | testCase isAsync ifTrue: [ self execute: [ error signal ] ] ] ] ] ]
+						catch: [ :error | testCase isAsync ifTrue: [ self execute: [ (Smalltalk asSmalltalkException: error) pass ] ] ] ] ] ]
 !
 
 start