Browse Source

Move error handling to Smalltalk side.

Herbert Vojčík 6 years ago
parent
commit
c0cec21d7c

+ 6 - 0
API-CHANGES.txt

@@ -6,6 +6,12 @@
 + AmberBootstrapInitialization class >>
   + run
   + initializeClasses
++ JavaScriptException
+  + shouldBeStubbed
+  + wrap
++ MethodContext
+  + stubHere
+  + stubToAtMost:
 + UndefinedObject >>
   + ==
 

+ 68 - 0
src/Kernel-Exceptions.js

@@ -608,6 +608,74 @@ messageSends: []
 }),
 $globals.JavaScriptException);
 
+$core.addMethod(
+$core.method({
+selector: "shouldBeStubbed",
+protocol: "testing",
+fn: function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $self["@exception"] instanceof RangeError;
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"shouldBeStubbed",{},$globals.JavaScriptException)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "shouldBeStubbed\x0a\x09<inlineJS: 'return $self[\x22@exception\x22] instanceof RangeError'>",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.JavaScriptException);
+
+$core.addMethod(
+$core.method({
+selector: "wrap",
+protocol: "error handling",
+fn: function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._signal();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))._tryCatch_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$1=$self._shouldBeStubbed();
+if($core.assert($1)){
+return $recv($self._context())._stubToAtMost_((100));
+}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"wrap",{},$globals.JavaScriptException)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "wrap\x0a\x09[ self signal ] tryCatch:\x0a\x09\x09[ self shouldBeStubbed ifTrue: [ self context stubToAtMost: 100 ] ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["tryCatch:", "signal", "ifTrue:", "shouldBeStubbed", "stubToAtMost:", "context"]
+}),
+$globals.JavaScriptException);
+
 
 $core.addMethod(
 $core.method({

+ 13 - 0
src/Kernel-Exceptions.st

@@ -164,6 +164,19 @@ messageText
 	<inlineJS: 'return "JavaScript exception: " + $self["@exception"].toString()'>
 ! !
 
+!JavaScriptException methodsFor: 'error handling'!
+
+wrap
+	[ self signal ] tryCatch:
+		[ self shouldBeStubbed ifTrue: [ self context stubToAtMost: 100 ] ]
+! !
+
+!JavaScriptException methodsFor: 'testing'!
+
+shouldBeStubbed
+	<inlineJS: 'return $self["@exception"] instanceof RangeError'>
+! !
+
 !JavaScriptException class methodsFor: 'instance creation'!
 
 on: anException

+ 71 - 0
src/Kernel-Methods.js

@@ -2612,6 +2612,77 @@ messageSends: []
 }),
 $globals.MethodContext);
 
+$core.addMethod(
+$core.method({
+selector: "stubHere",
+protocol: "accessing",
+fn: function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+self.homeContext = undefined;
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"stubHere",{},$globals.MethodContext)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "stubHere\x0a\x09<inlineJS: 'self.homeContext = undefined'>",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.MethodContext);
+
+$core.addMethod(
+$core.method({
+selector: "stubToAtMost:",
+protocol: "error handling",
+fn: function (anInteger){
+var self=this,$self=this;
+var context;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2,$receiver;
+context=self;
+$recv(anInteger)._timesRepeat_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$1=context;
+if(($receiver = $1) == null || $receiver.a$nil){
+context=$1;
+} else {
+context=$recv(context)._home();
+}
+return context;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+$2=context;
+if(($receiver = $2) == null || $receiver.a$nil){
+$2;
+} else {
+$recv(context)._stubHere();
+}
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"stubToAtMost:",{anInteger:anInteger,context:context},$globals.MethodContext)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anInteger"],
+source: "stubToAtMost: anInteger\x0a\x09| context |\x0a\x09context := self.\x0a\x09anInteger timesRepeat: [ context := context ifNotNil: [ context home ] ].\x0a\x09context ifNotNil: [ context stubHere ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["timesRepeat:", "ifNotNil:", "home", "stubHere"]
+}),
+$globals.MethodContext);
+
 $core.addMethod(
 $core.method({
 selector: "supercall",

+ 13 - 0
src/Kernel-Methods.st

@@ -643,6 +643,10 @@ sendIndexes
 	<inlineJS: 'return self.sendIdx'>
 !
 
+stubHere
+	<inlineJS: 'self.homeContext = undefined'>
+!
+
 supercall
 	<inlineJS: 'return self.supercall == true'>
 ! !
@@ -660,6 +664,15 @@ asString
 				ifFalse: [ self receiver class name, '(', methodClass name, ') >> ', self selector ] ]
 ! !
 
+!MethodContext methodsFor: 'error handling'!
+
+stubToAtMost: anInteger
+	| context |
+	context := self.
+	anInteger timesRepeat: [ context := context ifNotNil: [ context home ] ].
+	context ifNotNil: [ context stubHere ]
+! !
+
 !MethodContext methodsFor: 'printing'!
 
 printOn: aStream

+ 33 - 8
src/Platform-Services.js

@@ -1436,7 +1436,31 @@ var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
+var $1;
+$1=$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(anError)._isSmalltalkError();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))._tryCatch_((function(){
+return false;
+
+}));
+if($core.assert($1)){
 $self._handleUnhandledError_(anError);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["handleUnhandledError:"]=1;
+//>>excludeEnd("ctx");
+} else {
+var smalltalkError;
+smalltalkError=$recv($globals.JavaScriptException)._on_(anError);
+smalltalkError;
+$recv(smalltalkError)._wrap();
+$self._handleUnhandledError_(smalltalkError);
+}
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"handleError:",{anError:anError},$globals.ErrorHandler.a$cls)});
@@ -1444,10 +1468,10 @@ return self;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["anError"],
-source: "handleError: anError\x0a\x09self handleUnhandledError: anError",
-referencedClasses: [],
+source: "handleError: anError\x0a\x09([ anError isSmalltalkError ] tryCatch: [ false ])\x0a\x09\x09ifTrue: [ self handleUnhandledError: anError ]\x0a\x09\x09ifFalse: [\x0a\x09\x09\x09| smalltalkError |\x0a\x09\x09\x09smalltalkError := JavaScriptException on: anError.\x0a\x09\x09\x09smalltalkError wrap.\x0a\x09\x09\x09self handleUnhandledError: smalltalkError ]",
+referencedClasses: ["JavaScriptException"],
 //>>excludeEnd("ide");
