Graaljs: Endless recursion starting in JavaScriptLanguage#toString?

Created on 15 Apr 2019  路  4Comments  路  Source: oracle/graaljs

I'm currently getting an endless recursion when trying to evaluate

Polyglot.eval('squeaksmalltalk', 'Array')

in the RC15 polyglot shell.

"" + Polyglot.eval('squeaksmalltalk', 'Array')

on the other hand evaluates fine and prints something like this:

foreign {braceStream:: Metaclass (Array)>>braceStream:, new:: Metaclass (Array)>>new:, empty: Metaclass (Array)>>empty, ...

The JVM stack suggests that there could be a problem in JavaScriptLanguage#toString:

   java.lang.Thread.State: RUNNABLE
    at java.lang.StringCoding.decode(StringCoding.java:254)
    at java.lang.String.<init>(String.java:546)
    at java.lang.String.<init>(String.java:566)
    at de.hpi.swa.graal.squeak.util.ArrayConversionUtils.bytesToString(ArrayConversionUtils.java:109)
    at de.hpi.swa.graal.squeak.model.NativeObject.asString(NativeObject.java:232)
    at de.hpi.swa.graal.squeak.model.NativeObject.toString(NativeObject.java:197)
    at de.hpi.swa.graal.squeak.model.ClassObject.lambda$lookup$0(ClassObject.java:307)
    at de.hpi.swa.graal.squeak.model.ClassObject$$Lambda$57/1632789609.test(Unknown Source)
    at de.hpi.swa.graal.squeak.model.ClassObject.lookup(ClassObject.java:319)
    at de.hpi.swa.graal.squeak.model.ClassObject.lookup(ClassObject.java:307)
    at de.hpi.swa.graal.squeak.model.AbstractSqueakObject.readMember(AbstractSqueakObject.java:222)
    at de.hpi.swa.graal.squeak.model.AbstractSqueakObjectGen$InteropLibraryExports$Uncached.readMember(AbstractSqueakObjectGen.java:386)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.foreignObjectToString(JavaScriptLanguage.java:670)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toStringForeignObject(JavaScriptLanguage.java:367)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:324)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.foreignArrayToString(JavaScriptLanguage.java:643)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toStringForeignObject(JavaScriptLanguage.java:349)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:324)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.foreignObjectToString(JavaScriptLanguage.java:673)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toStringForeignObject(JavaScriptLanguage.java:367)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:324)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.foreignArrayToString(JavaScriptLanguage.java:643)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toStringForeignObject(JavaScriptLanguage.java:349)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:324)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.foreignObjectToString(JavaScriptLanguage.java:673)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toStringForeignObject(JavaScriptLanguage.java:367)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:324)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.foreignArrayToString(JavaScriptLanguage.java:643)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toStringForeignObject(JavaScriptLanguage.java:349)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:324)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.foreignObjectToString(JavaScriptLanguage.java:673)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toStringForeignObject(JavaScriptLanguage.java:367)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:324)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.foreignArrayToString(JavaScriptLanguage.java:643)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toStringForeignObject(JavaScriptLanguage.java:349)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:324)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.foreignObjectToString(JavaScriptLanguage.java:673)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toStringForeignObject(JavaScriptLanguage.java:367)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:324)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:299)
    at com.oracle.truffle.js.lang.JavaScriptLanguage.toString(JavaScriptLanguage.java:126)
    at com.oracle.truffle.api.TruffleLanguage$Env.toStringIfVisible(TruffleLanguage.java:2219)
    at com.oracle.truffle.api.TruffleLanguage$LanguageImpl.toStringIfVisible(TruffleLanguage.java:2632)
    at com.oracle.truffle.polyglot.PolyglotContextImpl.printResult(PolyglotContextImpl.java:790)
    at com.oracle.truffle.polyglot.PolyglotContextImpl.eval(PolyglotContextImpl.java:768)
    at org.graalvm.polyglot.Context.eval(Context.java:341)
    at org.graalvm.launcher.MultiLanguageShell.readEvalPrint(MultiLanguageShell.java:197)
    at org.graalvm.launcher.PolyglotLauncher.runShell(PolyglotLauncher.java:347)
    at org.graalvm.launcher.PolyglotLauncher.launch(PolyglotLauncher.java:220)
    at org.graalvm.launcher.PolyglotLauncher.main(PolyglotLauncher.java:357)

It's possible that this is an interop bug in our language implementation. But maybe Graal.js should deploy a failsafe and terminate toString() after a certain depth?

bug

All 4 comments

Hi @fniephaus

thanks for your report. This definitly looks like a bug in Graal.js. Some nesting and recursion is expected (depending on the nesting of your objects), but it should not be an infinite loop of course. I will investigate.

Best,
Christian

@wirthi The recursion is not infinite in theory, but it is too long to finish in practice. The root of the problem is that we allow the recursion up to 10th level, see JavaScriptLanguage.MAX_TOSTRING_DEPTH. This is too much for any object that may reference itself. Consider, for example,

        Context context = Context.newBuilder("js").allowAllAccess(true).build();
        context.getBindings("js").putMember("array", new ProxyArray() {
            @Override
            public Object get(long index) {
                return this;
            }

            @Override
            public void set(long index, Value arg1) {
            }

            @Override
            public long getSize() {
                return 10;
            }
        });
        Source source = Source.newBuilder("js", "array", "noname").interactive(true).buildLiteral();
        context.eval(source);

Hi @fniephaus

We have lowered the limit to show max 3 steps deep. This should solve the problem.

Best,
Christian

Great, thanks!

Was this page helpful?
0 / 5 - 0 ratings