8043232: Index selection of overloaded java new constructors
authorsundar
Wed, 02 Jul 2014 18:10:31 +0530
changeset 25252 e8bfc909db53
parent 25251 eb2479579f1e
child 25253 61601aa79b10
8043232: Index selection of overloaded java new constructors Reviewed-by: attila, hannesw, jlaskey
nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
nashorn/src/jdk/internal/dynalink/beans/BeansLinker.java
nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java
nashorn/src/jdk/internal/dynalink/beans/DynamicMethod.java
nashorn/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java
nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
nashorn/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java
nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java
nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java
nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties
nashorn/test/script/basic/JDK-8043232.js
nashorn/test/script/basic/JDK-8043232.js.EXPECTED
--- a/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Wed Jul 02 18:10:31 2014 +0530
@@ -290,7 +290,7 @@
             return new CallerSensitiveDynamicMethod(m);
         }
         final Member member = (Member)m;
-        return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName());
+        return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName(), m instanceof Constructor);
     }
 
     /**
--- a/nashorn/src/jdk/internal/dynalink/beans/BeansLinker.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/internal/dynalink/beans/BeansLinker.java	Wed Jul 02 18:10:31 2014 +0530
@@ -169,6 +169,26 @@
     }
 
     /**
+     * Returns true if the object is a Dynalink Java constructor.
+     *
+     * @param obj the object we want to test for being a constructor
+     * @return true if it is a constructor, false otherwise.
+     */
+    public static boolean isDynamicConstructor(final Object obj) {
+        return obj instanceof DynamicMethod && ((DynamicMethod)obj).isConstructor();
+    }
+
+    /**
+     * Return the dynamic method of constructor of the given class and the given signature.
+     * @param clazz the class
+     * @param signature full signature of the constructor
+     * @return DynamicMethod for the constructor
+     */
+    public static Object getConstructorMethod(final Class<?> clazz, final String signature) {
+        return StaticClassLinker.getConstructorMethod(clazz, signature);
+    }
+
+    /**
      * Returns a collection of names of all readable instance properties of a class.
      * @param clazz the class
      * @return a collection of names of all readable instance properties of a class.
--- a/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java	Wed Jul 02 18:10:31 2014 +0530
@@ -155,4 +155,9 @@
         return StaticClassIntrospector.editConstructorMethodHandle(Lookup.unreflectConstructor(lookup,
                 (Constructor<?>)target));
     }
+
+    @Override
+    boolean isConstructor() {
+        return target instanceof Constructor;
+    }
 }
--- a/nashorn/src/jdk/internal/dynalink/beans/DynamicMethod.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/internal/dynalink/beans/DynamicMethod.java	Wed Jul 02 18:10:31 2014 +0530
@@ -147,4 +147,13 @@
     public String toString() {
         return "[" + getClass().getName() + " " + getName() + "]";
     }
+
+    /**
+     * True if this method happens to be a constructor method.
+     *
+     * @return true if this represents a constructor.
+     */
+    boolean isConstructor() {
+        return false;
+    }
 }
--- a/nashorn/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java	Wed Jul 02 18:10:31 2014 +0530
@@ -114,15 +114,30 @@
             return null;
         }
         final String operator = desc.getNameToken(CallSiteDescriptor.OPERATOR);
-        if(operator == "call") {
-            final MethodHandle invocation = ((DynamicMethod)receiver).getInvocation(
+        final DynamicMethod dynMethod = (DynamicMethod)receiver;
+        final boolean constructor = dynMethod.isConstructor();
+        final MethodHandle invocation;
+
+        if (operator == "call" && !constructor) {
+            invocation = dynMethod.getInvocation(
                     CallSiteDescriptorFactory.dropParameterTypes(desc, 0, 1), linkerServices);
-            if(invocation == null) {
+        } else if (operator == "new" && constructor) {
+            final MethodHandle ctorInvocation = dynMethod.getInvocation(desc, linkerServices);
+            if(ctorInvocation == null) {
                 return null;
             }
+
+            // Insert null for StaticClass parameter
+            invocation = MethodHandles.insertArguments(ctorInvocation, 0, (Object)null);
+        } else {
+            return null;
+        }
+
+        if (invocation != null) {
             return new GuardedInvocation(MethodHandles.dropArguments(invocation, 0,
-                    desc.getMethodType().parameterType(0)), Guards.getIdentityGuard(receiver));
+                desc.getMethodType().parameterType(0)), Guards.getIdentityGuard(receiver));
         }
+
         return null;
     }
 }
