Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8141408

Nashorn's ScriptObjectMirror doesn't work with Object.prototype.hasOwnProperty

XMLWordPrintable

    • x86
    • windows_8

      FULL PRODUCT VERSION :
      java version "1.8.0_66"
      Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
      Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 10.0.10240]

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      64-bit Windows

      A DESCRIPTION OF THE PROBLEM :
      I have a program that executes Jasmine (http://jasmine.github.io/) tests in a Nashorn host. Tests are written against an API that typically returns host object wrappers. For example, I have IndexableWrapper that wraps a Collection. IndexableWrapper is a JSObject that exposes a toArray method that uses a Nashorn-created "native object factory" to create an array ultimately via Java.from.

      I want to use Jasmine to test that an array obtained by calling IndexableWrapper.toArray() is equal to some expected array. Jasmine tests array equality by iterating over the array keys and checking the ones for which Object.prototype.hasOwnProperty returns true.

      In u31, the native object factory returned a NativeArray object, for which Object.prototype.hasOwnProperty returns true for an index in the array. In u66 (and u60), the factory instead returns a ScriptObjectMirror object that fails the Object.prototype.hasOwnProperty check.

      REGRESSION. Last worked in version 8u45

      ADDITIONAL REGRESSION INFORMATION:
      java version "1.8.0_31"
      Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
      Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Compile and run the program included under "Source code for an executable test case".

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      1.8.0_31
      Native array class: jdk.nashorn.internal.objects.NativeArray
      Testing the array...
      Key: 0
      Has: true
      ACTUAL -
      1.8.0_66
      Native array class: jdk.nashorn.api.scripting.ScriptObjectMirror
      Testing the array...
      Key: 0
      Has: false

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import javax.script.*;
      import java.util.*;
      import jdk.nashorn.api.scripting.*;

      public class HasOwnPropertyBug {
        public static void main(String[] args) throws Exception {
          System.out.println(System.getProperty("java.version"));
          
          ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
          Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
          
          // The collection that will be turned into an array
          Collection<String> aCollection = new ArrayList<String>();
          aCollection.add("test");
          
          // The "native object" factory - necessary since we cannot call Java.from from host code.
          Object factoryObject = engine.eval(
            "var factory = {" +
            " toArray: function(coll) { return Java.from(coll); }" +
            "};" +
            "factory");
          NativeFactory factory = ((Invocable) engine).getInterface(factoryObject, NativeFactory.class);

          // Create a "receiver" - an object that will receive a function from JavaScript and immediately
          // invokes it with the wrapped collection as argument.
          Receiver receiver = new Receiver() {
            public void provideFunction(AFunction theFunction) {
              theFunction.call(new CollectionWrapper(aCollection, factory));
            }
          };
          bindings.put("receiver", receiver);
          
          // Note: The for loop is is essentially what Jasmine does when testing for equality.
          engine.eval(
            "receiver.provideFunction(function (wrapper) {" +
            " var array = wrapper.toArray();" +
            " print('Testing the array...');" +
            " for (var key in array) {" +
            " print('Key: ' + key);" +
            " print('Has: ' + Object.prototype.hasOwnProperty.call(array, key));" +
            " }" +
            "});");
        }
        
        public static interface NativeFactory {
          Object toArray(Collection<?> aCollection);
        }
        
        public static interface Receiver {
          void provideFunction(AFunction theFunction);
        }
        
        public static interface AFunction {
          void call(Object arg);
        }
        
        public static class CollectionWrapper extends AbstractJSObject {
          private Collection<?> aCollection;
          private NativeFactory nativeFactory;
          public CollectionWrapper(Collection<?> aCollection, NativeFactory nativeFactory) {
            this.aCollection = aCollection;
            this.nativeFactory = nativeFactory;
          }
          public Object getMember(String name) {
            if ("toArray".equals(name)) {
              Object nativeArray = nativeFactory.toArray(aCollection);
              System.out.println("Native array class: " + nativeArray.getClass().getName());
              return valueFn(nativeArray);
            }
            return null;
          }
          
          private JSObject valueFn(Object value) {
            return new AbstractJSObject() {
              public Object call(Object thiz, Object... args) {
                return value;
              }
            };
          }
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Patch Jasmine's equality matching to manually copy the contents from the ScriptObjectMirror-array to a real native array.

            sundar Sundararajan Athijegannathan
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: