nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
changeset 24719 f726e9d67629
parent 19630 99f53f31008e
child 24778 2ff5d7041566
--- a/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -97,7 +97,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
 import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -107,6 +106,7 @@
 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
 import jdk.internal.dynalink.support.Guards;
 import jdk.internal.dynalink.support.Lookup;
+import jdk.internal.dynalink.support.TypeUtilities;
 
 /**
  * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
@@ -459,12 +459,16 @@
 
     private GuardedInvocationComponent getPropertySetter(CallSiteDescriptor callSiteDescriptor,
             LinkerServices linkerServices, List<String> operations) throws Exception {
-        final MethodType type = callSiteDescriptor.getMethodType();
         switch(callSiteDescriptor.getNameTokenCount()) {
             case 2: {
                 // Must have three arguments: target object, property name, and property value.
                 assertParameterCount(callSiteDescriptor, 3);
 
+                // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
+                // valid for us to convert return values proactively. Also, since we don't know what setters will be
+                // invoked, we'll conservatively presume Object return type.
+                final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
+
                 // What's below is basically:
                 //   foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
                 //     get_setter_handle(type, linkerServices))
@@ -473,8 +477,8 @@
                 // component's invocation.
 
                 // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
-                // abbreviate to R(O, N, V) going forward.
-                // We want setters that conform to "R(O, V)"
+                // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
+                // Object return type).
                 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)
@@ -495,11 +499,11 @@
 
                 final MethodHandle fallbackFolded;
                 if(nextComponent == null) {
-                    // Object(MethodHandle)->R(MethodHandle, O, N, V); returns constant null
+                    // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
                     fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
                             type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
                 } else {
-                    // R(O, N, V)->R(MethodHandle, O, N, V); adapts the next component's invocation to drop the
+                    // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
                     // extra argument resulting from fold
                     fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
                             0, MethodHandle.class);
@@ -545,9 +549,12 @@
 
     private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
             LinkerServices linkerServices, List<String> ops) throws Exception {
-        final MethodType type = callSiteDescriptor.getMethodType();
         switch(callSiteDescriptor.getNameTokenCount()) {
             case 2: {
+                // Since we can't know what kind of a getter we'll get back on different invocations, we'll just
+                // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
+                // runtime might not allow coercing at that call site.
+                final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
                 // Must have exactly two arguments: receiver and name
                 assertParameterCount(callSiteDescriptor, 2);
 
@@ -563,11 +570,11 @@
                         GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
                 final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
                         callSiteBoundMethodGetter);
-                // Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0)
+                // Object(AnnotatedDynamicMethod, Object)->Object(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(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
+                // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
                 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
                         type.parameterType(1));
                 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
@@ -575,17 +582,19 @@
 
                 final MethodHandle fallbackFolded;
                 if(nextComponent == null) {
-                    // Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null
+                    // Object(AnnotatedDynamicMethod)->Object(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(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the
-                    // extra argument resulting from fold
-                    fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
-                            0, AnnotatedDynamicMethod.class);
+                    // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
+                    // drop the extra argument resulting from fold and to change its return type to Object.
+                    final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
+                    final MethodType nextType = nextInvocation.type();
+                    fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
+                            nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
                 }
 
-                // fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
+                // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
                 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
                             IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
                 if(nextComponent == null) {
@@ -612,8 +621,8 @@
                 // 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);
+                return new GuardedInvocationComponent(getter, getGuard(validationType,
+                        callSiteDescriptor.getMethodType()), clazz, validationType);
             }
             default: {
                 // Can't do anything with more than 3 name components
@@ -642,21 +651,25 @@
         }
     }
 
-    private static final MethodHandle IS_DYNAMIC_METHOD_NOT_NULL = Guards.asType(Guards.isNotNull(),
-            MethodType.methodType(boolean.class, DynamicMethod.class));
-    private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class);
+    private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
+            MethodType.methodType(boolean.class, Object.class));
+    private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
 
     private GuardedInvocationComponent getMethodGetter(CallSiteDescriptor callSiteDescriptor,
             LinkerServices linkerServices, List<String> ops) throws Exception {
-        final MethodType type = callSiteDescriptor.getMethodType();
+        // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
+        // be visible outside of this linker, declare it to return Object.
+        final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
         switch(callSiteDescriptor.getNameTokenCount()) {
             case 2: {
                 // Must have exactly two arguments: receiver and name
                 assertParameterCount(callSiteDescriptor, 2);
                 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
                         linkerServices, ops);
-                if(nextComponent == null) {
-                    // No next component operation; just return a component for this operation.
+                if(nextComponent == null || !TypeUtilities.areAssignable(DynamicMethod.class,
+                        nextComponent.getGuardedInvocation().getInvocation().type().returnType())) {
+                    // No next component operation, or it can never produce a dynamic method; just return a component
+                    // for this operation.
                     return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
                 }
 
@@ -665,21 +678,20 @@
                 // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
                 // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
 
-                final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType(
-                        DynamicMethod.class));
+                final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
                 // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
                 final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
-                        DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0,
-                                DynamicMethod.class));
+                        OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
                 final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
-                // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly
-                assert nextComponentInvocation.type().equals(type);
+                // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
+                // return type.
+                assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
                 // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
                 final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
-                        DynamicMethod.class);
+                        Object.class);
                 // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
                 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
-                        IS_DYNAMIC_METHOD_NOT_NULL, returnMethodHandle, nextCombinedInvocation), typedGetter);
+                        IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter);
 
                 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
             }
@@ -695,7 +707,7 @@
                 // No delegation to the next component of the composite operation; if we have a method with that name,
                 // we'll always return it at this point.
                 return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
-                        MethodHandles.constant(DynamicMethod.class, method), 0, type.parameterType(0)), type), type);
+                        MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
             }
             default: {
                 // Can't do anything with more than 3 name components
@@ -704,6 +716,30 @@
         }
     }
 
+    static class MethodPair {
+        final MethodHandle method1;
+        final MethodHandle method2;
+
+        MethodPair(final MethodHandle method1, final MethodHandle method2) {
+            this.method1 = method1;
+            this.method2 = method2;
+        }
+
+        MethodHandle guardWithTest(final MethodHandle test) {
+            return MethodHandles.guardWithTest(test, method1, method2);
+        }
+    }
+
+    static MethodPair matchReturnTypes(MethodHandle m1, MethodHandle m2) {
+        final MethodType type1 = m1.type();
+        final MethodType type2 = m2.type();
+        final Class<?> commonRetType = TypeUtilities.getCommonLosslessConversionType(type1.returnType(),
+                type2.returnType());
+        return new MethodPair(
+                m1.asType(type1.changeReturnType(commonRetType)),
+                m2.asType(type2.changeReturnType(commonRetType)));
+    }
+
     private static void assertParameterCount(CallSiteDescriptor descriptor, int paramCount) {
         if(descriptor.getMethodType().parameterCount() != paramCount) {
             throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
@@ -739,11 +775,14 @@
     }
 
     private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
-            "getDynamicMethod", DynamicMethod.class, Object.class), 1, Object.class);
+            "getDynamicMethod", Object.class, Object.class), 1, Object.class);
     private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
 
     @SuppressWarnings("unused")
-    private DynamicMethod getDynamicMethod(Object name) {
+    // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
+    // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
+    // "dyn:getMethod" linking).
+    private Object getDynamicMethod(Object name) {
         return getDynamicMethod(String.valueOf(name), methods);
     }