nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
changeset 18841 9bbc4b8832b2
parent 16267 cd471d62bb6a
child 18876 ada98218aaae
--- 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;
+        }
     }
 }