Is it possible to influence errors thrown from the JS compiler? In particular, I want to create my own error type NullPointerException and then proxy the built-in errors (such as Error and TypeError) to potentially return my custom exception.
Consider the following simple attempt (yes, they are global variables – but they are supposed to be):
NullPointerException = function (msg) {
this.message = msg;
};
NullPointerException.prototype.toString = function () {
return "NullPointerException: " + this.message;
};
var ProxyTypeError = TypeError;
TypeError = function (msg) {
if (msg.indexOf('null') === -1) {
return new ProxyTypeError(msg);
}
return new NullPointerException(msg);
};
This will work fine for cases like
throw new TypeError('normal error'); // 'TypeError: normal error'
throw new TypeError('null'); // 'NullPointerException: null'
However, it won't work for the scenario I actually want it to work:
var obj = null;
console.log( obj.someMethod() ); // 'Uncaught TypeError: ...'
I am aware that browsers both use different messages and different errors, as well as that it's sketchy to even be wanting to do any of this. However, I'd still be interested if there is any actual solution to this? In the end, the use-case is something like
try {
// ... code ...
} catch( e ) {
if( e instanceof NullPointerException ) {
// handle NPE separately
}
// do something else
}
wherein I do not have access to the catch part, hence my desire to throw the error accordingly.
Is it possible to influence errors thrown from the JS interpreter?
No. However, if you want to throw your own errors you can do that for all exceptions that happened in your own code:
try {
/* some code, possibly foreign */
var obj = null;
obj.someMethod()
} catch (e) {
throw new CustomTypeError(e.msg);
}
but it would be much better to just check your types:
var obj = null;
if (obj == null)
throw new NullPointerException();
else
obj.someMethod();
Related
I've been writing simple web-frontend for application with kotlin-js and faced with a problem of exception handling.
As I see, there is no API to get exception stacktrace: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-throwable/index.html
Is it so?
If it is, may be anyone know some library or snippets to get stacktrace out of Throwable object?
Currently, I've got some workaround for this:
import kotlin.browser.window
fun main() {
window.onload = {
try {
throw RuntimeException()
} catch (e: Throwable) {
console.log(e)
throw e
}
}
}
Console output is:
Object {
"message_8yp7un$_0": null,
"cause_th0jdv$_0": null,
"stack": "captureStack#http://localhost:9080/js/kotlin.js:1767:27\nException#http://localhost:9080/js/kotlin.js:3244:14\nRuntimeException#http://localhost:9080/js/kotlin.js:3255:17\nRuntimeException_init#http://localhost:9080/js/kotlin.js:3261:24\nmain$lambda#http://localhost:9080/js/web-client.js:34:13\n",
"name": "RuntimeException"
}
Here, console.log(Throwable) exposes underlying JavaScript object properties, and there is stack one, but it points to JavaScript code, that is hard to use without source mapping back to kotlin.
UPD: it seems like stack is not standard exception property, but common one for modern browsers.
Since Kotlin 1.4 the stdlib contains two extensions for this exact purpose
fun Throwable.stackTraceToString()
fun Throwable.printStackTrace()
Using these we can write
window.onload = {
try {
throw RuntimeException()
} catch (e: Throwable) {
e.printStackTrace()
}
}
Just change your console.log to console.error
import kotlin.browser.window
fun main() {
window.onload = {
try {
throw RuntimeException()
} catch (e: Throwable) {
console.error(e) // change here
throw e
}
}
}
Not perfect, not standard, but it works.
It also handles the source map in Firefox, so I get proper file names and line numbers:
try {
throw IllegalStateException("ops...")
}
catch (ex : Throwable) {
val stack = ex.asDynamic().stack
if (stack is String) {
val error = js("Error()")
error.name = ex.toString().substringBefore(':')
error.message = ex.message?.substringAfter(':')
error.stack = stack
console.error(error)
} else {
console.log(ex)
}
}
Implementing an assert in javascript is not difficult:
assert = function(expression,errorMessage){
if (!expression){
errorMessage = errorMessage || "(no msg)";
throw new Error("assert failed: "+errorMessage);
}
return true;
};
However, using this version of assert is tiresome because you have to have a meaningful error message for every test case:
var types = {isNumber:function(x){return typeof x === "number" && isFinite(x)}}
assert(types.isNumber(1)===true,"types.isNumber(1)===true");
assert(types.isNumber(NaN)===false,"types.isNumber(NaN)===false");
My question is that is there a way to implement the assert function such that it only takes one expression and it can return meaningful error message if that expression is not met? Like:
assert(SOMETHING_that_is_not_true); // throw Error: SOMETHINGELSE that refers to this particular assertion
assert(SOMETHING_that_is_not_true2); // throw Error: SOMETHINGELSE2 that refers to this different assertion
It's a bit more code than a simple expression for each assertion, but how about this?
assert = function(expression){
if (!expression()){
errorMessage = expression.toString() || "(no msg)";
throw new Error("assert failed: "+errorMessage);
}
return true;
};
assert(function() { return 'b' == 'a' });
I just want to add this as it will work. But it is
Not advised
As it uses eval, which is, juck. But it is useful if you don't face it to the client. (What am I saying? It is not advised. End of that. But it does work.)
function assert(expression){
var success = eval(expression);
if(!success) throw new Error("assert failed: "+ expression);
else return true;
}
Then you can do
assert("types.isNumber(1)===true");
So te reiterate, use #James Thorpe s code (hopefully) above.
Looks like in node.js, process.domain is null at the top level. What this means to me is that there is no "top-level domain" that errors can be thrown into. This leads me to my question:
var d = require('domain').create();
d.on('error', function(er) {
console.log(er)
})
d.run(function() {
setTimeout(function(){
throw new Error("?") // how do you throw this such that it doesn't hit the on 'error' handler but instead is treated like the error below would be?
}, 0)
})
//throw new Error("the kind of error handling i want")
Basically, I'm trying to store process.domain at one point, and then throw the exception in the context of that domain when the domain-context has changed. The problem is, since there is no "top-level domain", its unclear to me how to emulate how an exception would be handled at the top level normally.
Ideas?
Here's one way, but I don't like it - it seems pretty cludgy since it relies on what I consider to be a bad design of domains:
var d = require('domain').create();
d.on('error', function(er) {
console.log("never gets here, oddly enough")
})
d.run(function() {
var d1 = require('domain').create()
d1.on('error', function(e) {
throw e
})
d1.run(function() {
setTimeout(function() {
throw new Error("error I want to throw at the top-level")
})
})
})
Short version
Trying to write a debug command that returns the call stack, minus the current position. I thought I'd use:
try {
throw new Error(options["msg"])
} catch (e) {
e.stack.shift;
throw (e);
}
but I don't know how to do it exactly. apparently I can't just e.stack.shift like that. Also that always makes it an Uncaught Error — but these should just be debug messages.
Long version
I decided I needed a debug library for my content scripts. Here it is:
debug.js
var debugKeys = {
"level": ["off", "event", "function", "timeouts"],
"detail": ["minimal", "detailed"]
};
var debugState = { "level": "off", "detail": "minimal" };
function debug(options) {
if ("level" in options) {
if (verifyDebugValue("level", options["level"]) == false)
return
}
if ("detail" in options) {
if (verifyDebugValue("detail", options["detail"]) == false)
return
}
console.log(options["msg"]);
}
function verifyDebugValue(lval, rval){
var state = 10; // sufficiently high
for (k in debugKeys[lval]) {
if (debugKeys[lval][k] == rval) {
return true;
}
if (debugKeys[lval][k] == debugState[lval]) { // rval was greater than debug key
return false;
}
}
}
When you using it, you can change the debugState in the code to suit your needs. it is still a work in progress but it works just fine.
To use it from another content script, just load it in the manifest like:
manifest.json
"content_scripts": [
{
"js": ["debug.js", "foobar.js"],
}
],
and then call it like:
debug({"level": "timeouts", "msg": "foobar.js waitOnElement() timeout"});
which generates:
foobar.js waitOnElement() timeout debug.js:17
And there is my problem. At the moment, it is using the console log and so all the debug statements come from the same debug.js line. I'd rather return the calling context. I imagine I need something like:
try {
throw new Error(options["msg"])
} catch (e) {
e.stack.shift;
throw (e);
}
but I don't know how to do it exactly. apparently I can't just e.stack.shift like that. Also that always makes it an Uncaught Error — but these should just be debug messages.
You can't avoid mentioning the line in your debug.js, because either using throw (...) or console.log/error(...) your debug.js will be issuing the command.
What you can do, is have some try-catch blocks in your code, then in the catch block pass the error object to your debug function, which will handle it according to its debugState.
In any case, it is not quite clear how you are using your debug library (and why you need to remove the last call from the stack-trace, but you could try something like this:
Split the stack-trace (which is actually a multiline string) into lines.
Isolate the first line (corresponding to the last call) that is not part of the error's message.
Put together a new stack-trace, with the removed line.
E.g.:
function removeLastFromStack(stack, errMsg) {
var firstLines = 'Error: ' + errMsg + '\n';
var restOfStack = stack
.substring(firstLines.length) // <-- skip the error's message
.split('\n') // <-- split into lines
.slice(1) // <-- "slice out" the first line
.join('\n'); // <-- put the rest back together
return firstLines + restOfStack;
}
function myDebug(err) {
/* Based on my `debugState` I should decide what to do with this error.
* E.g. I could ignore it, or print the message only,
* or print the full stack-trace, or alert the user, or whatever */
var oldStack = err.stack;
var newStack = removeLastFromStack(oldStack, err.message);
console.log(newStack);
//or: console.error(newStack);
}
/* Somewhere in your code */
function someFuncThatMayThrowAnErr(errMsg) {
throw new Error(errMsg);
}
try {
someFuncThatMayThrowAnErr('test');
} catch (err) {
myDebug(err);
}
...but I still don't see how removing the last call from the trace would be helpful
Take this URL for instance: https://api.eveonline.com/eve/CharacterID.xml.aspx?names=Khan
Using xml2js node.js module you may parse that XML, although it does not look pretty:
var CharacterID = response.eveapi.result[0].rowset[0].row[0].$.characterID;
The app crashed after 2 weeks of running, all because rowset[0] was undefined. Prior to that it crashed because eveapi was not defined. Seriously, does my if-else has to be like this just to prevent server from crashing due to stupid undefined object errors?
if (!response.eveapi ||
!response.eveapi.result[0] ||
!response.eveapi.result[0].rowset[0] ||
!response.eveapi.result[0].rowset[0].row[0]) {
return res.send(500, "Error");
Besides the obvious if (err) return res.send(500, "Error"); error handling where applicable, what is the general practice for undefined errors?
I wrote a library for this kind of thing, called dotty (https://github.com/deoxxa/dotty).
In your case, you could do this:
var dotty = require("dotty");
var CharacterID = dotty.get(response, "eveapi.result.0.rowset.0.row.0.$.characterID");
In the case of the path not being resolvable, it'll just return undefined.
As you've discovered, undefined is not itself an error, but using undefined as an array/object is an error.
x = {'a': { 'b': { 'c': { 'd': [1,2,3,4,5]} } } } ;
try { j = x.a.b.c.e[3] } catch(e) { console.log(e); }
prints
[TypeError: Cannot read property '3' of undefined]
This suggests to me that try/catch can be used with your code to return an error code, and if desired, an error text (or just stick the error text in console.log, a database or local file).
In your case, that could look like:
var CharacterID; // can't define it yet
try {
CharacterID = response.eveapi.result[0].rowset[0].row[0].$.characterID;
} catch(e) {
// send description on the line with error
return res.send(500, "Error: NodeJS assigning CharacterID: "+e);
// return res.send(500, "error"); use this one if you dont want to reveal reason for errors
}
// code here can assume CharacterID evaluated. It might still be undefined, though.
Maybe this function helps?
function tryPath(obj, path) {
path = path.split(/[.,]/);
while (path.length && obj) {
obj = obj[path.shift()];
}
return obj || null;
}
For your code you'd use:
if (tryPath(response,'eveapi.result.0.rows.0.row.0') === null) {
return res.send(500, "Error");
}
jsFiddle example
jsFiddle same example, but as extension to Object.prototype