8010946: AccessControl.doPrivileged is broken when called from js script
Reviewed-by: jlaskey, sundar
--- a/nashorn/make/build.xml Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/make/build.xml Wed Jul 03 12:39:28 2013 +0200
@@ -235,44 +235,31 @@
</target>
<target name="generate-policy-file" depends="prepare">
- <!-- Generating nashorn.policy file -->
+ <echo file="${build.dir}/nashorn.policy">
- <!-- nashorn internal tests jar requires AllPermission -->
- <echo message="grant codeBase "file:/${basedir}/${nashorn.internal.tests.jar}" {" file="${build.dir}/nashorn.policy"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message=" permission java.security.AllPermission;" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="};" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
+grant codeBase "file:/${basedir}/${nashorn.internal.tests.jar}" {
+ permission java.security.AllPermission;
+};
- <!-- TestNG framework jar needs AllPermission -->
- <echo message="grant codeBase "file:/${basedir}/${file.reference.testng.jar}" {" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message=" permission java.security.AllPermission;" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="};" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
+grant codeBase "file:/${basedir}/${file.reference.testng.jar}" {
+ permission java.security.AllPermission;
+};
- <!-- AllPermission to test/script/trusted tests -->
- <echo message="grant codeBase "file:/${basedir}/test/script/trusted/*" {" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message=" permission java.security.AllPermission;" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="};" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
+grant codeBase "file:/${basedir}/test/script/trusted/*" {
+ permission java.security.AllPermission;
+};
- <echo message="grant codeBase "file:/${basedir}/test/script/basic/*" {" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
- <!-- test/script/basic .js scripts load other script tests -->
- <echo message=" permission java.io.FilePermission "${basedir}/test/script/-", "read";" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message=" permission java.io.FilePermission "user.dir", "read";" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message=" permission java.util.PropertyPermission "user.dir", "read";" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
- <!-- test/script/basic .js scripts can read nashorn.test.* properties -->
- <echo message=" permission java.util.PropertyPermission "nashorn.test.*", "read";" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="};" file="${build.dir}/nashorn.policy" append="true"/>
- <echo message="" file="${build.dir}/nashorn.policy" append="true"/>
+grant codeBase "file:/${basedir}/test/script/basic/*" {
+ permission java.io.FilePermission "${basedir}/test/script/-", "read";
+ permission java.io.FilePermission "$${user.dir}", "read";
+ permission java.util.PropertyPermission "user.dir", "read";
+ permission java.util.PropertyPermission "nashorn.test.*", "read";
+};
+
+grant codeBase "file:/${basedir}/test/script/basic/JDK-8010946-privileged.js" {
+ permission java.util.PropertyPermission "java.security.policy", "read";
+};
+ </echo>
<replace file="${build.dir}/nashorn.policy"><replacetoken>\</replacetoken><replacevalue>/</replacevalue></replace> <!--hack for Windows - to make URLs with normal path separators -->
<replace file="${build.dir}/nashorn.policy"><replacetoken>//</replacetoken><replacevalue>/</replacevalue></replace> <!--hack for Unix - to avoid leading // in URLs -->
--- a/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Jul 03 12:39:28 2013 +0200
@@ -86,7 +86,10 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
@@ -109,10 +112,11 @@
* @author Attila Szegedi
*/
abstract class AbstractJavaLinker implements GuardingDynamicLinker {
+
final Class<?> clazz;
private final MethodHandle classGuard;
private final MethodHandle assignableGuard;
- private final Map<String, AnnotatedMethodHandle> propertyGetters = new HashMap<>();
+ private final Map<String, AnnotatedDynamicMethod> propertyGetters = new HashMap<>();
private final Map<String, DynamicMethod> propertySetters = new HashMap<>();
private final Map<String, DynamicMethod> methods = new HashMap<>();
@@ -129,22 +133,19 @@
// Add methods and properties
for(Method method: introspector.getMethods()) {
final String name = method.getName();
- final MethodHandle methodHandle = introspector.unreflect(method);
// Add method
- addMember(name, methodHandle, methods);
+ addMember(name, method, methods);
// Add the method as a property getter and/or setter
if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) {
// Property getter
- setPropertyGetter(decapitalize(name.substring(3)), introspector.unreflect(
- getMostGenericGetter(method)), ValidationType.INSTANCE_OF);
+ setPropertyGetter(method, 3);
} else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 &&
method.getReturnType() == boolean.class) {
// Boolean property getter
- setPropertyGetter(decapitalize(name.substring(2)), introspector.unreflect(
- getMostGenericGetter(method)), ValidationType.INSTANCE_OF);
+ setPropertyGetter(method, 2);
} else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) {
// Property setter
- addMember(decapitalize(name.substring(3)), methodHandle, propertySetters);
+ addMember(decapitalize(name.substring(3)), method, propertySetters);
}
}
@@ -156,7 +157,8 @@
setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS);
}
if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) {
- addMember(name, introspector.unreflectSetter(field), propertySetters);
+ addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name),
+ propertySetters);
}
}
@@ -192,38 +194,119 @@
abstract FacetIntrospector createFacetIntrospector();
- void setPropertyGetter(String name, MethodHandle handle, ValidationType validationType) {
- propertyGetters.put(name, new AnnotatedMethodHandle(handle, validationType));
+ /**
+ * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only
+ * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
+ * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
+ * instead.
+ * @param name name of the property
+ * @param handle the method handle that implements the property getter
+ * @param validationType the validation type for the property
+ */
+ private void setPropertyGetter(String name, SingleDynamicMethod handle, ValidationType validationType) {
+ propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType));
}
- private void addMember(String name, MethodHandle mh, Map<String, DynamicMethod> methodMap) {
+ /**
+ * Sets the specified reflective method to be the property getter for the specified property.
+ * @param getter the getter method
+ * @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for
+ * names starting with "is".
+ */
+ private void setPropertyGetter(Method getter, int prefixLen) {
+ setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod(
+ getMostGenericGetter(getter)), ValidationType.INSTANCE_OF);
+ }
+
+ /**
+ * Sets the specified method handle to be the property getter for the specified property. Note that you can only
+ * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
+ * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
+ * instead.
+ * @param name name of the property
+ * @param handle the method handle that implements the property getter
+ * @param validationType the validation type for the property
+ */
+ void setPropertyGetter(String name, MethodHandle handle, ValidationType validationType) {
+ setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType);
+ }
+
+ private void addMember(String name, AccessibleObject ao, Map<String, DynamicMethod> methodMap) {
+ addMember(name, createDynamicMethod(ao), methodMap);
+ }
+
+ private void addMember(String name, SingleDynamicMethod method, Map<String, DynamicMethod> methodMap) {
final DynamicMethod existingMethod = methodMap.get(name);
- final DynamicMethod newMethod = addMember(mh, existingMethod, clazz, name);
+ final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name);
if(newMethod != existingMethod) {
methodMap.put(name, newMethod);
}
}
- static DynamicMethod createDynamicMethod(Iterable<MethodHandle> methodHandles, Class<?> clazz, String name) {
+ /**
+ * Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The
+ * methods should represent all overloads of the same name (or all constructors of the class).
+ * @param members the reflective members
+ * @param clazz the class declaring the reflective members
+ * @param name the common name of the reflective members.
+ * @return a dynamic method representing all the specified reflective members.
+ */
+ static DynamicMethod createDynamicMethod(Iterable<? extends AccessibleObject> members, Class<?> clazz, String name) {
DynamicMethod dynMethod = null;
- for(MethodHandle methodHandle: methodHandles) {
- dynMethod = addMember(methodHandle, dynMethod, clazz, name);
+ for(AccessibleObject method: members) {
+ dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name);
}
return dynMethod;
}
- private static DynamicMethod addMember(MethodHandle mh, DynamicMethod existing, Class<?> clazz, String name) {
+ /**
+ * Given a reflective method or a constructor, creates a dynamic method that represents it. This method will
+ * distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive
+ * dynamic method when needed.
+ * @param m the reflective member
+ * @return the single dynamic method representing the reflective member
+ */
+ private static SingleDynamicMethod createDynamicMethod(AccessibleObject m) {
+ if(CallerSensitiveDetector.isCallerSensitive(m)) {
+ return new CallerSensitiveDynamicMethod(m);
+ }
+ final Member member = (Member)m;
+ return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName());
+ }
+
+ /**
+ * Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be
+ * only used for methods and constructors that are not caller sensitive. If a caller sensitive method were
+ * unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege
+ * unreflector as its caller, and thus completely useless.
+ * @param m the method or constructor
+ * @return the method handle
+ */
+ private static MethodHandle unreflectSafely(AccessibleObject m) {
+ if(m instanceof Method) {
+ final Method reflMethod = (Method)m;
+ final MethodHandle handle = SafeUnreflector.unreflect(reflMethod);
+ if(Modifier.isStatic(reflMethod.getModifiers())) {
+ return StaticClassIntrospector.editStaticMethodHandle(handle);
+ }
+ return handle;
+ }
+ return StaticClassIntrospector.editConstructorMethodHandle(SafeUnreflector.unreflectConstructor(
+ (Constructor<?>)m));
+ }
+
+ private static DynamicMethod mergeMethods(SingleDynamicMethod method, DynamicMethod existing, Class<?> clazz, String name) {
if(existing == null) {
- return new SimpleDynamicMethod(mh, clazz, name);
- } else if(existing.contains(mh)) {
+ return method;
+ } else if(existing.contains(method)) {
return existing;
- } else if(existing instanceof SimpleDynamicMethod) {
+ } else if(existing instanceof SingleDynamicMethod) {
final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name);
- odm.addMethod(((SimpleDynamicMethod)existing));
- odm.addMethod(mh);
+ odm.addMethod(((SingleDynamicMethod)existing));
+ odm.addMethod(method);
return odm;
} else if(existing instanceof OverloadedDynamicMethod) {
- ((OverloadedDynamicMethod)existing).addMethod(mh);
+ ((OverloadedDynamicMethod)existing).addMethod(method);
return existing;
}
throw new AssertionError();
@@ -296,7 +379,7 @@
private GuardedInvocation getCallPropWithThis(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) {
switch(callSiteDescriptor.getNameTokenCount()) {
case 3: {
- return createGuardedDynamicMethodInvocation(callSiteDescriptor.getMethodType(), linkerServices,
+ return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods);
}
default: {
@@ -305,16 +388,16 @@
}
}
- private GuardedInvocation createGuardedDynamicMethodInvocation(MethodType callSiteType,
+ private GuardedInvocation createGuardedDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor,
LinkerServices linkerServices, String methodName, Map<String, DynamicMethod> methodMap){
- final MethodHandle inv = getDynamicMethodInvocation(callSiteType, linkerServices, methodName, methodMap);
- return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteType));
+ final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
+ return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));
}
- private static MethodHandle getDynamicMethodInvocation(MethodType callSiteType, LinkerServices linkerServices,
- String methodName, Map<String, DynamicMethod> methodMap) {
+ private static MethodHandle getDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor,
+ LinkerServices linkerServices, String methodName, Map<String, DynamicMethod> methodMap) {
final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
- return dynaMethod != null ? dynaMethod.getInvocation(callSiteType, linkerServices) : null;
+ return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;
}
private static DynamicMethod getDynamicMethod(String methodName, Map<String, DynamicMethod> methodMap) {
@@ -322,13 +405,13 @@
return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
}
- private static SimpleDynamicMethod getExplicitSignatureDynamicMethod(String methodName,
+ private static SingleDynamicMethod getExplicitSignatureDynamicMethod(String methodName,
Map<String, DynamicMethod> methodsMap) {
// What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
// to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
// resolution works correctly in almost every situation. However, in presence of many language-specific
// conversions with a radically dynamic language, most overloaded methods will end up being constantly selected
- // at invocation time, so a programmer knowledgable of the situation might choose to pin down an exact overload
+ // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload
// for performance reasons.
// Is the method name lexically of the form "name(types)"?
@@ -377,8 +460,8 @@
final MethodType setterType = type.dropParameterTypes(1, 2);
// Bind property setter handle to the expected setter type and linker services. Type is
// MethodHandle(Object, String, Object)
- final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0, setterType,
- linkerServices);
+ final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
+ CallSiteDescriptorFactory.dropParameterTypes(callSiteDescriptor, 1, 2), linkerServices);
// Cast getter to MethodHandle(O, N, V)
final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
@@ -415,9 +498,8 @@
case 3: {
// Must have two arguments: target object and property value
assertParameterCount(callSiteDescriptor, 2);
- final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor.getMethodType(),
- linkerServices, callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND),
- propertySetters);
+ final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
+ callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), propertySetters);
// If we have a property setter with this name, this composite operation will always stop here
if(gi != null) {
return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
@@ -435,14 +517,13 @@
private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());
- private static final MethodHandle IS_ANNOTATED_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
- boolean.class, AnnotatedMethodHandle.class));
- private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_HANDLE = MethodHandles.dropArguments(
- MethodHandles.constant(Object.class, null), 0, AnnotatedMethodHandle.class);
- private static final MethodHandle GET_ANNOTATED_HANDLE = privateLookup.findGetter(AnnotatedMethodHandle.class,
- "handle", MethodHandle.class);
- private static final MethodHandle GENERIC_PROPERTY_GETTER_HANDLER_INVOKER = MethodHandles.filterArguments(
- MethodHandles.invoker(MethodType.methodType(Object.class, Object.class)), 0, GET_ANNOTATED_HANDLE);
+ private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
+ boolean.class, AnnotatedDynamicMethod.class));
+ private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(
+ MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);
+ private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,
+ "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class));
+ private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
LinkerServices linkerServices, List<String> ops) throws Exception {
@@ -455,16 +536,20 @@
// What's below is basically:
// foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
// only with a bunch of method signature adjustments. Basically, retrieve method getter
- // AnnotatedMethodHandle; if it is non-null, invoke its "handle" field, otherwise either return null,
+ // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,
// or delegate to next component's invocation.
final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
- AnnotatedMethodHandle.class));
- // Object(AnnotatedMethodHandle, Object)->R(AnnotatedMethodHandle, T0)
- final MethodHandle invokeHandleTyped = linkerServices.asType(GENERIC_PROPERTY_GETTER_HANDLER_INVOKER,
- MethodType.methodType(type.returnType(), AnnotatedMethodHandle.class, type.parameterType(0)));
+ AnnotatedDynamicMethod.class));
+ final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
+ GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
+ final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
+ callSiteBoundMethodGetter);
+ // Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0)
+ final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
+ MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
// Since it's in the target of a fold, drop the unnecessary second argument
- // R(AnnotatedMethodHandle, T0)->R(AnnotatedMethodHandle, T0, T1)
+ // R(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
type.parameterType(1));
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
@@ -472,19 +557,19 @@
final MethodHandle fallbackFolded;
if(nextComponent == null) {
- // Object(AnnotatedMethodHandle)->R(AnnotatedMethodHandle, T0, T1); returns constant null
- fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_HANDLE, 1,
- type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedMethodHandle.class));
+ // Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null
+ fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
+ type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
} else {
- // R(T0, T1)->R(AnnotatedMethodHAndle, T0, T1); adapts the next component's invocation to drop the
+ // R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the
// extra argument resulting from fold
fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
- 0, AnnotatedMethodHandle.class);
+ 0, AnnotatedDynamicMethod.class);
}
- // fold(R(AnnotatedMethodHandle, T0, T1), AnnotatedMethodHandle(T0, T1))
+ // fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
- IS_ANNOTATED_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
+ IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
if(nextComponent == null) {
return getClassGuardedInvocationComponent(compositeGetter, type);
}
@@ -494,13 +579,13 @@
// Must have exactly one argument: receiver
assertParameterCount(callSiteDescriptor, 1);
// Fixed name
- final AnnotatedMethodHandle annGetter = propertyGetters.get(callSiteDescriptor.getNameToken(
+ final AnnotatedDynamicMethod annGetter = propertyGetters.get(callSiteDescriptor.getNameToken(
CallSiteDescriptor.NAME_OPERAND));
if(annGetter == null) {
// We have no such property, always delegate to the next component operation
return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
}
- final MethodHandle getter = annGetter.handle;
+ final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices);
// NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
// overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
// method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
@@ -508,6 +593,7 @@
// NOTE: No delegation to the next component operation if we have a property with this name, even if its
// value is null.
final ValidationType validationType = annGetter.validationType;
+ // TODO: we aren't using the type that declares the most generic getter here!
return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType,
type), clazz, validationType);
}
@@ -623,14 +709,15 @@
// args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is
// a typical property setter with variable name signature (target, name, value).
private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments(
- privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, MethodType.class,
+ privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class,
LinkerServices.class, Object.class), 3, Object.class), 5, Object.class);
// Type is MethodHandle(MethodType, LinkerServices, Object, String, Object)
private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);
@SuppressWarnings("unused")
- private MethodHandle getPropertySetterHandle(MethodType setterType, LinkerServices linkerServices, Object id) {
- return getDynamicMethodInvocation(setterType, linkerServices, String.valueOf(id), propertySetters);
+ private MethodHandle getPropertySetterHandle(CallSiteDescriptor setterDescriptor, LinkerServices linkerServices,
+ Object id) {
+ return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters);
}
private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
@@ -689,13 +776,24 @@
return null;
}
- private static final class AnnotatedMethodHandle {
- final MethodHandle handle;
+ private static final class AnnotatedDynamicMethod {
+ private final SingleDynamicMethod method;
/*private*/ final ValidationType validationType;
- AnnotatedMethodHandle(MethodHandle handle, ValidationType validationType) {
- this.handle = handle;
+ AnnotatedDynamicMethod(SingleDynamicMethod method, ValidationType validationType) {
+ this.method = method;
this.validationType = validationType;
}
+
+ MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) {
+ return method.getInvocation(callSiteDescriptor, linkerServices);
+ }
+
+ @SuppressWarnings("unused")
+ MethodHandle getTarget(MethodHandles.Lookup lookup) {
+ MethodHandle inv = method.getTarget(lookup);
+ assert inv != null;
+ return inv;
+ }
}
}
--- a/nashorn/src/jdk/internal/dynalink/beans/ApplicableOverloadedMethods.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/ApplicableOverloadedMethods.java Wed Jul 03 12:39:28 2013 +0200
@@ -83,7 +83,6 @@
package jdk.internal.dynalink.beans;
-import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.LinkedList;
import java.util.List;
@@ -95,7 +94,7 @@
* @author Attila Szegedi
*/
class ApplicableOverloadedMethods {
- private final List<MethodHandle> methods;
+ private final List<SingleDynamicMethod> methods;
private final boolean varArgs;
/**
@@ -106,10 +105,10 @@
* @param test applicability test. One of {@link #APPLICABLE_BY_SUBTYPING},
* {@link #APPLICABLE_BY_METHOD_INVOCATION_CONVERSION}, or {@link #APPLICABLE_BY_VARIABLE_ARITY}.
*/
- ApplicableOverloadedMethods(final List<MethodHandle> methods, final MethodType callSiteType,
+ ApplicableOverloadedMethods(final List<SingleDynamicMethod> methods, final MethodType callSiteType,
final ApplicabilityTest test) {
this.methods = new LinkedList<>();
- for(MethodHandle m: methods) {
+ for(SingleDynamicMethod m: methods) {
if(test.isApplicable(callSiteType, m)) {
this.methods.add(m);
}
@@ -122,7 +121,7 @@
*
* @return list of all methods.
*/
- List<MethodHandle> getMethods() {
+ List<SingleDynamicMethod> getMethods() {
return methods;
}
@@ -131,12 +130,12 @@
*
* @return a list of maximally specific methods.
*/
- List<MethodHandle> findMaximallySpecificMethods() {
+ List<SingleDynamicMethod> findMaximallySpecificMethods() {
return MaximallySpecific.getMaximallySpecificMethods(methods, varArgs);
}
abstract static class ApplicabilityTest {
- abstract boolean isApplicable(MethodType callSiteType, MethodHandle method);
+ abstract boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method);
}
/**
@@ -144,8 +143,8 @@
*/
static final ApplicabilityTest APPLICABLE_BY_SUBTYPING = new ApplicabilityTest() {
@Override
- boolean isApplicable(MethodType callSiteType, MethodHandle method) {
- final MethodType methodType = method.type();
+ boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method) {
+ final MethodType methodType = method.getMethodType();
final int methodArity = methodType.parameterCount();
if(methodArity != callSiteType.parameterCount()) {
return false;
@@ -166,8 +165,8 @@
*/
static final ApplicabilityTest APPLICABLE_BY_METHOD_INVOCATION_CONVERSION = new ApplicabilityTest() {
@Override
- boolean isApplicable(MethodType callSiteType, MethodHandle method) {
- final MethodType methodType = method.type();
+ boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method) {
+ final MethodType methodType = method.getMethodType();
final int methodArity = methodType.parameterCount();
if(methodArity != callSiteType.parameterCount()) {
return false;
@@ -189,11 +188,11 @@
*/
static final ApplicabilityTest APPLICABLE_BY_VARIABLE_ARITY = new ApplicabilityTest() {
@Override
- boolean isApplicable(MethodType callSiteType, MethodHandle method) {
- if(!method.isVarargsCollector()) {
+ boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method) {
+ if(!method.isVarArgs()) {
return false;
}
- final MethodType methodType = method.type();
+ final MethodType methodType = method.getMethodType();
final int methodArity = methodType.parameterCount();
final int fixArity = methodArity - 1;
final int callSiteArity = callSiteType.parameterCount();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDetector.java Wed Jul 03 12:39:28 2013 +0200
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2010, 2013, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+ Copyright 2009-2013 Attila Szegedi
+
+ Licensed under both the Apache License, Version 2.0 (the "Apache License")
+ and the BSD License (the "BSD License"), with licensee being free to
+ choose either of the two at their discretion.
+
+ You may not use this file except in compliance with either the Apache
+ License or the BSD License.
+
+ If you choose to use this file in compliance with the Apache License, the
+ following notice applies to you:
+
+ You may obtain a copy of the Apache License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+ If you choose to use this file in compliance with the BSD License, the
+ following notice applies to you:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the names of
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink.beans;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+import sun.reflect.CallerSensitive;
+
+/**
+ * Utility class that determines if a method or constructor is caller sensitive. It actually encapsulates two different
+ * strategies for determining caller sensitivity; a more robust one that works if Dynalink runs as code with access
+ * to {@code sun.reflect} package, and an unprivileged one that is used when Dynalink doesn't have access to that
+ * package. Note that even the unprivileged strategy is ordinarily robust, but it relies on the {@code toString} method
+ * of the annotation. If an attacker were to use a different annotation to spoof the string representation of the
+ * {@code CallerSensitive} annotation, they could designate their own methods as caller sensitive. This however does not
+ * escalate privileges, only causes Dynalink to never cache method handles for such methods, so all it would do would
+ * decrease the performance in linking such methods. In the opposite case when an attacker could trick Dynalink into not
+ * recognizing genuine {@code CallerSensitive} annotations, Dynalink would treat caller sensitive methods as ordinary
+ * methods, and would cache them bound to a zero-privilege delegate as the caller (just what Dynalink did before it
+ * could handle caller-sensitive methods). That would practically render caller-sensitive methods exposed through
+ * Dynalink unusable, but again, can not lead to any privilege escalations. Therefore, even the less robust unprivileged
+ * strategy is safe; the worst thing a successful attack against it can achieve is slight reduction in Dynalink-exposed
+ * functionality or performance.
+ */
+public class CallerSensitiveDetector {
+
+ private static final DetectionStrategy DETECTION_STRATEGY = getDetectionStrategy();
+
+ static boolean isCallerSensitive(AccessibleObject ao) {
+ return DETECTION_STRATEGY.isCallerSensitive(ao);
+ }
+
+ private static DetectionStrategy getDetectionStrategy() {
+ try {
+ return new PrivilegedDetectionStrategy();
+ } catch(Throwable t) {
+ return new UnprivilegedDetectionStrategy();
+ }
+ }
+
+ private abstract static class DetectionStrategy {
+ abstract boolean isCallerSensitive(AccessibleObject ao);
+ }
+
+ private static class PrivilegedDetectionStrategy extends DetectionStrategy {
+ private static final Class<? extends Annotation> CALLER_SENSITIVE_ANNOTATION_CLASS = CallerSensitive.class;
+
+ @Override
+ boolean isCallerSensitive(AccessibleObject ao) {
+ return ao.getAnnotation(CALLER_SENSITIVE_ANNOTATION_CLASS) != null;
+ }
+ }
+
+ private static class UnprivilegedDetectionStrategy extends DetectionStrategy {
+ private static final String CALLER_SENSITIVE_ANNOTATION_STRING = "@sun.reflect.CallerSensitive()";
+
+ @Override
+ boolean isCallerSensitive(AccessibleObject o) {
+ for(Annotation a: o.getAnnotations()) {
+ if(String.valueOf(a).equals(CALLER_SENSITIVE_ANNOTATION_STRING)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Wed Jul 03 12:39:28 2013 +0200
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2010, 2013, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+ Copyright 2009-2013 Attila Szegedi
+
+ Licensed under both the Apache License, Version 2.0 (the "Apache License")
+ and the BSD License (the "BSD License"), with licensee being free to
+ choose either of the two at their discretion.
+
+ You may not use this file except in compliance with either the Apache
+ License or the BSD License.
+
+ If you choose to use this file in compliance with the Apache License, the
+ following notice applies to you:
+
+ You may obtain a copy of the Apache License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+ If you choose to use this file in compliance with the BSD License, the
+ following notice applies to you:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the names of
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink.beans;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import jdk.internal.dynalink.support.Lookup;
+
+/**
+ * A dynamic method bound to exactly one Java method or constructor that is caller sensitive. Since the target method is
+ * caller sensitive, it doesn't cache a method handle but rather uses the passed lookup object in
+ * {@link #getTarget(java.lang.invoke.MethodHandles.Lookup)} to unreflect a method handle from the reflective member on
+ * every request.
+ *
+ * @author Attila Szegedi
+ */
+class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
+ // Typed as "AccessibleObject" as it can be either a method or a constructor.
+ // If we were Java8-only, we could use java.lang.reflect.Executable
+ private final AccessibleObject target;
+ private final MethodType type;
+
+ public CallerSensitiveDynamicMethod(AccessibleObject target) {
+ super(getName(target));
+ this.target = target;
+ this.type = getMethodType(target);
+ }
+
+ private static String getName(AccessibleObject target) {
+ final Member m = (Member)target;
+ return getMethodNameWithSignature(getMethodType(target), getClassAndMethodName(m.getDeclaringClass(),
+ m.getName()));
+ }
+
+ @Override
+ MethodType getMethodType() {
+ return type;
+ }
+
+ private static MethodType getMethodType(AccessibleObject ao) {
+ final boolean isMethod = ao instanceof Method;
+ final Class<?> rtype = isMethod ? ((Method)ao).getReturnType() : ((Constructor<?>)ao).getDeclaringClass();
+ final Class<?>[] ptypes = isMethod ? ((Method)ao).getParameterTypes() : ((Constructor<?>)ao).getParameterTypes();
+ final MethodType type = MethodType.methodType(rtype, ptypes);
+ final Member m = (Member)ao;
+ return type.insertParameterTypes(0,
+ isMethod ?
+ Modifier.isStatic(m.getModifiers()) ?
+ Object.class :
+ m.getDeclaringClass() :
+ StaticClass.class);
+ }
+
+ @Override
+ boolean isVarArgs() {
+ return target instanceof Method ? ((Method)target).isVarArgs() : ((Constructor<?>)target).isVarArgs();
+ }
+
+ @Override
+ MethodHandle getTarget(MethodHandles.Lookup lookup) {
+ if(target instanceof Method) {
+ final MethodHandle mh = Lookup.unreflect(lookup, (Method)target);
+ if(Modifier.isStatic(((Member)target).getModifiers())) {
+ return StaticClassIntrospector.editStaticMethodHandle(mh);
+ }
+ return mh;
+ }
+ return StaticClassIntrospector.editConstructorMethodHandle(Lookup.unreflectConstructor(lookup,
+ (Constructor<?>)target));
+ }
+}
--- a/nashorn/src/jdk/internal/dynalink/beans/ClassString.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/ClassString.java Wed Jul 03 12:39:28 2013 +0200
@@ -155,8 +155,8 @@
}
List<MethodHandle> getMaximallySpecifics(List<MethodHandle> methods, LinkerServices linkerServices, boolean varArg) {
- return MaximallySpecific.getMaximallySpecificMethods(getApplicables(methods, linkerServices, varArg), varArg,
- classes, linkerServices);
+ return MaximallySpecific.getMaximallySpecificMethodHandles(getApplicables(methods, linkerServices, varArg),
+ varArg, classes, linkerServices);
}
/**
--- a/nashorn/src/jdk/internal/dynalink/beans/DynamicMethod.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/DynamicMethod.java Wed Jul 03 12:39:28 2013 +0200
@@ -84,8 +84,7 @@
package jdk.internal.dynalink.beans;
import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodType;
-import java.util.StringTokenizer;
+import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.LinkerServices;
/**
@@ -116,45 +115,28 @@
* is a variable arguments (vararg) method, it will pack the extra arguments in an array before the invocation of
* the underlying method if it is not already done.
*
- * @param callSiteType the method type at a call site
+ * @param callSiteDescriptor the descriptor of the call site
* @param linkerServices linker services. Used for language-specific type conversions.
* @return an invocation suitable for calling the method from the specified call site.
*/
- abstract MethodHandle getInvocation(MethodType callSiteType, LinkerServices linkerServices);
+ abstract MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices);
/**
- * Returns a simple dynamic method representing a single underlying Java method (possibly selected among several
+ * Returns a single dynamic method representing a single underlying Java method (possibly selected among several
* overloads) with formal parameter types exactly matching the passed signature.
* @param paramTypes the comma-separated list of requested parameter type names. The names will match both
* qualified and unqualified type names.
- * @return a simple dynamic method representing a single underlying Java method, or null if none of the Java methods
+ * @return a single dynamic method representing a single underlying Java method, or null if none of the Java methods
* behind this dynamic method exactly match the requested parameter types.
*/
- abstract SimpleDynamicMethod getMethodForExactParamTypes(String paramTypes);
+ abstract SingleDynamicMethod getMethodForExactParamTypes(String paramTypes);
/**
- * True if this dynamic method already contains a method handle with an identical signature as the passed in method
- * handle.
- * @param mh the method handle to check
- * @return true if it already contains an equivalent method handle.
+ * True if this dynamic method already contains a method with an identical signature as the passed in method.
+ * @param method the method to check
+ * @return true if it already contains an equivalent method.
*/
- abstract boolean contains(MethodHandle mh);
-
- static boolean typeMatchesDescription(String paramTypes, MethodType type) {
- final StringTokenizer tok = new StringTokenizer(paramTypes, ", ");
- for(int i = 1; i < type.parameterCount(); ++i) { // i = 1 as we ignore the receiver
- if(!(tok.hasMoreTokens() && typeNameMatches(tok.nextToken(), type.parameterType(i)))) {
- return false;
- }
- }
- return !tok.hasMoreTokens();
- }
-
- private static boolean typeNameMatches(String typeName, Class<?> type) {
- final int lastDot = typeName.lastIndexOf('.');
- final String fullTypeName = type.getCanonicalName();
- return lastDot != -1 && fullTypeName.endsWith(typeName.substring(lastDot)) || typeName.equals(fullTypeName);
- }
+ abstract boolean contains(SingleDynamicMethod method);
static String getClassAndMethodName(Class<?> clazz, String name) {
final String clazzName = clazz.getCanonicalName();
--- a/nashorn/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java Wed Jul 03 12:39:28 2013 +0200
@@ -85,12 +85,12 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
+import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.internal.dynalink.support.Guards;
/**
@@ -110,19 +110,18 @@
return null;
}
final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
- if(desc.getNameTokenCount() != 2 && desc.getNameToken(CallSiteDescriptor.SCHEME) != "dyn") {
+ if(desc.getNameTokenCount() != 2 && desc.getNameToken(CallSiteDescriptor.SCHEME) != "dyn") {
return null;
}
final String operator = desc.getNameToken(CallSiteDescriptor.OPERATOR);
if(operator == "call") {
- final MethodType type = desc.getMethodType();
- final MethodHandle invocation = ((DynamicMethod)receiver).getInvocation(type.dropParameterTypes(0, 1),
- linkerServices);
+ final MethodHandle invocation = ((DynamicMethod)receiver).getInvocation(
+ CallSiteDescriptorFactory.dropParameterTypes(desc, 0, 1), linkerServices);
if(invocation == null) {
return null;
}
- return new GuardedInvocation(MethodHandles.dropArguments(invocation, 0, type.parameterType(0)),
- Guards.getIdentityGuard(receiver));
+ return new GuardedInvocation(MethodHandles.dropArguments(invocation, 0,
+ desc.getMethodType().parameterType(0)), Guards.getIdentityGuard(receiver));
}
return null;
}
--- a/nashorn/src/jdk/internal/dynalink/beans/FacetIntrospector.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/FacetIntrospector.java Wed Jul 03 12:39:28 2013 +0200
@@ -167,10 +167,6 @@
return editMethodHandle(SafeUnreflector.unreflectSetter(field));
}
- MethodHandle unreflect(Method method) {
- return editMethodHandle(SafeUnreflector.unreflect(method));
- }
-
/**
* Returns an edited method handle. A facet might need to edit an unreflected method handle before it is usable with
* the facet. By default, returns the passed method handle unchanged. The class' static facet will introduce a
--- a/nashorn/src/jdk/internal/dynalink/beans/MaximallySpecific.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/MaximallySpecific.java Wed Jul 03 12:39:28 2013 +0200
@@ -105,10 +105,58 @@
* @param varArgs whether to assume the methods are varargs
* @return the list of maximally specific methods.
*/
- static List<MethodHandle> getMaximallySpecificMethods(List<MethodHandle> methods, boolean varArgs) {
- return getMaximallySpecificMethods(methods, varArgs, null, null);
+ static List<SingleDynamicMethod> getMaximallySpecificMethods(List<SingleDynamicMethod> methods, boolean varArgs) {
+ return getMaximallySpecificSingleDynamicMethods(methods, varArgs, null, null);
+ }
+
+ private abstract static class MethodTypeGetter<T> {
+ abstract MethodType getMethodType(T t);
}
+ private static final MethodTypeGetter<MethodHandle> METHOD_HANDLE_TYPE_GETTER =
+ new MethodTypeGetter<MethodHandle>() {
+ @Override
+ MethodType getMethodType(MethodHandle t) {
+ return t.type();
+ }
+ };
+
+ private static final MethodTypeGetter<SingleDynamicMethod> DYNAMIC_METHOD_TYPE_GETTER =
+ new MethodTypeGetter<SingleDynamicMethod>() {
+ @Override
+ MethodType getMethodType(SingleDynamicMethod t) {
+ return t.getMethodType();
+ }
+ };
+
+ /**
+ * Given a list of methods handles, returns a list of maximally specific methods, applying language-runtime
+ * specific conversion preferences.
+ *
+ * @param methods the list of method handles
+ * @param varArgs whether to assume the method handles are varargs
+ * @param argTypes concrete argument types for the invocation
+ * @return the list of maximally specific method handles.
+ */
+ static List<MethodHandle> getMaximallySpecificMethodHandles(List<MethodHandle> methods, boolean varArgs,
+ Class<?>[] argTypes, LinkerServices ls) {
+ return getMaximallySpecificMethods(methods, varArgs, argTypes, ls, METHOD_HANDLE_TYPE_GETTER);
+ }
+
+ /**
+ * Given a list of methods, returns a list of maximally specific methods, applying language-runtime specific
+ * conversion preferences.
+ *
+ * @param methods the list of methods
+ * @param varArgs whether to assume the methods are varargs
+ * @param argTypes concrete argument types for the invocation
+ * @return the list of maximally specific methods.
+ */
+ static List<SingleDynamicMethod> getMaximallySpecificSingleDynamicMethods(List<SingleDynamicMethod> methods,
+ boolean varArgs, Class<?>[] argTypes, LinkerServices ls) {
+ return getMaximallySpecificMethods(methods, varArgs, argTypes, ls, DYNAMIC_METHOD_TYPE_GETTER);
+ }
+
/**
* Given a list of methods, returns a list of maximally specific methods, applying language-runtime specific
* conversion preferences.
@@ -118,18 +166,18 @@
* @param argTypes concrete argument types for the invocation
* @return the list of maximally specific methods.
*/
- static List<MethodHandle> getMaximallySpecificMethods(List<MethodHandle> methods, boolean varArgs,
- Class<?>[] argTypes, LinkerServices ls) {
+ private static <T> List<T> getMaximallySpecificMethods(List<T> methods, boolean varArgs,
+ Class<?>[] argTypes, LinkerServices ls, MethodTypeGetter<T> methodTypeGetter) {
if(methods.size() < 2) {
return methods;
}
- final LinkedList<MethodHandle> maximals = new LinkedList<>();
- for(MethodHandle m: methods) {
- final MethodType methodType = m.type();
+ final LinkedList<T> maximals = new LinkedList<>();
+ for(T m: methods) {
+ final MethodType methodType = methodTypeGetter.getMethodType(m);
boolean lessSpecific = false;
- for(Iterator<MethodHandle> maximal = maximals.iterator(); maximal.hasNext();) {
- final MethodHandle max = maximal.next();
- switch(isMoreSpecific(methodType, max.type(), varArgs, argTypes, ls)) {
+ for(Iterator<T> maximal = maximals.iterator(); maximal.hasNext();) {
+ final T max = maximal.next();
+ switch(isMoreSpecific(methodType, methodTypeGetter.getMethodType(max), varArgs, argTypes, ls)) {
case TYPE_1_BETTER: {
maximal.remove();
break;
--- a/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Wed Jul 03 12:39:28 2013 +0200
@@ -84,16 +84,21 @@
package jdk.internal.dynalink.beans;
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.beans.ApplicableOverloadedMethods.ApplicabilityTest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.support.TypeUtilities;
/**
- * Represents an overloaded method.
+ * Represents a group of {@link SingleDynamicMethod} objects that represents all overloads of a particular name (or all
+ * constructors) for a particular class. Correctly handles overload resolution, variable arity methods, and caller
+ * sensitive methods within the overloads.
*
* @author Attila Szegedi
*/
@@ -101,7 +106,7 @@
/**
* Holds a list of all methods.
*/
- private final LinkedList<MethodHandle> methods;
+ private final LinkedList<SingleDynamicMethod> methods;
private final ClassLoader classLoader;
/**
@@ -111,21 +116,22 @@
* @param name the name of the method
*/
OverloadedDynamicMethod(Class<?> clazz, String name) {
- this(new LinkedList<MethodHandle>(), clazz.getClassLoader(), getClassAndMethodName(clazz, name));
+ this(new LinkedList<SingleDynamicMethod>(), clazz.getClassLoader(), getClassAndMethodName(clazz, name));
}
- private OverloadedDynamicMethod(LinkedList<MethodHandle> methods, ClassLoader classLoader, String name) {
+ private OverloadedDynamicMethod(LinkedList<SingleDynamicMethod> methods, ClassLoader classLoader, String name) {
super(name);
this.methods = methods;
this.classLoader = classLoader;
}
@Override
- SimpleDynamicMethod getMethodForExactParamTypes(String paramTypes) {
- final LinkedList<MethodHandle> matchingMethods = new LinkedList<>();
- for(MethodHandle method: methods) {
- if(typeMatchesDescription(paramTypes, method.type())) {
- matchingMethods.add(method);
+ SingleDynamicMethod getMethodForExactParamTypes(String paramTypes) {
+ final LinkedList<SingleDynamicMethod> matchingMethods = new LinkedList<>();
+ for(SingleDynamicMethod method: methods) {
+ final SingleDynamicMethod matchingMethod = method.getMethodForExactParamTypes(paramTypes);
+ if(matchingMethod != null) {
+ matchingMethods.add(matchingMethod);
}
}
switch(matchingMethods.size()) {
@@ -133,8 +139,7 @@
return null;
}
case 1: {
- final MethodHandle target = matchingMethods.get(0);
- return new SimpleDynamicMethod(target, SimpleDynamicMethod.getMethodNameWithSignature(target, getName()));
+ return matchingMethods.getFirst();
}
default: {
throw new BootstrapMethodError("Can't choose among " + matchingMethods + " for argument types "
@@ -144,7 +149,8 @@
}
@Override
- public MethodHandle getInvocation(final MethodType callSiteType, final LinkerServices linkerServices) {
+ public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
+ final MethodType callSiteType = callSiteDescriptor.getMethodType();
// First, find all methods applicable to the call site by subtyping (JLS 15.12.2.2)
final ApplicableOverloadedMethods subtypingApplicables = getApplicables(callSiteType,
ApplicableOverloadedMethods.APPLICABLE_BY_SUBTYPING);
@@ -156,7 +162,7 @@
ApplicableOverloadedMethods.APPLICABLE_BY_VARIABLE_ARITY);
// Find the methods that are maximally specific based on the call site signature
- List<MethodHandle> maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods();
+ List<SingleDynamicMethod> maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods();
if(maximallySpecifics.isEmpty()) {
maximallySpecifics = methodInvocationApplicables.findMaximallySpecificMethods();
if(maximallySpecifics.isEmpty()) {
@@ -171,12 +177,12 @@
// (Object, Object), and we have a method whose parameter types are (String, int). None of the JLS applicability
// rules will trigger, but we must consider the method, as it can be the right match for a concrete invocation.
@SuppressWarnings({ "unchecked", "rawtypes" })
- final List<MethodHandle> invokables = (List)methods.clone();
+ final List<SingleDynamicMethod> invokables = (List)methods.clone();
invokables.removeAll(subtypingApplicables.getMethods());
invokables.removeAll(methodInvocationApplicables.getMethods());
invokables.removeAll(variableArityApplicables.getMethods());
- for(final Iterator<MethodHandle> it = invokables.iterator(); it.hasNext();) {
- final MethodHandle m = it.next();
+ for(final Iterator<SingleDynamicMethod> it = invokables.iterator(); it.hasNext();) {
+ final SingleDynamicMethod m = it.next();
if(!isApplicableDynamically(linkerServices, callSiteType, m)) {
it.remove();
}
@@ -199,54 +205,45 @@
}
case 1: {
// Very lucky, we ended up with a single candidate method handle based on the call site signature; we
- // can link it very simply by delegating to a SimpleDynamicMethod.
- final MethodHandle mh = invokables.iterator().next();
- return new SimpleDynamicMethod(mh).getInvocation(callSiteType, linkerServices);
+ // can link it very simply by delegating to the SingleDynamicMethod.
+ invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
}
default: {
// We have more than one candidate. We have no choice but to link to a method that resolves overloads on
// every invocation (alternatively, we could opportunistically link the one method that resolves for the
// current arguments, but we'd need to install a fairly complex guard for that and when it'd fail, we'd
- // go back all the way to candidate selection.
- // TODO: cache per call site type
- return new OverloadedMethod(invokables, this, callSiteType, linkerServices).getInvoker();
+ // go back all the way to candidate selection. Note that we're resolving any potential caller sensitive
+ // methods here to their handles, as the OverloadedMethod instance is specific to a call site, so it
+ // has an already determined Lookup.
+ final List<MethodHandle> methodHandles = new ArrayList<>(invokables.size());
+ final MethodHandles.Lookup lookup = callSiteDescriptor.getLookup();
+ for(SingleDynamicMethod method: invokables) {
+ methodHandles.add(method.getTarget(lookup));
+ }
+ return new OverloadedMethod(methodHandles, this, callSiteType, linkerServices).getInvoker();
}
}
}
@Override
- public boolean contains(MethodHandle mh) {
- final MethodType type = mh.type();
- for(MethodHandle method: methods) {
- if(typesEqualNoReceiver(type, method.type())) {
+ public boolean contains(SingleDynamicMethod m) {
+ for(SingleDynamicMethod method: methods) {
+ if(method.contains(m)) {
return true;
}
}
return false;
}
- private static boolean typesEqualNoReceiver(MethodType type1, MethodType type2) {
- final int pc = type1.parameterCount();
- if(pc != type2.parameterCount()) {
- return false;
- }
- for(int i = 1; i < pc; ++i) { // i = 1: ignore receiver
- if(type1.parameterType(i) != type2.parameterType(i)) {
- return false;
- }
- }
- return true;
- }
-
ClassLoader getClassLoader() {
return classLoader;
}
private static boolean isApplicableDynamically(LinkerServices linkerServices, MethodType callSiteType,
- MethodHandle m) {
- final MethodType methodType = m.type();
- final boolean varArgs = m.isVarargsCollector();
+ SingleDynamicMethod m) {
+ final MethodType methodType = m.getMethodType();
+ final boolean varArgs = m.isVarArgs();
final int fixedArgLen = methodType.parameterCount() - (varArgs ? 1 : 0);
final int callSiteArgLen = callSiteType.parameterCount();
@@ -301,20 +298,11 @@
}
/**
- * Add a method identified by a {@link SimpleDynamicMethod} to this overloaded method's set.
- *
- * @param method the method to add.
- */
- void addMethod(SimpleDynamicMethod method) {
- addMethod(method.getTarget());
- }
-
- /**
* Add a method to this overloaded method's set.
*
* @param method a method to add
*/
- public void addMethod(MethodHandle method) {
+ public void addMethod(SingleDynamicMethod method) {
methods.add(method);
}
}
--- a/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java Wed Jul 03 12:39:28 2013 +0200
@@ -135,7 +135,7 @@
varArgMethods.trimToSize();
final MethodHandle bound = SELECT_METHOD.bindTo(this);
- final MethodHandle collecting = SimpleDynamicMethod.collectArguments(bound, argNum).asType(
+ final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
callSiteType.changeReturnType(MethodHandle.class));
invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(callSiteType), collecting);
}
@@ -167,7 +167,7 @@
break;
}
case 1: {
- method = new SimpleDynamicMethod(methods.get(0)).getInvocation(callSiteType, linkerServices);
+ method = SingleDynamicMethod.getInvocation(methods.get(0), callSiteType, linkerServices);
break;
}
default: {
--- a/nashorn/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Wed Jul 03 12:39:28 2013 +0200
@@ -84,29 +84,22 @@
package jdk.internal.dynalink.beans;
import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
-import java.lang.reflect.Array;
-import jdk.internal.dynalink.linker.LinkerServices;
-import jdk.internal.dynalink.support.Guards;
/**
- * A dynamic method bound to exactly one, non-overloaded Java method. Handles varargs.
+ * A dynamic method bound to exactly one Java method or constructor that is not caller sensitive. Since its target is
+ * not caller sensitive, this class pre-caches its method handle and always returns it from the call to
+ * {@link #getTarget(Lookup)}. Can be used in general to represents dynamic methods bound to a single method handle,
+ * even if that handle is not mapped to a Java method, i.e. as a wrapper around field getters/setters, array element
+ * getters/setters, etc.
*
* @author Attila Szegedi
*/
-class SimpleDynamicMethod extends DynamicMethod {
+class SimpleDynamicMethod extends SingleDynamicMethod {
private final MethodHandle target;
/**
- * Creates a simple dynamic method with no name.
- * @param target the target method handle
- */
- SimpleDynamicMethod(MethodHandle target) {
- this(target, null);
- }
-
- /**
* Creates a new simple dynamic method, with a name constructed from the class name, method name, and handle
* signature.
*
@@ -115,125 +108,26 @@
* @param name the simple name of the method
*/
SimpleDynamicMethod(MethodHandle target, Class<?> clazz, String name) {
- this(target, getName(target, clazz, name));
- }
-
- SimpleDynamicMethod(MethodHandle target, String name) {
- super(name);
+ super(getName(target, clazz, name));
this.target = target;
}
private static String getName(MethodHandle target, Class<?> clazz, String name) {
- return getMethodNameWithSignature(target, getClassAndMethodName(clazz, name));
- }
-
- static String getMethodNameWithSignature(MethodHandle target, String methodName) {
- final String typeStr = target.type().toString();
- final int retTypeIndex = typeStr.lastIndexOf(')') + 1;
- int secondParamIndex = typeStr.indexOf(',') + 1;
- if(secondParamIndex == 0) {
- secondParamIndex = retTypeIndex - 1;
- }
- return typeStr.substring(retTypeIndex) + " " + methodName + "(" + typeStr.substring(secondParamIndex, retTypeIndex);
- }
-
- /**
- * Returns the target of this dynamic method
- *
- * @return the target of this dynamic method
- */
- MethodHandle getTarget() {
- return target;
+ return getMethodNameWithSignature(target.type(), getClassAndMethodName(clazz, name));
}
@Override
- SimpleDynamicMethod getMethodForExactParamTypes(String paramTypes) {
- return typeMatchesDescription(paramTypes, target.type()) ? this : null;
+ boolean isVarArgs() {
+ return target.isVarargsCollector();
}
@Override
- MethodHandle getInvocation(MethodType callSiteType, LinkerServices linkerServices) {
- final MethodType methodType = target.type();
- final int paramsLen = methodType.parameterCount();
- final boolean varArgs = target.isVarargsCollector();
- final MethodHandle fixTarget = varArgs ? target.asFixedArity() : target;
- final int fixParamsLen = varArgs ? paramsLen - 1 : paramsLen;
- final int argsLen = callSiteType.parameterCount();
- if(argsLen < fixParamsLen) {
- // Less actual arguments than number of fixed declared arguments; can't invoke.
- return null;
- }
- // Method handle has the same number of fixed arguments as the call site type
- if(argsLen == fixParamsLen) {
- // Method handle that matches the number of actual arguments as the number of fixed arguments
- final MethodHandle matchedMethod;
- if(varArgs) {
- // If vararg, add a zero-length array of the expected type as the last argument to signify no variable
- // arguments.
- matchedMethod = MethodHandles.insertArguments(fixTarget, fixParamsLen, Array.newInstance(
- methodType.parameterType(fixParamsLen).getComponentType(), 0));
- } else {
- // Otherwise, just use the method
- matchedMethod = fixTarget;
- }
- return createConvertingInvocation(matchedMethod, linkerServices, callSiteType);
- }
-
- // What's below only works for varargs
- if(!varArgs) {
- return null;
- }
-
- final Class<?> varArgType = methodType.parameterType(fixParamsLen);
- // Handle a somewhat sinister corner case: caller passes exactly one argument in the vararg position, and we
- // must handle both a prepacked vararg array as well as a genuine 1-long vararg sequence.
- if(argsLen == paramsLen) {
- final Class<?> callSiteLastArgType = callSiteType.parameterType(fixParamsLen);
- if(varArgType.isAssignableFrom(callSiteLastArgType)) {
- // Call site signature guarantees we'll always be passed a single compatible array; just link directly
- // to the method.
- return createConvertingInvocation(fixTarget, linkerServices, callSiteType);
- }
- if(!linkerServices.canConvert(callSiteLastArgType, varArgType)) {
- // Call site signature guarantees the argument can definitely not be an array (i.e. it is primitive);
- // link immediately to a vararg-packing method handle.
- return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
- }
- // Call site signature makes no guarantees that the single argument in the vararg position will be
- // compatible across all invocations. Need to insert an appropriate guard and fall back to generic vararg
- // method when it is not.
- return MethodHandles.guardWithTest(Guards.isInstance(varArgType, fixParamsLen, callSiteType),
- createConvertingInvocation(fixTarget, linkerServices, callSiteType),
- createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType));
- }
-
- // Remaining case: more than one vararg.
- return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
+ MethodType getMethodType() {
+ return target.type();
}
@Override
- public boolean contains(MethodHandle mh) {
- return target.type().parameterList().equals(mh.type().parameterList());
- }
-
- /**
- * Creates a method handle out of the original target that will collect the varargs for the exact component type of
- * the varArg array. Note that this will nicely trigger language-specific type converters for exactly those varargs
- * for which it is necessary when later passed to linkerServices.convertArguments().
- *
- * @param target the original method handle
- * @param parameterCount the total number of arguments in the new method handle
- * @return a collecting method handle
- */
- static MethodHandle collectArguments(MethodHandle target, final int parameterCount) {
- final MethodType methodType = target.type();
- final int fixParamsLen = methodType.parameterCount() - 1;
- final Class<?> arrayType = methodType.parameterType(fixParamsLen);
- return target.asCollector(arrayType, parameterCount - fixParamsLen);
- }
-
- private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod,
- final LinkerServices linkerServices, final MethodType callSiteType) {
- return linkerServices.asType(sizedMethod, callSiteType);
+ MethodHandle getTarget(Lookup lookup) {
+ return target;
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java Wed Jul 03 12:39:28 2013 +0200
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2010, 2013, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+ Copyright 2009-2013 Attila Szegedi
+
+ Licensed under both the Apache License, Version 2.0 (the "Apache License")
+ and the BSD License (the "BSD License"), with licensee being free to
+ choose either of the two at their discretion.
+
+ You may not use this file except in compliance with either the Apache
+ License or the BSD License.
+
+ If you choose to use this file in compliance with the Apache License, the
+ following notice applies to you:
+
+ You may obtain a copy of the Apache License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+ If you choose to use this file in compliance with the BSD License, the
+ following notice applies to you:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the names of
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink.beans;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Array;
+import java.util.StringTokenizer;
+import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.support.Guards;
+
+/**
+ * Base class for dynamic methods that dispatch to a single target Java method or constructor. Handles adaptation of the
+ * target method to a call site type (including mapping variable arity methods to a call site signature with different
+ * arity).
+ * @author Attila Szegedi
+ * @version $Id: $
+ */
+abstract class SingleDynamicMethod extends DynamicMethod {
+ SingleDynamicMethod(String name) {
+ super(name);
+ }
+
+ /**
+ * Returns true if this method is variable arity.
+ * @return true if this method is variable arity.
+ */
+ abstract boolean isVarArgs();
+
+ /**
+ * Returns this method's native type.
+ * @return this method's native type.
+ */
+ abstract MethodType getMethodType();
+
+ /**
+ * Given a specified lookup, returns a method handle to this method's target.
+ * @param lookup the lookup to use.
+ * @return the handle to this method's target method.
+ */
+ abstract MethodHandle getTarget(MethodHandles.Lookup lookup);
+
+ @Override
+ MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) {
+ return getInvocation(getTarget(callSiteDescriptor.getLookup()), callSiteDescriptor.getMethodType(),
+ linkerServices);
+ }
+
+ @Override
+ SingleDynamicMethod getMethodForExactParamTypes(String paramTypes) {
+ return typeMatchesDescription(paramTypes, getMethodType()) ? this : null;
+ }
+
+ @Override
+ boolean contains(SingleDynamicMethod method) {
+ return getMethodType().parameterList().equals(method.getMethodType().parameterList());
+ }
+
+ static String getMethodNameWithSignature(MethodType type, String methodName) {
+ final String typeStr = type.toString();
+ final int retTypeIndex = typeStr.lastIndexOf(')') + 1;
+ int secondParamIndex = typeStr.indexOf(',') + 1;
+ if(secondParamIndex == 0) {
+ secondParamIndex = retTypeIndex - 1;
+ }
+ return typeStr.substring(retTypeIndex) + " " + methodName + "(" + typeStr.substring(secondParamIndex, retTypeIndex);
+ }
+
+ /**
+ * Given a method handle and a call site type, adapts the method handle to the call site type. Performs type
+ * conversions as needed using the specified linker services, and in case that the method handle is a vararg
+ * collector, matches it to the arity of the call site.
+ * @param target the method handle to adapt
+ * @param callSiteType the type of the call site
+ * @param linkerServices the linker services used for type conversions
+ * @return the adapted method handle.
+ */
+ static MethodHandle getInvocation(MethodHandle target, MethodType callSiteType, LinkerServices linkerServices) {
+ final MethodType methodType = target.type();
+ final int paramsLen = methodType.parameterCount();
+ final boolean varArgs = target.isVarargsCollector();
+ final MethodHandle fixTarget = varArgs ? target.asFixedArity() : target;
+ final int fixParamsLen = varArgs ? paramsLen - 1 : paramsLen;
+ final int argsLen = callSiteType.parameterCount();
+ if(argsLen < fixParamsLen) {
+ // Less actual arguments than number of fixed declared arguments; can't invoke.
+ return null;
+ }
+ // Method handle has the same number of fixed arguments as the call site type
+ if(argsLen == fixParamsLen) {
+ // Method handle that matches the number of actual arguments as the number of fixed arguments
+ final MethodHandle matchedMethod;
+ if(varArgs) {
+ // If vararg, add a zero-length array of the expected type as the last argument to signify no variable
+ // arguments.
+ matchedMethod = MethodHandles.insertArguments(fixTarget, fixParamsLen, Array.newInstance(
+ methodType.parameterType(fixParamsLen).getComponentType(), 0));
+ } else {
+ // Otherwise, just use the method
+ matchedMethod = fixTarget;
+ }
+ return createConvertingInvocation(matchedMethod, linkerServices, callSiteType);
+ }
+
+ // What's below only works for varargs
+ if(!varArgs) {
+ return null;
+ }
+
+ final Class<?> varArgType = methodType.parameterType(fixParamsLen);
+ // Handle a somewhat sinister corner case: caller passes exactly one argument in the vararg position, and we
+ // must handle both a prepacked vararg array as well as a genuine 1-long vararg sequence.
+ if(argsLen == paramsLen) {
+ final Class<?> callSiteLastArgType = callSiteType.parameterType(fixParamsLen);
+ if(varArgType.isAssignableFrom(callSiteLastArgType)) {
+ // Call site signature guarantees we'll always be passed a single compatible array; just link directly
+ // to the method, introducing necessary conversions. Also, preserve it being a variable arity method.
+ return createConvertingInvocation(target, linkerServices, callSiteType).asVarargsCollector(
+ callSiteLastArgType);
+ }
+ if(!linkerServices.canConvert(callSiteLastArgType, varArgType)) {
+ // Call site signature guarantees the argument can definitely not be an array (i.e. it is primitive);
+ // link immediately to a vararg-packing method handle.
+ return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
+ }
+ // Call site signature makes no guarantees that the single argument in the vararg position will be
+ // compatible across all invocations. Need to insert an appropriate guard and fall back to generic vararg
+ // method when it is not.
+ return MethodHandles.guardWithTest(Guards.isInstance(varArgType, fixParamsLen, callSiteType),
+ createConvertingInvocation(fixTarget, linkerServices, callSiteType),
+ createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType));
+ }
+
+ // Remaining case: more than one vararg.
+ return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
+ }
+
+ /**
+ * Creates a method handle out of the original target that will collect the varargs for the exact component type of
+ * the varArg array. Note that this will nicely trigger language-specific type converters for exactly those varargs
+ * for which it is necessary when later passed to linkerServices.convertArguments().
+ *
+ * @param target the original method handle
+ * @param parameterCount the total number of arguments in the new method handle
+ * @return a collecting method handle
+ */
+ static MethodHandle collectArguments(MethodHandle target, final int parameterCount) {
+ final MethodType methodType = target.type();
+ final int fixParamsLen = methodType.parameterCount() - 1;
+ final Class<?> arrayType = methodType.parameterType(fixParamsLen);
+ return target.asCollector(arrayType, parameterCount - fixParamsLen);
+ }
+
+ private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod,
+ final LinkerServices linkerServices, final MethodType callSiteType) {
+ return linkerServices.asType(sizedMethod, callSiteType);
+ }
+
+ private static boolean typeMatchesDescription(String paramTypes, MethodType type) {
+ final StringTokenizer tok = new StringTokenizer(paramTypes, ", ");
+ for(int i = 1; i < type.parameterCount(); ++i) { // i = 1 as we ignore the receiver
+ if(!(tok.hasMoreTokens() && typeNameMatches(tok.nextToken(), type.parameterType(i)))) {
+ return false;
+ }
+ }
+ return !tok.hasMoreTokens();
+ }
+
+ private static boolean typeNameMatches(String typeName, Class<?> type) {
+ return typeName.equals(typeName.indexOf('.') == -1 ? type.getSimpleName() : type.getCanonicalName());
+ }
+}
--- a/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java Wed Jul 03 12:39:28 2013 +0200
@@ -106,10 +106,18 @@
@Override
MethodHandle editMethodHandle(MethodHandle mh) {
+ return editStaticMethodHandle(mh);
+ }
+
+ static MethodHandle editStaticMethodHandle(MethodHandle mh) {
return dropReceiver(mh, Object.class);
}
- static MethodHandle dropReceiver(final MethodHandle mh, final Class<?> receiverClass) {
+ static MethodHandle editConstructorMethodHandle(MethodHandle cmh) {
+ return dropReceiver(cmh, StaticClass.class);
+ }
+
+ private static MethodHandle dropReceiver(final MethodHandle mh, final Class<?> receiverClass) {
MethodHandle newHandle = MethodHandles.dropArguments(mh, 0, receiverClass);
// NOTE: this is a workaround for the fact that dropArguments doesn't preserve vararg collector state.
if(mh.isVarargsCollector() && !newHandle.isVarargsCollector()) {
--- a/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java Wed Jul 03 12:39:28 2013 +0200
@@ -87,9 +87,7 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -131,20 +129,11 @@
private static DynamicMethod createConstructorMethod(Class<?> clazz) {
if(clazz.isArray()) {
final MethodHandle boundArrayCtor = ARRAY_CTOR.bindTo(clazz.getComponentType());
- return new SimpleDynamicMethod(drop(boundArrayCtor.asType(boundArrayCtor.type().changeReturnType(
- clazz))), clazz, "<init>");
+ return new SimpleDynamicMethod(StaticClassIntrospector.editConstructorMethodHandle(
+ boundArrayCtor.asType(boundArrayCtor.type().changeReturnType(clazz))), clazz, "<init>");
}
- final Constructor<?>[] ctrs = clazz.getConstructors();
- final List<MethodHandle> mhs = new ArrayList<>(ctrs.length);
- for(int i = 0; i < ctrs.length; ++i) {
- mhs.add(drop(SafeUnreflector.unreflectConstructor(ctrs[i])));
- }
- return createDynamicMethod(mhs, clazz, "<init>");
- }
-
- private static MethodHandle drop(MethodHandle mh) {
- return StaticClassIntrospector.dropReceiver(mh, StaticClass.class);
+ return createDynamicMethod(Arrays.asList(clazz.getConstructors()), clazz, "<init>");
}
@Override
@@ -161,11 +150,10 @@
}
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
final String op = desc.getNameToken(CallSiteDescriptor.OPERATOR);
- final MethodType methodType = desc.getMethodType();
if("new" == op && constructor != null) {
- final MethodHandle ctorInvocation = constructor.getInvocation(methodType, linkerServices);
+ final MethodHandle ctorInvocation = constructor.getInvocation(desc, linkerServices);
if(ctorInvocation != null) {
- return new GuardedInvocation(ctorInvocation, getClassGuard(methodType));
+ return new GuardedInvocation(ctorInvocation, getClassGuard(desc.getMethodType()));
}
}
return null;
--- a/nashorn/src/jdk/internal/dynalink/support/AbstractCallSiteDescriptor.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/support/AbstractCallSiteDescriptor.java Wed Jul 03 12:39:28 2013 +0200
@@ -139,8 +139,9 @@
@Override
public int hashCode() {
+ final MethodHandles.Lookup lookup = getLookup();
+ int h = lookup.lookupClass().hashCode() + 31 * lookup.lookupModes();
final int c = getNameTokenCount();
- int h = 0;
for(int i = 0; i < c; ++i) {
h = h * 31 + getNameToken(i).hashCode();
}
--- a/nashorn/src/jdk/internal/dynalink/support/Lookup.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/internal/dynalink/support/Lookup.java Wed Jul 03 12:39:28 2013 +0200
@@ -122,6 +122,18 @@
* @return the unreflected method handle.
*/
public MethodHandle unreflect(Method m) {
+ return unreflect(lookup, m);
+ }
+
+ /**
+ * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)}, converting any encountered
+ * {@link IllegalAccessException} into an {@link IllegalAccessError}.
+ *
+ * @param lookup the lookup used to unreflect
+ * @param m the method to unreflect
+ * @return the unreflected method handle.
+ */
+ public static MethodHandle unreflect(MethodHandles.Lookup lookup, Method m) {
try {
return lookup.unreflect(m);
} catch(IllegalAccessException e) {
@@ -131,7 +143,6 @@
}
}
-
/**
* Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectGetter(Field)}, converting any encountered
* {@link IllegalAccessException} into an {@link IllegalAccessError}.
@@ -202,6 +213,18 @@
* @return the unreflected constructor handle.
*/
public MethodHandle unreflectConstructor(Constructor<?> c) {
+ return unreflectConstructor(lookup, c);
+ }
+
+ /**
+ * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)}, converting any
+ * encountered {@link IllegalAccessException} into an {@link IllegalAccessError}.
+ *
+ * @param lookup the lookup used to unreflect
+ * @param c the constructor to unreflect
+ * @return the unreflected constructor handle.
+ */
+ public static MethodHandle unreflectConstructor(MethodHandles.Lookup lookup, Constructor<?> c) {
try {
return lookup.unreflectConstructor(c);
} catch(IllegalAccessException e) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Wed Jul 03 12:39:28 2013 +0200
@@ -78,7 +78,7 @@
* @return CallSite with MethodHandle to appropriate method or null if not found.
*/
public static CallSite bootstrap(final Lookup lookup, final String opDesc, final MethodType type, final int flags) {
- return dynamicLinker.link(LinkerCallSite.newLinkerCallSite(opDesc, type, flags));
+ return dynamicLinker.link(LinkerCallSite.newLinkerCallSite(lookup, opDesc, type, flags));
}
/**
@@ -94,12 +94,12 @@
return new RuntimeCallSite(type, initialName);
}
-
/**
- * Returns a dynamic invoker for a specified dynamic operation. You can use this method to create a method handle
- * that when invoked acts completely as if it were a Nashorn-linked call site. An overview of available dynamic
- * operations can be found in the <a href="https://github.com/szegedi/dynalink/wiki/User-Guide-0.4">Dynalink User Guide</a>,
- * but we'll show few examples here:
+ * Returns a dynamic invoker for a specified dynamic operation using the public lookup. You can use this method to
+ * create a method handle that when invoked acts completely as if it were a Nashorn-linked call site. An overview of
+ * available dynamic operations can be found in the
+ * <a href="https://github.com/szegedi/dynalink/wiki/User-Guide-0.6">Dynalink User Guide</a>, but we'll show few
+ * examples here:
* <ul>
* <li>Get a named property with fixed name:
* <pre>
@@ -196,7 +196,7 @@
}
/**
- * Returns a dynamic invoker for a specified dynamic operation. Similar to
+ * Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to
* {@link #createDynamicInvoker(String, Class, Class...)} but with return and parameter types composed into a
* method type in the signature. See the discussion of that method for details.
* @param opDesc Dynalink dynamic operation descriptor.
@@ -204,7 +204,7 @@
* @return MethodHandle for invoking the operation.
*/
public static MethodHandle createDynamicInvoker(final String opDesc, final MethodType type) {
- return bootstrap(null, opDesc, type, 0).dynamicInvoker();
+ return bootstrap(MethodHandles.publicLookup(), opDesc, type, 0).dynamicInvoker();
}
/**
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java Wed Jul 03 12:39:28 2013 +0200
@@ -28,6 +28,7 @@
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Modifier;
import java.security.AccessController;
@@ -39,7 +40,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.support.LinkRequestImpl;
import jdk.nashorn.internal.objects.NativeJava;
@@ -119,9 +119,12 @@
return AccessController.doPrivileged(new PrivilegedExceptionAction<MethodHandle>() {
@Override
public MethodHandle run() throws Exception {
- return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(NashornCallSiteDescriptor.get(
- "dyn:new", MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false,
- adapterClass, null)).getInvocation(), adapterClass);
+ // NOTE: we use publicLookup(), but none of our adapter constructors are caller sensitive, so this is
+ // okay, we won't artificially limit access.
+ return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(
+ NashornCallSiteDescriptor.get(MethodHandles.publicLookup(), "dyn:new",
+ MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false,
+ adapterClass, null)).getInvocation(), adapterClass);
}
});
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Wed Jul 03 12:39:28 2013 +0200
@@ -25,7 +25,6 @@
package jdk.nashorn.internal.runtime.linker;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.io.FileNotFoundException;
@@ -47,6 +46,7 @@
import jdk.internal.dynalink.ChainedCallSite;
import jdk.internal.dynalink.DynamicLinker;
import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -79,8 +79,9 @@
* @param flags Call site specific flags.
* @return New LinkerCallSite.
*/
- static LinkerCallSite newLinkerCallSite(final String name, final MethodType type, final int flags) {
- final NashornCallSiteDescriptor desc = NashornCallSiteDescriptor.get(name, type, flags);
+ static LinkerCallSite newLinkerCallSite(final MethodHandles.Lookup lookup, final String name, final MethodType type,
+ final int flags) {
+ final NashornCallSiteDescriptor desc = NashornCallSiteDescriptor.get(lookup, name, type, flags);
if (desc.isProfile()) {
return ProfilingLinkerCallSite.newProfilingLinkerCallSite(desc);
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Wed Jul 03 14:08:00 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Wed Jul 03 12:39:28 2013 +0200
@@ -25,9 +25,12 @@
package jdk.nashorn.internal.runtime.linker;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
-import java.lang.ref.WeakReference;
-import java.util.WeakHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.support.AbstractCallSiteDescriptor;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
@@ -70,9 +73,15 @@
* set. */
public static final int CALLSITE_TRACE_SCOPE = 0x200;
- private static final WeakHashMap<NashornCallSiteDescriptor, WeakReference<NashornCallSiteDescriptor>> canonicals =
- new WeakHashMap<>();
+ private static final ClassValue<ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor>> canonicals =
+ new ClassValue<ConcurrentMap<NashornCallSiteDescriptor,NashornCallSiteDescriptor>>() {
+ @Override
+ protected ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> computeValue(Class<?> type) {
+ return new ConcurrentHashMap<>();
+ }
+ };
+ private final MethodHandles.Lookup lookup;
private final String operator;
private final String operand;
private final MethodType methodType;
@@ -81,39 +90,35 @@
/**
* Retrieves a Nashorn call site descriptor with the specified values. Since call site descriptors are immutable
* this method is at liberty to retrieve canonicalized instances (although it is not guaranteed it will do so).
+ * @param lookup the lookup describing the script
* @param name the name at the call site, e.g. {@code "dyn:getProp|getElem|getMethod:color"}.
* @param methodType the method type at the call site
* @param flags Nashorn-specific call site flags
* @return a call site descriptor with the specified values.
*/
- public static NashornCallSiteDescriptor get(final String name, final MethodType methodType, final int flags) {
+ public static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name,
+ final MethodType methodType, final int flags) {
final String[] tokenizedName = CallSiteDescriptorFactory.tokenizeName(name);
assert tokenizedName.length == 2 || tokenizedName.length == 3;
assert "dyn".equals(tokenizedName[0]);
assert tokenizedName[1] != null;
// TODO: see if we can move mangling/unmangling into Dynalink
- return get(tokenizedName[1], tokenizedName.length == 3 ? tokenizedName[2].intern() : null,
+ return get(lookup, tokenizedName[1], tokenizedName.length == 3 ? tokenizedName[2].intern() : null,
methodType, flags);
}
- private static NashornCallSiteDescriptor get(final String operator, final String operand, final MethodType methodType, final int flags) {
- final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(operator, operand, methodType, flags);
+ private static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String operator, final String operand, final MethodType methodType, final int flags) {
+ final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(lookup, operator, operand, methodType, flags);
// Many of these call site descriptors are identical (e.g. every getter for a property color will be
- // "dyn:getProp:color(Object)Object", so it makes sense canonicalizing them in a weak map
- synchronized(canonicals) {
- final WeakReference<NashornCallSiteDescriptor> ref = canonicals.get(csd);
- if(ref != null) {
- final NashornCallSiteDescriptor canonical = ref.get();
- if(canonical != null) {
- return canonical;
- }
- }
- canonicals.put(csd, new WeakReference<>(csd));
- }
- return csd;
+ // "dyn:getProp:color(Object)Object", so it makes sense canonicalizing them.
+ final Map<NashornCallSiteDescriptor, NashornCallSiteDescriptor> classCanonicals = canonicals.get(lookup.lookupClass());
+ final NashornCallSiteDescriptor canonical = classCanonicals.putIfAbsent(csd, csd);
+ return canonical != null ? canonical : csd;
}
- private NashornCallSiteDescriptor(final String operator, final String operand, final MethodType methodType, final int flags) {
+ private NashornCallSiteDescriptor(final MethodHandles.Lookup lookup, final String operator, final String operand,
+ final MethodType methodType, final int flags) {
+ this.lookup = lookup;
this.operator = operator;
this.operand = operand;
this.methodType = methodType;
@@ -142,6 +147,11 @@
}
@Override
+ public Lookup getLookup() {
+ return lookup;
+ }
+
+ @Override
public boolean equals(final CallSiteDescriptor csd) {
return super.equals(csd) && flags == getFlags(csd);
}
@@ -279,6 +289,6 @@
@Override
public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
- return get(operator, operand, newMethodType, flags);
+ return get(getLookup(), operator, operand, newMethodType, flags);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8010946-2.js Wed Jul 03 12:39:28 2013 +0200
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010, 2013, 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-8010946: AccessController.doPrivileged() doesn't work as expected.
+ * This is actually a broader issue of having Dynalink correctly handle
+ * caller-sensitive methods.
+ *
+ * @test
+ * @run
+ */
+
+// Ensure these are CallerSensitiveDynamicMethods
+print(java.security.AccessController["doPrivileged(PrivilegedAction)"])
+print(java.lang.Class["forName(String)"])
+
+// Ensure this is not
+print(java.lang.String["valueOf(char)"])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8010946-2.js.EXPECTED Wed Jul 03 12:39:28 2013 +0200
@@ -0,0 +1,3 @@
+[jdk.internal.dynalink.beans.CallerSensitiveDynamicMethod Object java.security.AccessController.doPrivileged(PrivilegedAction)]
+[jdk.internal.dynalink.beans.CallerSensitiveDynamicMethod Class java.lang.Class.forName(String)]
+[jdk.internal.dynalink.beans.SimpleDynamicMethod String java.lang.String.valueOf(char)]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8010946-privileged.js Wed Jul 03 12:39:28 2013 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 2013, 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-8010946: AccessController.doPrivileged() doesn't work as expected.
+ * This is actually a broader issue of having Dynalink correctly handle
+ * caller-sensitive methods.
+ *
+ * NOTE: This is not a standalone test file, it is loaded by JDK-801946.js
+ * @subtest
+ */
+
+(function() {
+ var getProperty = java.lang.System.getProperty
+ var doPrivileged = java.security.AccessController["doPrivileged(PrivilegedAction)"]
+
+ this.executeUnprivileged = function() {
+ var x = getProperty("java.security.policy")
+ if(x != null) {
+ print("Successfully retrieved restricted system property.")
+ }
+ }
+
+ this.executePrivileged = function() {
+ doPrivileged(executeUnprivileged)
+ }
+})();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8010946.js Wed Jul 03 12:39:28 2013 +0200
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010, 2013, 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-8010946: AccessController.doPrivileged() doesn't work as expected.
+ * This is actually a broader issue of having Dynalink correctly handle
+ * caller-sensitive methods.
+ *
+ * @test
+ * @run
+ */
+
+// This is unprivileged code that loads privileged code.
+load(__DIR__ + "JDK-8010946-privileged.js")
+
+try {
+ // This should fail, even though the code itself resides in the
+ // privileged script, as we're invoking it without going through
+ // doPrivileged()
+ print("Attempting unprivileged execution...")
+ executeUnprivileged()
+ print("FAIL: Unprivileged execution succeeded!")
+} catch(e) {
+ print("Unprivileged execution failed with " + e)
+}
+
+print()
+
+// This should succeed, as it's going through doPrivileged().
+print("Attempting privileged execution...")
+executePrivileged()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8010946.js.EXPECTED Wed Jul 03 12:39:28 2013 +0200
@@ -0,0 +1,5 @@
+Attempting unprivileged execution...
+Unprivileged execution failed with java.security.AccessControlException: access denied ("java.util.PropertyPermission" "java.security.policy" "read")
+
+Attempting privileged execution...
+Successfully retrieved restricted system property.