Browse Source

Error handling out of kernel.

Kernel no more takes care of error handling,
it only does basic bookkeeping of thisContext.

Error handling is installed fully on Smalltalk side,
and only catches unhandled exceptions and unhandled promise rejections.
Herby Vojčík 4 years ago
parent
commit
35a78741f1

+ 6 - 27
lang/base/kernel-runtime.js

@@ -363,35 +363,14 @@ define(['./junk-drawer'], function ($goodies) {
                 }
             }
 
-            /*
-             Runs worker function so that error handler is not set up
-             if there isn't one. This is accomplished by unconditional
-             wrapping inside a context of a simulated `nil seamlessDoIt` call,
-             which then stops error handler setup (see st.withContext above).
-             The effect is, $core.seamless(fn)'s exceptions are not
-             handed into ST error handler and caller should process them.
-             */
+            // TODO deprecated, remove
             st.seamless = function (worker) {
-                var oldContext = thisContext;
-                thisContext = new SmalltalkMethodContext(thisContext, function (ctx) {
-                    ctx.fill(null, "seamlessDoIt", {}, globals.UndefinedObject);
-                });
-                var result = oldContext == null ? resultWithNoErrorHandling(worker) : worker(thisContext);
-                thisContext = oldContext;
-                return result;
+                return worker();
+                // return st.withContext(worker, new SmalltalkMethodContext(thisContext, function (ctx) {
+                //     ctx.fill(null, "seamlessDoIt", {}, globals.UndefinedObject);
+                // }));
             };
 
