--- 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);
}