下面列出了java.lang.invoke.MethodHandles#filterArguments ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* Generates a method handle for the {@code equals} method for a given data class
* @param receiverClass the data class
* @param getters the list of getters
* @return the method handle
*/
private static MethodHandle makeEquals(Class<?> receiverClass,
List<MethodHandle> getters) {
MethodType rr = MethodType.methodType(boolean.class, receiverClass, receiverClass);
MethodType ro = MethodType.methodType(boolean.class, receiverClass, Object.class);
MethodHandle instanceFalse = MethodHandles.dropArguments(FALSE, 0, receiverClass, Object.class); // (RO)Z
MethodHandle instanceTrue = MethodHandles.dropArguments(TRUE, 0, receiverClass, Object.class); // (RO)Z
MethodHandle isSameObject = OBJECT_EQ.asType(ro); // (RO)Z
MethodHandle isInstance = MethodHandles.dropArguments(CLASS_IS_INSTANCE.bindTo(receiverClass), 0, receiverClass); // (RO)Z
MethodHandle accumulator = MethodHandles.dropArguments(TRUE, 0, receiverClass, receiverClass); // (RR)Z
for (MethodHandle getter : getters) {
MethodHandle equalator = equalator(getter.type().returnType()); // (TT)Z
MethodHandle thisFieldEqual = MethodHandles.filterArguments(equalator, 0, getter, getter); // (RR)Z
accumulator = MethodHandles.guardWithTest(thisFieldEqual, accumulator, instanceFalse.asType(rr));
}
return MethodHandles.guardWithTest(isSameObject,
instanceTrue,
MethodHandles.guardWithTest(isInstance, accumulator.asType(ro), instanceFalse));
}
private static MethodHandle convertArgToInt(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) {
final Class<?> sourceType = desc.getMethodType().parameterType(1);
if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
return mh;
} else if(ls.canConvert(sourceType, Number.class)) {
final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class);
return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType(
mh.type().parameterType(1))));
}
return mh;
}
private static MethodHandle applyConverters(final MethodHandle handle, final int pos, final List<MethodHandle> converters) {
if(converters.isEmpty()) {
return handle;
}
final MethodHandle newHandle =
MethodHandles.filterArguments(handle, pos, converters.toArray(new MethodHandle[converters.size()]));
converters.clear();
return newHandle;
}
private static MethodHandle convertArgToInt(MethodHandle mh, LinkerServices ls, CallSiteDescriptor desc) {
final Class<?> sourceType = desc.getMethodType().parameterType(1);
if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
return mh;
} else if(ls.canConvert(sourceType, Number.class)) {
final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class);
return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType(
mh.type().parameterType(1))));
}
return mh;
}
private static MethodHandle convertArgToInt(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) {
final Class<?> sourceType = desc.getMethodType().parameterType(1);
if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
return mh;
} else if(ls.canConvert(sourceType, Number.class)) {
final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class);
return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType(
mh.type().parameterType(1))));
}
return mh;
}
private static MethodHandle convertArgToInt(MethodHandle mh, LinkerServices ls, CallSiteDescriptor desc) {
final Class<?> sourceType = desc.getMethodType().parameterType(1);
if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
return mh;
} else if(ls.canConvert(sourceType, Number.class)) {
final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class);
return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType(
mh.type().parameterType(1))));
}
return mh;
}
private static MethodHandle applyConverters(MethodHandle handle, int pos, List<MethodHandle> converters) {
if(converters.isEmpty()) {
return handle;
}
final MethodHandle newHandle =
MethodHandles.filterArguments(handle, pos, converters.toArray(new MethodHandle[converters.size()]));
converters.clear();
return newHandle;
}
private static MethodHandle convertArgToInt(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) {
final Class<?> sourceType = desc.getMethodType().parameterType(1);
if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
return mh;
} else if(ls.canConvert(sourceType, Number.class)) {
final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class);
return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType(
mh.type().parameterType(1))));
}
return mh;
}
@Override
public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
final MethodHandle mh = MethodHandles.filterArguments(target, pos, filters);
return debug(mh, "filterArguments", target, pos, filters);
}
private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
LinkerServices linkerServices, List<String> ops) throws Exception {
final MethodType type = callSiteDescriptor.getMethodType();
switch(callSiteDescriptor.getNameTokenCount()) {
case 2: {
// Must have exactly two arguments: receiver and name
assertParameterCount(callSiteDescriptor, 2);
// 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
// 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(
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(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
type.parameterType(1));
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
linkerServices, ops);
final MethodHandle fallbackFolded;
if(nextComponent == null) {
// 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(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);
}
// fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
if(nextComponent == null) {
return getClassGuardedInvocationComponent(compositeGetter, type);
}
return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
}
case 3: {
// Must have exactly one argument: receiver
assertParameterCount(callSiteDescriptor, 1);
// Fixed name
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.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
// we're linking against a field getter, don't make the assumption.
// 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);
}
default: {
// Can't do anything with more than 3 name components
return null;
}
}
}
private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
LinkerServices linkerServices, List<String> ops) throws Exception {
final MethodType type = callSiteDescriptor.getMethodType();
switch(callSiteDescriptor.getNameTokenCount()) {
case 2: {
// Must have exactly two arguments: receiver and name
assertParameterCount(callSiteDescriptor, 2);
// 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
// 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(
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(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
type.parameterType(1));
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
linkerServices, ops);
final MethodHandle fallbackFolded;
if(nextComponent == null) {
// 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(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);
}
// fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
if(nextComponent == null) {
return getClassGuardedInvocationComponent(compositeGetter, type);
}
return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
}
case 3: {
// Must have exactly one argument: receiver
assertParameterCount(callSiteDescriptor, 1);
// Fixed name
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.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
// we're linking against a field getter, don't make the assumption.
// 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);
}
default: {
// Can't do anything with more than 3 name components
return null;
}
}
}
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
throws Exception {
final Object objSuperAdapter = linkRequest.getReceiver();
if(!(objSuperAdapter instanceof JavaSuperAdapter)) {
return null;
}
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
if(!CallSiteDescriptorFactory.tokenizeOperators(descriptor).contains(GET_METHOD)) {
// We only handle getMethod
return null;
}
final Object adapter = ((JavaSuperAdapter)objSuperAdapter).getAdapter();
// Replace argument (javaSuperAdapter, ...) => (adapter, ...) when delegating to BeansLinker
final Object[] args = linkRequest.getArguments();
args[0] = adapter;
// Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker.
final MethodType type = descriptor.getMethodType();
final Class<?> adapterClass = adapter.getClass();
final boolean hasFixedName = descriptor.getNameTokenCount() > 2;
final String opName = hasFixedName ? (DYN_GET_METHOD_FIXED + descriptor.getNameToken(
CallSiteDescriptor.NAME_OPERAND)) : DYN_GET_METHOD;
final CallSiteDescriptor newDescriptor = NashornCallSiteDescriptor.get(descriptor.getLookup(), opName,
type.changeParameterType(0, adapterClass), 0);
// Delegate to BeansLinker
final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation(
BeansLinker.getLinkerForClass(adapterClass), linkRequest.replaceArguments(newDescriptor, args),
linkerServices);
final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass);
if(guardedInv == null) {
// Short circuit the lookup here for non-existent methods by linking an empty getter. If we just returned
// null instead, BeansLinker would find final methods on the JavaSuperAdapter instead: getClass() and
// wait().
return new GuardedInvocation(MethodHandles.dropArguments(EMPTY_GETTER, 1,type.parameterList().subList(1,
type.parameterCount())), guard).asType(descriptor);
}
final MethodHandle invocation = guardedInv.getInvocation();
final MethodType invType = invocation.type();
// For invocation typed R(T0, ...) create a dynamic method binder of type Object(R, T0)
final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(Object.class,
invType.returnType(), invType.parameterType(0)));
// For invocation typed R(T0, T1, ...) create a dynamic method binder of type Object(R, T0, T1, ...)
final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2,
invType.parameterList().subList(1, invType.parameterCount()));
// Finally, fold the invocation into the binder to produce a method handle that will bind every returned
// DynamicMethod object from dyn:getMethod calls to the actual receiver
// Object(R(T0, T1, ...), T0, T1, ...)
final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation);
final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type);
final MethodHandle adaptedInvocation;
if(hasFixedName) {
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter);
} else {
// Add a filter that'll prepend "super$" to each name passed to the variable-name "dyn:getMethod".
final MethodHandle typedAddPrefix = asFilterType(ADD_PREFIX_TO_METHOD_NAME, 1, invType, type);
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter, typedAddPrefix);
}
return guardedInv.replaceMethods(adaptedInvocation, guard).asType(descriptor);
}
private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> ops) throws Exception {
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);
// 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
// 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(
AnnotatedDynamicMethod.class));
final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup(), linkerServices);
final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
callSiteBoundMethodGetter);
// 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
// Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
type.parameterType(1));
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
linkerServices, ops);
final MethodHandle fallbackFolded;
if(nextComponent == 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 {
// 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(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) {
return getClassGuardedInvocationComponent(compositeGetter, type);
}
return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
}
case 3: {
// Must have exactly one argument: receiver
assertParameterCount(callSiteDescriptor, 1);
// Fixed name
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.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
// we're linking against a field getter, don't make the assumption.
// 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(getter, getGuard(validationType,
callSiteDescriptor.getMethodType()), clazz, validationType);
}
default: {
// Can't do anything with more than 3 name components
return null;
}
}
}
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
throws Exception {
final Object objSuperAdapter = linkRequest.getReceiver();
if(!(objSuperAdapter instanceof JavaSuperAdapter)) {
return null;
}
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
if(!CallSiteDescriptorFactory.tokenizeOperators(descriptor).contains(GET_METHOD)) {
// We only handle getMethod
return null;
}
final Object adapter = ((JavaSuperAdapter)objSuperAdapter).getAdapter();
// Replace argument (javaSuperAdapter, ...) => (adapter, ...) when delegating to BeansLinker
final Object[] args = linkRequest.getArguments();
args[0] = adapter;
// Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker.
final MethodType type = descriptor.getMethodType();
final Class<?> adapterClass = adapter.getClass();
final boolean hasFixedName = descriptor.getNameTokenCount() > 2;
final String opName = hasFixedName ? (DYN_GET_METHOD_FIXED + descriptor.getNameToken(
CallSiteDescriptor.NAME_OPERAND)) : DYN_GET_METHOD;
final CallSiteDescriptor newDescriptor = NashornCallSiteDescriptor.get(descriptor.getLookup(), opName,
type.changeParameterType(0, adapterClass), 0);
// Delegate to BeansLinker
final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation(
BeansLinker.getLinkerForClass(adapterClass), linkRequest.replaceArguments(newDescriptor, args),
linkerServices);
final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass);
if(guardedInv == null) {
// Short circuit the lookup here for non-existent methods by linking an empty getter. If we just returned
// null instead, BeansLinker would find final methods on the JavaSuperAdapter instead: getClass() and
// wait().
return new GuardedInvocation(MethodHandles.dropArguments(EMPTY_GETTER, 1,type.parameterList().subList(1,
type.parameterCount())), guard).asType(descriptor);
}
final MethodHandle invocation = guardedInv.getInvocation();
final MethodType invType = invocation.type();
// For invocation typed R(T0, ...) create a dynamic method binder of type R(R, T0)
final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(invType.returnType(),
invType.returnType(), invType.parameterType(0)));
// For invocation typed R(T0, T1, ...) create a dynamic method binder of type R(R, T0, T1, ...)
final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2,
invType.parameterList().subList(1, invType.parameterCount()));
// Finally, fold the invocation into the binder to produce a method handle that will bind every returned
// DynamicMethod object from dyn:getMethod calls to the actual receiver
// R(R(T0, T1, ...), T0, T1, ...)
final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation);
final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type);
final MethodHandle adaptedInvocation;
if(hasFixedName) {
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter);
} else {
// Add a filter that'll prepend "super$" to each name passed to the variable-name "dyn:getMethod".
final MethodHandle typedAddPrefix = asFilterType(ADD_PREFIX_TO_METHOD_NAME, 1, invType, type);
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter, typedAddPrefix);
}
return guardedInv.replaceMethods(adaptedInvocation, guard).asType(descriptor);
}
private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> ops) throws Exception {
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);
// 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
// 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(
AnnotatedDynamicMethod.class));
final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup(), linkerServices);
final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
callSiteBoundMethodGetter);
// 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
// Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
type.parameterType(1));
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
linkerServices, ops);
final MethodHandle fallbackFolded;
if(nextComponent == 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 {
// 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(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) {
return getClassGuardedInvocationComponent(compositeGetter, type);
}
return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
}
case 3: {
// Must have exactly one argument: receiver
assertParameterCount(callSiteDescriptor, 1);
// Fixed name
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.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
// we're linking against a field getter, don't make the assumption.
// 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(getter, getGuard(validationType,
callSiteDescriptor.getMethodType()), clazz, validationType);
}
default: {
// Can't do anything with more than 3 name components
return null;
}
}
}
@Override
public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
final MethodHandle mh = MethodHandles.filterArguments(target, pos, filters);
return debug(mh, "filterArguments", target, pos, filters);
}
@Override
public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
final MethodHandle mh = MethodHandles.filterArguments(target, pos, filters);
return debug(mh, "filterArguments", target, pos, filters);
}
@Override
public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
return MethodHandles.filterArguments(target, pos, filters);
}
@Override
public MethodHandle filterArguments(final MethodHandle target, final int pos, final MethodHandle... filters) {
final MethodHandle mh = MethodHandles.filterArguments(target, pos, filters);
return debug(mh, "filterArguments", target, pos, filters);
}
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
throws Exception {
final Object objSuperAdapter = linkRequest.getReceiver();
if(!(objSuperAdapter instanceof JavaSuperAdapter)) {
return null;
}
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
if(!CallSiteDescriptorFactory.tokenizeOperators(descriptor).contains(GET_METHOD)) {
// We only handle getMethod
return null;
}
final Object adapter = ((JavaSuperAdapter)objSuperAdapter).getAdapter();
// Replace argument (javaSuperAdapter, ...) => (adapter, ...) when delegating to BeansLinker
final Object[] args = linkRequest.getArguments();
args[0] = adapter;
// Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker.
final MethodType type = descriptor.getMethodType();
final Class<?> adapterClass = adapter.getClass();
final boolean hasFixedName = descriptor.getNameTokenCount() > 2;
final String opName = hasFixedName ? (DYN_GET_METHOD_FIXED + descriptor.getNameToken(
CallSiteDescriptor.NAME_OPERAND)) : DYN_GET_METHOD;
final CallSiteDescriptor newDescriptor = NashornCallSiteDescriptor.get(descriptor.getLookup(), opName,
type.changeParameterType(0, adapterClass), 0);
// Delegate to BeansLinker
final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation(
BeansLinker.getLinkerForClass(adapterClass), linkRequest.replaceArguments(newDescriptor, args),
linkerServices);
final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass);
if(guardedInv == null) {
// Short circuit the lookup here for non-existent methods by linking an empty getter. If we just returned
// null instead, BeansLinker would find final methods on the JavaSuperAdapter instead: getClass() and
// wait().
return new GuardedInvocation(MethodHandles.dropArguments(EMPTY_GETTER, 1,type.parameterList().subList(1,
type.parameterCount())), guard).asType(descriptor);
}
final MethodHandle invocation = guardedInv.getInvocation();
final MethodType invType = invocation.type();
// For invocation typed R(T0, ...) create a dynamic method binder of type R(R, T0)
final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(invType.returnType(),
invType.returnType(), invType.parameterType(0)));
// For invocation typed R(T0, T1, ...) create a dynamic method binder of type R(R, T0, T1, ...)
final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2,
invType.parameterList().subList(1, invType.parameterCount()));
// Finally, fold the invocation into the binder to produce a method handle that will bind every returned
// DynamicMethod object from dyn:getMethod calls to the actual receiver
// R(R(T0, T1, ...), T0, T1, ...)
final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation);
final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type);
final MethodHandle adaptedInvocation;
if(hasFixedName) {
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter);
} else {
// Add a filter that'll prepend "super$" to each name passed to the variable-name "dyn:getMethod".
final MethodHandle typedAddPrefix = asFilterType(ADD_PREFIX_TO_METHOD_NAME, 1, invType, type);
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter, typedAddPrefix);
}
return guardedInv.replaceMethods(adaptedInvocation, guard).asType(descriptor);
}