-messageSends: ["handleUnhandledError:"]
+messageSends: ["ifTrue:ifFalse:", "tryCatch:", "isSmalltalkError", "handleUnhandledError:", "on:", "wrap"]
 }),
 $globals.ErrorHandler.a$cls);
 
@@ -1462,20 +1486,21 @@ return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
 var $1;
 $1=$recv(anError)._wasHandled();
-if($core.assert($1)){
-return self;
+if(!$core.assert($1)){
+$recv($self._current())._handleError_(anError);
+$recv(anError)._beHandled();
 }
-return $recv($self._current())._handleError_(anError);
+return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"handleUnhandledError:",{anError:anError},$globals.ErrorHandler.a$cls)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["anError"],
-source: "handleUnhandledError: anError\x0a\x09anError wasHandled ifTrue: [ ^ self ].\x0a\x09\x0a\x09^ self current handleError: anError",
+source: "handleUnhandledError: anError\x0a\x09anError wasHandled ifFalse: [\x0a\x09\x09self current handleError: anError.\x0a\x09\x09anError beHandled ]",
 referencedClasses: [],
 //>>excludeEnd("ide");
-messageSends: ["ifTrue:", "wasHandled", "handleError:", "current"]
+messageSends: ["ifFalse:", "wasHandled", "handleError:", "current", "beHandled"]
 }),
 $globals.ErrorHandler.a$cls);
 

+ 10 - 4
src/Platform-Services.st

@@ -356,13 +356,19 @@ Registered service instances must implement `#handleError:` to perform an action
 !ErrorHandler class methodsFor: 'error handling'!
 
 handleError: anError
-	self handleUnhandledError: anError
+	([ anError isSmalltalkError ] tryCatch: [ false ])
+		ifTrue: [ self handleUnhandledError: anError ]
+		ifFalse: [
+			| smalltalkError |
+			smalltalkError := JavaScriptException on: anError.
+			smalltalkError wrap.
+			self handleUnhandledError: smalltalkError ]
 !
 
 handleUnhandledError: anError
-	anError wasHandled ifTrue: [ ^ self ].
-	
-	^ self current handleError: anError
+	anError wasHandled ifFalse: [
+		self current handleError: anError.
+		anError beHandled ]
 ! !
 
 Service subclass: #Finder

+ 1 - 49
support/kernel-runtime.js

@@ -342,7 +342,7 @@ define(function () {
             try {
                 return inContext(worker, setup);
             } catch (error) {
-                handleError(error);
+                globals.ErrorHandler._handleError_(error);
                 thisContext = null;
                 // Rethrow the error in any case.
                 throw error;
@@ -357,54 +357,6 @@ define(function () {
             return result;
         }
 
-        /* Wrap a JavaScript exception in a Smalltalk Exception.
-
-         In case of a RangeError, stub the stack after 100 contexts to
-         avoid another RangeError later when the stack is manipulated. */
-        function wrappedError (error) {
-            var errorWrapper = globals.JavaScriptException._on_(error);
-            // Add the error to the context, so it is visible in the stack
-            try {
-                errorWrapper._signal();
-            } catch (ex) {
-            }
-            if (shouldBeStubbed(error)) {
-                stubContextStack(errorWrapper.context);
-            }
-            return errorWrapper;
-        }
-
-        /* Stub the context stack after 100 contexts */
-        function stubContextStack (context) {
-            var currentContext = context;
-            var contexts = 0;
-            while (contexts < 100) {
-                if (currentContext) {
-                    currentContext = currentContext.homeContext;
-                }
-                contexts++;
-            }
-            if (currentContext) {
-                currentContext.homeContext = undefined;
-            }
-        }
-
-        function shouldBeStubbed (error) {
-            return error instanceof RangeError;
-        }
-
-
-        /* Handles Smalltalk errors. Triggers the registered ErrorHandler
-         (See the Smalltalk class ErrorHandler and its subclasses */
-
-        function handleError (error) {
-            if (!error.smalltalkError) {
-                error = wrappedError(error);
-            }
-            globals.ErrorHandler._handleError_(error);
-            error.amberHandled = true;
-        }
-
         /* Handle thisContext pseudo variable */
 
         st.getThisContext = function () {