Mercurial > hg > openjdk > jdk9 > nashorn
changeset 919:fd943e294985
8043232: Index selection of overloaded java new constructors
Reviewed-by: attila, hannesw, jlaskey
line wrap: on
line diff
--- a/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/internal/dynalink/beans/BeansLinker.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/internal/dynalink/beans/DynamicMethod.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/internal/dynalink/beans/StaticClassLinker.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Tue Jul 01 14:27:28 2014 -0700 +++ b/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/src/jdk/nashorn/internal/runtime/resources/Messages.properties Tue Jul 01 14:27:28 2014 -0700 +++ b/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/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/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.