--- a/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java	Wed Jul 02 18:10:31 2014 +0530
@@ -236,6 +236,12 @@
         return false;
     }
 
+    @Override
+    public boolean isConstructor() {
+        assert !methods.isEmpty();
+        return methods.getFirst().isConstructor();
+    }
+
     ClassLoader getClassLoader() {
         return classLoader;
     }
@@ -303,6 +309,11 @@
      * @param method a method to add
      */
     public void addMethod(final SingleDynamicMethod method) {
+        assert constructorFlagConsistent(method);
         methods.add(method);
     }
+
+    private boolean constructorFlagConsistent(final SingleDynamicMethod method) {
+        return methods.isEmpty()? true : (methods.getFirst().isConstructor() == method.isConstructor());
+    }
 }
--- a/nashorn/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java	Wed Jul 02 18:10:31 2014 +0530
@@ -98,6 +98,7 @@
  */
 class SimpleDynamicMethod extends SingleDynamicMethod {
     private final MethodHandle target;
+    private final boolean constructor;
 
     /**
      * Creates a new simple dynamic method, with a name constructed from the class name, method name, and handle
@@ -108,8 +109,22 @@
      * @param name the simple name of the method
      */
     SimpleDynamicMethod(final MethodHandle target, final Class<?> clazz, final String name) {
+        this(target, clazz, name, false);
+    }
+
+    /**
+     * Creates a new simple dynamic method, with a name constructed from the class name, method name, and handle
+     * signature.
+     *
+     * @param target the target method handle
+     * @param clazz the class declaring the method
+     * @param name the simple name of the method
+     * @param constructor does this represent a constructor?
+     */
+    SimpleDynamicMethod(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) {
         super(getName(target, clazz, name));
         this.target = target;
+        this.constructor = constructor;
     }
 
     private static String getName(final MethodHandle target, final Class<?> clazz, final String name) {
@@ -130,4 +145,9 @@
     MethodHandle getTarget(final Lookup lookup) {
         return target;
     }
+
+    @Override
+    boolean isConstructor() {
+        return constructor;
+    }
 }
--- a/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java	Wed Jul 02 18:10:31 2014 +0530
@@ -160,6 +160,14 @@
             }
             return null;
         }
+
+        DynamicMethod getConstructorMethod(final String signature) {
+            return constructor.getMethodForExactParamTypes(signature);
+        }
+    }
+
+    static Object getConstructorMethod(final Class<?> clazz, final String signature) {
+        return linkers.get(clazz).getConstructorMethod(signature);
     }
 
     static Collection<String> getReadableStaticPropertyNames(final Class<?> clazz) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Wed Jul 02 18:10:31 2014 +0530
@@ -26,11 +26,13 @@
 package jdk.nashorn.internal.runtime;
 
 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.beans.BeansLinker;
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
@@ -232,6 +234,35 @@
             //ignored
         }
 
+        // Check for explicit constructor signature use
+        // Example: new (java.awt["Color(int, int,int)"])(2, 3, 4);
+        final int openBrace = propertyName.indexOf('(');
+        final int closeBrace = propertyName.lastIndexOf(')');
+        if (openBrace != -1 || closeBrace != -1) {
+            final int lastChar = propertyName.length() - 1;
+            if (openBrace == -1 || closeBrace != lastChar) {
+                throw typeError("improper.constructor.signature", propertyName);
+            }
+
+            // get the class name and try to load it
+            final String className = name + "." + propertyName.substring(0, openBrace);
+            try {
+                javaClass = context.findClass(className);
+            } catch (final NoClassDefFoundError | ClassNotFoundException e) {
+                throw typeError(e, "no.such.java.class", className);
+            }
+
+            // try to find a matching constructor
+            final Object constructor = BeansLinker.getConstructorMethod(
+                    javaClass, propertyName.substring(openBrace + 1, lastChar));
+            if (constructor != null) {
+                set(propertyName, constructor, false);
+                return constructor;
+            }
+            // we didn't find a matching constructor!
+            throw typeError("no.such.java.constructor", propertyName);
+        }
+
         final Object propertyValue;
         if (javaClass == null) {
             propertyValue = new NativeJavaPackage(fullName, getProto());
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java	Wed Jul 02 18:10:31 2014 +0530
@@ -111,6 +111,9 @@
                         MH.dropArguments(desc.getLookup().unreflect(m), 1, callType.parameterType(1)),
                         Guards.getInstanceOfGuard(m.getDeclaringClass())), linkerServices, desc);
             }