-            function resultWithErrorHandling (worker) {
-                try {
-                    return worker(thisContext);
-                } catch (error) {
-                    globals.ErrorHandler._handleError_(error);
-                    thisContext = null;
-                    // Rethrow the error in any case.
-                    throw error;
-                }
-            }
-
             /*
              Standard way to run within context.
              Sets up error handler if entering first ST context in a stack.
@@ -399,7 +378,7 @@ define(['./junk-drawer'], function ($goodies) {
             st.withContext = function (worker, setup) {
                 var oldContext = thisContext;
                 thisContext = new SmalltalkMethodContext(thisContext, setup);
-                var result = oldContext == null ? resultWithErrorHandling(worker) : worker(thisContext);
+                var result = oldContext == null ? resultWithNoErrorHandling(worker) : worker(thisContext);
                 thisContext = oldContext;
                 return result;
             };

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

@@ -184,19 +184,17 @@ selector: "catch:",
 protocol: "promises",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aBlock"],
-source: "catch: aBlock\x0a<inlineJS: 'return self.then(null, function (err) {return $core.seamless(function () {\x0a    return aBlock._value_(err);\x0a})})'>",
+source: "catch: aBlock\x0a<inlineJS: 'return self.then(null, function (err) { return aBlock._value_(err); })'>",
 referencedClasses: [],
 //>>excludeEnd("ide");
-pragmas: [["inlineJS:", ["return self.then(null, function (err) {return $core.seamless(function () {\x0a    return aBlock._value_(err);\x0a})})"]]],
+pragmas: [["inlineJS:", ["return self.then(null, function (err) { return aBlock._value_(err); })"]]],
 messageSends: []
 }, function ($methodClass){ return function (aBlock){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-return self.then(null, function (err) {return $core.seamless(function () {
-    return aBlock._value_(err);
-})});
+return self.then(null, function (err) { return aBlock._value_(err); });
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"catch:",{aBlock:aBlock})});
@@ -210,20 +208,20 @@ selector: "on:do:",
 protocol: "promises",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aClass", "aBlock"],
-source: "on: aClass do: aBlock\x0a<inlineJS: 'return self.then(null, function (err) {return $core.seamless(function () {\x0a    if (err._isKindOf_(aClass)) return aBlock._value_(err);\x0a    else throw err;\x0a})})'>",
+source: "on: aClass do: aBlock\x0a<inlineJS: 'return self.then(null, function (err) {\x0a    if (err._isKindOf_(aClass)) return aBlock._value_(err);\x0a    else throw err;\x0a})'>",
 referencedClasses: [],
 //>>excludeEnd("ide");
-pragmas: [["inlineJS:", ["return self.then(null, function (err) {return $core.seamless(function () {\x0a    if (err._isKindOf_(aClass)) return aBlock._value_(err);\x0a    else throw err;\x0a})})"]]],
+pragmas: [["inlineJS:", ["return self.then(null, function (err) {\x0a    if (err._isKindOf_(aClass)) return aBlock._value_(err);\x0a    else throw err;\x0a})"]]],
 messageSends: []
 }, function ($methodClass){ return function (aClass,aBlock){
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-return self.then(null, function (err) {return $core.seamless(function () {
+return self.then(null, function (err) {
     if (err._isKindOf_(aClass)) return aBlock._value_(err);
     else throw err;
-})});
+});
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"on:do:",{aClass:aClass,aBlock:aBlock})});
@@ -260,10 +258,10 @@ selector: "then:",
 protocol: "promises",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aBlockOrArray"],
-source: "then: aBlockOrArray\x0a\x22Accepts a block or array of blocks.\x0aEach of blocks in the array or the singleton one is\x0aused in .then call to a promise, to accept a result\x0aand transform it to the result for the next one.\x0aIn case a block has more than one argument\x0aand result is an array, first n-1 elements of the array\x0aare put into additional arguments beyond the first.\x0aThe first argument always contains the result as-is.\x22\x0a<inlineJS: '\x0avar array = Array.isArray(aBlockOrArray) ? aBlockOrArray : [aBlockOrArray];\x0areturn array.reduce(function (soFar, aBlock) {\x0a    return soFar.then(typeof aBlock === \x22function\x22 && aBlock.length > 1 ?\x0a        function (result) {return $core.seamless(function () {\x0a            if (Array.isArray(result)) {\x0a                return aBlock._valueWithPossibleArguments_([result].concat(result.slice(0, aBlock.length-1)));\x0a            } else {\x0a                return aBlock._value_(result);\x0a            }\x0a        })} :\x0a        function (result) {return $core.seamless(function () {\x0a            return aBlock._value_(result);\x0a        })}\x0a    );\x0a}, self)'>",
+source: "then: aBlockOrArray\x0a\x22Accepts a block or array of blocks.\x0aEach of blocks in the array or the singleton one is\x0aused in .then call to a promise, to accept a result\x0aand transform it to the result for the next one.\x0aIn case a block has more than one argument\x0aand result is an array, first n-1 elements of the array\x0aare put into additional arguments beyond the first.\x0aThe first argument always contains the result as-is.\x22\x0a<inlineJS: '\x0avar array = Array.isArray(aBlockOrArray) ? aBlockOrArray : [aBlockOrArray];\x0areturn array.reduce(function (soFar, aBlock) {\x0a    return soFar.then(typeof aBlock === \x22function\x22 && aBlock.length > 1 ?\x0a       function (result) {\x0a            if (Array.isArray(result)) {\x0a                return aBlock._valueWithPossibleArguments_([result].concat(result.slice(0, aBlock.length-1)));\x0a            } else {\x0a                return aBlock._value_(result);\x0a            }\x0a        } :\x0a        function (result) {\x0a            return aBlock._value_(result);\x0a        }\x0a    );\x0a}, self)'>",
 referencedClasses: [],
 //>>excludeEnd("ide");
-pragmas: [["inlineJS:", ["\x0avar array = Array.isArray(aBlockOrArray) ? aBlockOrArray : [aBlockOrArray];\x0areturn array.reduce(function (soFar, aBlock) {\x0a    return soFar.then(typeof aBlock === \x22function\x22 && aBlock.length > 1 ?\x0a        function (result) {return $core.seamless(function () {\x0a            if (Array.isArray(result)) {\x0a                return aBlock._valueWithPossibleArguments_([result].concat(result.slice(0, aBlock.length-1)));\x0a            } else {\x0a                return aBlock._value_(result);\x0a            }\x0a        })} :\x0a        function (result) {return $core.seamless(function () {\x0a            return aBlock._value_(result);\x0a        })}\x0a    );\x0a}, self)"]]],
+pragmas: [["inlineJS:", ["\x0avar array = Array.isArray(aBlockOrArray) ? aBlockOrArray : [aBlockOrArray];\x0areturn array.reduce(function (soFar, aBlock) {\x0a    return soFar.then(typeof aBlock === \x22function\x22 && aBlock.length > 1 ?\x0a       function (result) {\x0a            if (Array.isArray(result)) {\x0a                return aBlock._valueWithPossibleArguments_([result].concat(result.slice(0, aBlock.length-1)));\x0a            } else {\x0a                return aBlock._value_(result);\x0a            }\x0a        } :\x0a        function (result) {\x0a            return aBlock._value_(result);\x0a        }\x0a    );\x0a}, self)"]]],
 messageSends: []
 }, function ($methodClass){ return function (aBlockOrArray){
 var self=this,$self=this;
@@ -274,16 +272,16 @@ return $core.withContext(function($ctx1) {
 var array = Array.isArray(aBlockOrArray) ? aBlockOrArray : [aBlockOrArray];
 return array.reduce(function (soFar, aBlock) {
     return soFar.then(typeof aBlock === "function" && aBlock.length > 1 ?
-        function (result) {return $core.seamless(function () {
+       function (result) {
             if (Array.isArray(result)) {
                 return aBlock._valueWithPossibleArguments_([result].concat(result.slice(0, aBlock.length-1)));
             } else {
                 return aBlock._value_(result);
             }
-        })} :
-        function (result) {return $core.seamless(function () {
+        } :
+        function (result) {
             return aBlock._value_(result);
-        })}
+        }
     );
 }, self);
 return self;

+ 7 - 9
lang/src/Kernel-Promises.st

@@ -57,16 +57,14 @@ Trait named: #TThenable
 !TThenable methodsFor: 'promises'!
 
 catch: aBlock
-<inlineJS: 'return self.then(null, function (err) {return $core.seamless(function () {
-    return aBlock._value_(err);
-})})'>
+<inlineJS: 'return self.then(null, function (err) { return aBlock._value_(err); })'>
 !
 
 on: aClass do: aBlock
-<inlineJS: 'return self.then(null, function (err) {return $core.seamless(function () {
+<inlineJS: 'return self.then(null, function (err) {
     if (err._isKindOf_(aClass)) return aBlock._value_(err);
     else throw err;
-})})'>
+})'>
 !
 
 on: aClass do: aBlock catch: anotherBlock
@@ -86,16 +84,16 @@ The first argument always contains the result as-is."
 var array = Array.isArray(aBlockOrArray) ? aBlockOrArray : [aBlockOrArray];
 return array.reduce(function (soFar, aBlock) {
     return soFar.then(typeof aBlock === "function" && aBlock.length > 1 ?
-        function (result) {return $core.seamless(function () {
+       function (result) {
             if (Array.isArray(result)) {
                 return aBlock._valueWithPossibleArguments_([result].concat(result.slice(0, aBlock.length-1)));
             } else {
                 return aBlock._value_(result);
             }
-        })} :
-        function (result) {return $core.seamless(function () {
+        } :
+        function (result) {
             return aBlock._value_(result);
-        })}
+        }
     );
 }, self)'>
 !

+ 51 - 0
lang/src/Platform-Browser.js

@@ -108,6 +108,57 @@ return window;
 }; }),
 $globals.BrowserPlatform);
 
+$core.addMethod(
+$core.method({
+selector: "initialize",
+protocol: "public API",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "initialize\x0a\x09window\x0a\x09\x09addEventListener: 'error'\x0a\x09\x09do: [ :event | ErrorHandler handleError: event error ];\x0a\x09\x09addEventListener: 'unhandledrejection'\x0a\x09\x09do: [ :event | ErrorHandler handleError: event reason ]",
+referencedClasses: ["ErrorHandler"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["addEventListener:do:", "handleError:", "error", "reason"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=window;
+[$recv($1)._addEventListener_do_("error",(function(event){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return [$recv($globals.ErrorHandler)._handleError_($recv(event)._error())
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx2.sendIdx["handleError:"]=1
+//>>excludeEnd("ctx");
+][0];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({event:event},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["addEventListener:do:"]=1
+//>>excludeEnd("ctx");
+][0];
+$recv($1)._addEventListener_do_("unhandledrejection",(function(event){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($globals.ErrorHandler)._handleError_($recv(event)._reason());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({event:event},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"initialize",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.BrowserPlatform);
+
 $core.addMethod(
 $core.method({
 selector: "newXhr",

+ 8 - 0
lang/src/Platform-Browser.st

@@ -26,6 +26,14 @@ fetchUrl: aString options: anObject
 		ifAbsent: [ Promise signal: 'fetch not available.' ]
 !
 
+initialize
+	window
+		addEventListener: 'error'
+		do: [ :event | ErrorHandler handleError: event error ];
+		addEventListener: 'unhandledrejection'
+		do: [ :event | ErrorHandler handleError: event reason ]
+!
+
 newXhr
 	XMLHttpRequest
 		ifNotNil: [ ^ NativeFunction constructorOf: XMLHttpRequest ]