diff -r ac62e33a99b0 -r e2497b11a021 src/jdk/internal/dynalink/beans/AbstractJavaLinker.java --- a/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Aug 20 10:25:28 2014 +0200 +++ b/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Aug 20 10:26:01 2014 +0200 @@ -106,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 @@ -289,7 +290,7 @@ return new CallerSensitiveDynamicMethod(m); } final Member member = (Member)m; - return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName()); + return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName(), m instanceof Constructor); } /** @@ -389,6 +390,10 @@ return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); } + SingleDynamicMethod getConstructorMethod(final String signature) { + return null; + } + private MethodHandle getAssignableGuard(final MethodType type) { return Guards.asType(assignableGuard, type); } @@ -411,18 +416,18 @@ return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType())); } - private static MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor, + private MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices, final String methodName, final Map methodMap) { final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap); return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null; } - private static DynamicMethod getDynamicMethod(final String methodName, final Map methodMap) { + private DynamicMethod getDynamicMethod(final String methodName, final Map methodMap) { final DynamicMethod dynaMethod = methodMap.get(methodName); return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap); } - private static SingleDynamicMethod getExplicitSignatureDynamicMethod(final String methodName, + private SingleDynamicMethod getExplicitSignatureDynamicMethod(final String fullName, final Map 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 @@ -432,23 +437,33 @@ // for performance reasons. // Is the method name lexically of the form "name(types)"? - final int lastChar = methodName.length() - 1; - if(methodName.charAt(lastChar) != ')') { + final int lastChar = fullName.length() - 1; + if(fullName.charAt(lastChar) != ')') { return null; } - final int openBrace = methodName.indexOf('('); + final int openBrace = fullName.indexOf('('); if(openBrace == -1) { return null; } + final String name = fullName.substring(0, openBrace); + final String signature = fullName.substring(openBrace + 1, lastChar); + // Find an existing method for the "name" part - final DynamicMethod simpleNamedMethod = methodsMap.get(methodName.substring(0, openBrace)); + final DynamicMethod simpleNamedMethod = methodsMap.get(name); if(simpleNamedMethod == null) { + // explicit signature constructor access + // Java.type("java.awt.Color")["(int,int,int)"] + // will get Color(int,int,int) constructor of Color class. + if (name.isEmpty()) { + return getConstructorMethod(signature); + } + return null; } // Try to get a narrowed dynamic method for the explicit parameter types. - return simpleNamedMethod.getMethodForExactParamTypes(methodName.substring(openBrace + 1, lastChar)); + return simpleNamedMethod.getMethodForExactParamTypes(signature); } private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType( @@ -458,12 +473,16 @@ private GuardedInvocationComponent getPropertySetter(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices, final List 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)) @@ -472,8 +491,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) @@ -494,11 +513,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); @@ -544,9 +563,12 @@ private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices, final List 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); @@ -562,11 +584,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, @@ -574,17 +596,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) { @@ -611,8 +635,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 @@ -641,21 +665,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(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices, final List 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); } @@ -664,21 +692,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); } @@ -694,7 +721,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 @@ -703,6 +730,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(final MethodHandle m1, final 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(final CallSiteDescriptor descriptor, final int paramCount) { if(descriptor.getMethodType().parameterCount() != paramCount) { throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters."); @@ -738,11 +789,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(final 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(final Object name) { return getDynamicMethod(String.valueOf(name), methods); }