+            if(BeansLinker.isDynamicConstructor(self)) {
+                throw typeError("constructor.requires.new", ScriptRuntime.safeToString(self));
+            }
             if(BeansLinker.isDynamicMethod(self)) {
                 throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
             }
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Tue Jul 01 14:27:28 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Wed Jul 02 18:10:31 2014 +0530
@@ -92,6 +92,9 @@
 type.error.property.has.no.setter=Cannot set property "{0}" of {1} that has only a getter
 type.error.cant.set.proto.to.non.object=Cannot set Object {0}'s __proto__ to be a non-object like {1}
 type.error.no.such.function={1} has no such function "{0}"
+type.error.no.such.java.class=No such Java class: {0}
+type.error.no.such.java.constructor=No such Java constructor: {0}
+type.error.improper.constructor.signature=Java constructor signature invalid: {0}
 type.error.cant.get.property=Cannot get property "{0}" of {1}
 type.error.cant.set.property=Cannot set property "{0}" of {1}
 type.error.cant.delete.property=Cannot delete property "{0}" of {1}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8043232.js	Wed Jul 02 18:10:31 2014 +0530
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8043232: Index selection of overloaded java new constructors
+ *
+ * @test
+ * @run
+ */
+
+// call explicit constructor
+print(new (java.awt["Color(int,int,int)"])(255,0,255));
+// print the constructor itself
+print(java.awt["Color(int,int,int)"]);
+
+// store constructor to call later
+var Color = java.awt["Color(int,int,int)"];
+// call stored constructor
+print(new Color(33, 233, 2))
+
+// check if default constructor works
+var obj = new (java.lang["Object()"])();
+if (obj.class != java.lang.Object.class) {
+    fail("obj is a java.lang.Object");
+}
+
+// expected failure cases.
+function checkIt(func) {
+    try {
+        func();
+        throw new Error("should have thrown TypeError");
+    } catch(e) {
+        if (! (e instanceof TypeError)) {
+            fail("Expected TypeError, got " + e);
+        }
+        print(e);
+    }
+}
+
+// constructor of a non-existent class
+checkIt(function() new (java.lang["NonExistent(String)"])());  
+ 
+// non-existent constructor of an existing class
+checkIt(function() new (java.lang["Object(String)"])());
+
+// garbage signature string
+checkIt(function() new (java.lang["Object()xxxxx"])());
+checkIt(function() new (java.lang["Object("])());
+checkIt(function() new (java.lang["Object)"])());
+
+var System = Java.type("java.lang.System");
+// try to do 'new' on static method
+checkIt(function() new (System.getProperty)("java.version"));
+
+// try to do 'new' on an instance method
+var println = System.out.println;
+checkIt(function() new println("hello"));
+
+// call constructor as normal method (without 'new')
+checkIt(function() Color());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8043232.js.EXPECTED	Wed Jul 02 18:10:31 2014 +0530
@@ -0,0 +1,11 @@
+java.awt.Color[r=255,g=0,b=255]
+[jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)]
+java.awt.Color[r=33,g=233,b=2]
+TypeError: No such Java class: java.lang.NonExistent
+TypeError: No such Java constructor: Object(String)
+TypeError: Java constructor signature invalid: Object()xxxxx
+TypeError: Java constructor signature invalid: Object(
+TypeError: Java constructor signature invalid: Object)
+TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.lang.System.getProperty] cant be used as a constructor.
+TypeError: Java method [jdk.internal.dynalink.beans.OverloadedDynamicMethod java.io.PrintStream.println] cant be used as a constructor.
+TypeError: Constructor [jdk.internal.dynalink.beans.SimpleDynamicMethod Color java.awt.Color.java.awt.Color(int,int,int)] requires new.