mozilla / rhino

Rhino is an open-source implementation of JavaScript written entirely in Java
https://rhino.github.io
Other
4.18k stars 850 forks source link

Context.FEATURE_ENABLE_JAVA_MAP_ACCESS throws error for map with numeric keys #1314

Open jugglingcats opened 1 year ago

jugglingcats commented 1 year ago

It seems numeric keys cause an issue in the following case:

    public class MyContextFactory extends ContextFactory {
        @Override
        protected boolean hasFeature(Context cx, int featureIndex) {
            switch (featureIndex) {
                case Context.FEATURE_ENABLE_JAVA_MAP_ACCESS:
                    return true;
            }
            return super.hasFeature(cx, featureIndex);
        }
    }

    @Test
    public void testMarshallAsInteger() {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("200", "foo");

        ContextFactory contextFactory=new MyContextFactory();

        try (Context context = contextFactory.enterContext()) {
            Scriptable scope = context.initStandardObjects();
            Function fn= context.compileFunction(scope, "function(doc) { for ( var key in doc ) {java.lang.System.out.println(doc[key].toString())} }", "doc", 1, null);
            fn.call(context, scope, fn, new Object[]{map});
        }
    }

Output:

org.mozilla.javascript.EvaluatorException: Java class "java.util.HashMap" has no public instance field or method named "200". (doc#1)

    at org.mozilla.javascript.DefaultErrorReporter.runtimeError(DefaultErrorReporter.java:79)

Reading related issue #1161, I could understand that doc["200"] might be treated as doc[200] and therefore be undefined, but I don't believe this use case should throw an error.

tuchida commented 1 year ago

Since FEATURE_ENABLE_JAVA_MAP_ACCESS is broken, it is disabled by default. We have not found a way to fix this without breaking compatibility.

https://github.com/mozilla/rhino/blob/d22447c4d82c5e975770c299ced589339c0ef809/src/org/mozilla/javascript/Context.java#L330-L342

By the way, for-of is useful in this case. ref. #846

"function(doc) { for ( var key in doc ) {java.lang.System.out.println(doc[key].toString())} }"

https://github.com/mozilla/rhino/blob/d22447c4d82c5e975770c299ced589339c0ef809/testsrc/org/mozilla/javascript/tests/NativeJavaMapTest.java#L163