8043232: Index selection of overloaded java new constructors
Reviewed-by: attila, hannesw, jlaskey
--- 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.