8043232: Index selection of overloaded java new constructors
Reviewed-by: attila, hannesw, jlaskey
This commit is contained in:
parent
113a7f0c05
commit
27ba0956b6
nashorn
src/jdk
internal/dynalink/beans
AbstractJavaLinker.javaBeansLinker.javaCallerSensitiveDynamicMethod.javaDynamicMethod.javaDynamicMethodLinker.javaOverloadedDynamicMethod.javaSimpleDynamicMethod.javaStaticClassLinker.java
nashorn/internal/runtime
test/script/basic
@ -290,7 +290,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -168,6 +168,26 @@ public class BeansLinker implements GuardingDynamicLinker {
|
||||
return obj instanceof DynamicMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -155,4 +155,9 @@ class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
|
||||
return StaticClassIntrospector.editConstructorMethodHandle(Lookup.unreflectConstructor(lookup,
|
||||
(Constructor<?>)target));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isConstructor() {
|
||||
return target instanceof Constructor;
|
||||
}
|
||||
}
|
||||
|
@ -147,4 +147,13 @@ abstract class DynamicMethod {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -114,15 +114,30 @@ class DynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
|
||||
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;
|
||||
}
|
||||
return new GuardedInvocation(MethodHandles.dropArguments(invocation, 0,
|
||||
desc.getMethodType().parameterType(0)), Guards.getIdentityGuard(receiver));
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -236,6 +236,12 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConstructor() {
|
||||
assert !methods.isEmpty();
|
||||
return methods.getFirst().isConstructor();
|
||||
}
|
||||
|
||||
ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
@ -303,6 +309,11 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
* @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());
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,7 @@ import java.lang.invoke.MethodType;
|
||||
*/
|
||||
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 @@ class SimpleDynamicMethod extends SingleDynamicMethod {
|
||||
* @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 @@ class SimpleDynamicMethod extends SingleDynamicMethod {
|
||||
MethodHandle getTarget(final Lookup lookup) {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
}
|
||||
|
@ -160,6 +160,14 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
}
|
||||
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) {
|
||||
|
@ -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 @@ public final class NativeJavaPackage extends ScriptObject {
|
||||
//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());
|
||||
|
@ -111,6 +111,9 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
|
||||
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));
|
||||
}
|
||||
|
@ -92,6 +92,9 @@ type.error.cant.delete.property.of.undefined=Cannot delete property "{0}" of und
|
||||
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}
|
||||
|
80
nashorn/test/script/basic/JDK-8043232.js
Normal file
80
nashorn/test/script/basic/JDK-8043232.js
Normal file
@ -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());
|
11
nashorn/test/script/basic/JDK-8043232.js.EXPECTED
Normal file
11
nashorn/test/script/basic/JDK-8043232.js.EXPECTED
Normal file
@ -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.
|
Loading…
x
Reference in New Issue
Block a user