graaljs icon indicating copy to clipboard operation
graaljs copied to clipboard

Js error message became [object Object]

Open sixinli opened this issue 3 years ago • 5 comments

js script exception not being extracted properly after 19.x.x

we noticed that exceptions changes from having actual message (in this case we have some errors from handlebars.js), to having [object Object] when upgrading from 19.3.1 to 21.0.0, looking at https://github.com/oracle/graaljs/blob/master/graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/runtime/UserScriptException.java it seems like getMessage is changed between the versions...

specifically https://github.com/oracle/graaljs/blob/master/graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/runtime/UserScriptException.java#L125 returns null, and that's from com.oracle.truffle.object.DynamicObjectLibraryImpl

@TruffleBoundary
@Override
public Object getOrDefault(DynamicObject object, Shape cachedShape, Object key, Object defaultValue) {
    Property existing = ACCESS.getShape(object).getProperty(key);
    if (existing != null) {
        return getLocation(existing).get(object, false);
    } else {
        return defaultValue;
    }
}

thinks DynamicObject<JSError> has no constructor....

sixinli avatar Apr 03 '21 00:04 sixinli

Do you have some example code that you can share that triggers this?

woess avatar Apr 06 '21 18:04 woess

sorry for the late reply, we have the handlebars.js 3.x running, and it's just an error thrown in javascript like https://github.com/handlebars-lang/handlebars.js/blob/v3.0.8/lib/handlebars/runtime.js#L198, the message is lot in between

sixinli avatar Jun 11 '21 05:06 sixinli

Hey it seems that Handlebars JS has created their own custom exception which seems to be the problem.

Here is a minimal example that outlines the problem

import com.google.common.io.Resources;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Source;


public class Program {
    public static void main(String[] args) {
        Engine engine = Engine.create();
        Context context = Context.newBuilder()
                .engine(engine)
                .option("js.nashorn-compat", "true")
                .allowAllAccess(true)
                .build();


        String customErrorFunc = "function customError(errorMessage) { this.message = Error.prototype.constructor.call(this, errorMessage).message; }\n" +
                "customError.prototype = new Error();";
        try {
            context.eval(Source.create("js", customErrorFunc));
            context.eval(Source.create("js", "throw new customError('This is a basic error')"));

        } catch (Exception e) {
            System.out.println(e.getMessage());
            // 19.3.1 returns {message: "This is a basic error"}
            // 21.1.0 returns [object Object]
        }
    }
}

rdosanjh avatar Jul 13 '21 14:07 rdosanjh

I've done some digging and this was introduced in this commit. We were using the "js.nashorn-compat" mode but when they became combined it broke our functionality.

Here is the line in question before - after. (JSUserObject has since been renamed to JSOrdinary).

Would it be possible to add an option to go back to the previous way of displaying errors?

rdosanjh avatar Jul 14 '21 08:07 rdosanjh

A simple workaround for this would be to set the constructor property on the prototype of the custom error: customError.prototype.constructor = customError; With this simple change I see: customError: This is a basic error

Alternatively, you could use just subclass Error, e.g. class CustomError extends Error {}

It would probably make sense to show the message also without the constructor property. We'll look into that.

woess avatar Sep 01 '21 17:09 woess