Undefined should be part of the exposed nashorn API

Art Fiedler artfiedler at gmail.com
Wed Nov 30 20:54:32 UTC 2016


@Sundararajan

I ran into an issue with a JSObject I implemented and some code in LessCSS.
The team is likely aware by now of a need to
return Undefined from JSObject's getMember() and possibly call(), and
getSlot()... You had previously asked a long time ago
why someone would want to do that and I responded here:

http://stackoverflow.com/questions/30528083/passing-undefined-to-nashorn-javascript-in-scala-java/30533897#30533897

with a comment:

Why pass undefined? Let's say your calling an existing javascript lib (like
lesscss) that
calls something like this...
    var variables = options.variables;
    // keep in mind typeof null === 'object' and typeof undefined ===
'undefined'
    if (typeof variables === 'object') {
        var keys = Object.keys(variables); //error 'null is not an Object'
    }
Now to put into perspective, 'options' is an implementation of
AbstractJSObject... how else
would you specify in getMember that the member does not exist, returning
null would throw
an error when Object.keys() is called.

I used this workaround... because in my sandbox that runs lesscss etc java
accessibility is removed and I would not be able
to make the callback...

    // Code Fragment
    if (Undefined.getUndefined() == null) {
        // Get the undefined object now, in its own script engine, to avoid
problems with permissions/security

factory.getScriptEngine().eval("Java.type('io.github.artfiedler.nashorn.Undefined').setUndefined(undefined);");
    }

    /**
     * Used to reference the Undefined value used in nashorn.
     *
     * This is a standalone class to avoid a passing a reference
     * to an object like NashornSandbox to javascript that may be
     * exploited in some way unexpected way.
     */
    public final class Undefined
    {
        private static Object undefinedObject;
        public static void setUndefined(Object undefined) { undefinedObject
= undefined; }
        /**
         * Provides and easy way to get the undefined javascript value used
by nashorn.
         * This value is not populated until after a NashornSandbox has
been initialized
         *
         * @return the internal Nashorn object used to reference undefined
         */
        public static Object getUndefined() { return undefinedObject; }
    }

Now using my Undefined class I can do the following in my
AbstractJSObject... and this will now support the existing lesscss code
without modifications.
    @Override
    public Object getMember(String name)
    {
        return spill.getOrDefault(name, Undefined.getUndefined());
    }

I know that Undefined is defined at
jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED &&
jdk.nashorn.internal.runtime.Undefined.getUndefined()
and the team may not want to expose those specific classes to the API but I
believe at least something like this should be done...

    @Exported
    public final class ScriptObjectMirror extends AbstractJSObject
implements Bindings {
        // This already exists...
        public static boolean isUndefined(Object obj) {
            return obj == ScriptRuntime.UNDEFINED;
        }
        // Add...
        public static Object getUndefined() {
            return ScriptRuntime.UNDEFINED;
        }
        ...
    }

-- or --

    @Exported
    public interface JSObject
    {
        Object Undefined = ScriptRuntime.UNDEFINED;
        ...
    }

-- or --

    @Exported
    public interface JSObject
    {
        Object Undefined = new Object() { @Override public String
toString() {return "undefined";} };
        ...
    }
    // This would require any usages of JSObject to check for
JSObject.Undefined coming out of getMember(), call(), getSlot(), eval(),
newObject()


More information about the nashorn-dev mailing list