private Lookup getLookup(Class<?> declaringClass) { Lookup lookup = MethodHandles.lookup(); if (privateLookupIn != null) { try { return (Lookup) privateLookupIn.invoke(null, declaringClass, lookup); } catch (ReflectiveOperationException e) { return lookup; } } return lookup; }
private static MethodHandle findConstructor(String className, MethodType methodType) { try { return MethodHandles.publicLookup().findConstructor(Class.forName(className), methodType); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } }
private static MethodHandle unmapJava7Or8(MethodHandles.Lookup lookup) throws ReflectiveOperationException { /* "Compile" a MethodHandle that is roughly equivalent to the following lambda: * * (ByteBuffer buffer) -> { * sun.misc.Cleaner cleaner = ((java.nio.DirectByteBuffer) byteBuffer).cleaner(); * if (nonNull(cleaner)) * cleaner.clean(); * else * noop(cleaner); // the noop is needed because MethodHandles#guardWithTest always needs both if and else * } */ Class<?> directBufferClass = Class.forName("java.nio.DirectByteBuffer"); Method m = directBufferClass.getMethod("cleaner"); m.setAccessible(true); MethodHandle directBufferCleanerMethod = lookup.unreflect(m); Class<?> cleanerClass = directBufferCleanerMethod.type().returnType(); MethodHandle cleanMethod = lookup.findVirtual(cleanerClass, "clean", methodType(void.class)); MethodHandle nonNullTest = lookup.findStatic(MappedByteBuffers.class, "nonNull", methodType(boolean.class, Object.class)).asType(methodType(boolean.class, cleanerClass)); MethodHandle noop = dropArguments(constant(Void.class, null).asType(methodType(void.class)), 0, cleanerClass); MethodHandle unmapper = filterReturnValue(directBufferCleanerMethod, guardWithTest(nonNullTest, cleanMethod, noop)) .asType(methodType(void.class, ByteBuffer.class)); return unmapper; }
static MethodHandle alwaysSet() { return MethodHandles.dropArguments(MethodHandles.constant(boolean.class, true), 0, Object.class); }
private void handleNullWithoutBoolean() { if (handle!=null || args[0]!=null) return; if (staticTargetType.isPrimitive()) { handle = MethodHandles.insertArguments(GROOVY_CAST_EXCEPTION,1,staticTargetType); // need to call here here because we used the static target type // it won't be done otherwise because handle.type() == callSite.type() castAndSetGuards(); } else { handle = MethodHandles.identity(staticSourceType); } }
private static MethodHandle buildGetter(Class arrayClass) { MethodHandle get = MethodHandles.arrayElementGetter(arrayClass); MethodHandle fallback = MethodHandles.explicitCastArguments(get, get.type().changeParameterType(0, Object.class)); fallback = MethodHandles.dropArguments(fallback, 2, int.class); MethodType reorderType = fallback.type(). insertParameterTypes(0, int.class). dropParameterTypes(2,3); fallback = MethodHandles.permuteArguments(fallback, reorderType, 1, 0, 0); fallback = MethodHandles.foldArguments(fallback, normalizeIndex); fallback = MethodHandles.explicitCastArguments(fallback, get.type()); MethodHandle guard = MethodHandles.dropArguments(notNegative, 0, arrayClass); MethodHandle handle = MethodHandles.guardWithTest(guard, get, fallback); return handle; }
@Override public MethodHandle findShadowMethodHandle(Class<?> theClass, String name, MethodType type, boolean isStatic) throws IllegalAccessException { String signature = getSignature(theClass, name, type, isStatic); InvocationProfile invocationProfile = new InvocationProfile(signature, isStatic, getClass().getClassLoader()); try { MethodHandle mh = MethodHandles.lookup().findVirtual(getClass(), "invoke", methodType(Object.class, InvocationProfile.class, Object.class, Object[].class)); mh = insertArguments(mh, 0, this, invocationProfile); if (isStatic) { return mh.bindTo(null).asCollector(Object[].class, type.parameterCount()); } else { return mh.asCollector(Object[].class, type.parameterCount() - 1); } } catch (NoSuchMethodException e) { throw new AssertionError(e); } }
private static MethodHandle buildSetter(Class arrayClass){ MethodHandle set = MethodHandles.arrayElementSetter(arrayClass); MethodHandle fallback = MethodHandles.explicitCastArguments(set, set.type().changeParameterType(0, Object.class)); fallback = MethodHandles.dropArguments(fallback, 3, int.class); MethodType reorderType = fallback.type(). insertParameterTypes(0, int.class). dropParameterTypes(4,5); fallback = MethodHandles.permuteArguments(fallback, reorderType, 1, 0, 3, 0); fallback = MethodHandles.foldArguments(fallback, normalizeIndex); fallback = MethodHandles.explicitCastArguments(fallback, set.type()); MethodHandle guard = MethodHandles.dropArguments(notNegative, 0, arrayClass); MethodHandle handle = MethodHandles.guardWithTest(guard, set, fallback); return handle; }
cast = MethodHandles.dropArguments(cast, 0, ConnectorSession.class); cast = MethodHandles.permuteArguments(cast, MethodType.methodType(Slice.class, cast.type().parameterArray()[1], cast.type().parameterArray()[0]), 1, 0); cast = MethodHandles.dropArguments(cast, 1, int.class); cast = MethodHandles.dropArguments(cast, 1, Block.class); cast = MethodHandles.foldArguments(cast, getter.bindTo(type)); MethodHandle target = MethodHandles.insertArguments(methodHandle, 0, cast); return new ScalarFunctionImplementation( false,
@Override public ScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) { Type fromType = boundVariables.getTypeVariable("F"); Type toType = boundVariables.getTypeVariable("T"); Class<?> returnType = Primitives.wrap(toType.getJavaType()); List<ArgumentProperty> argumentProperties; MethodHandle tryCastHandle; // the resulting method needs to return a boxed type Signature signature = functionRegistry.getCoercion(fromType, toType); ScalarFunctionImplementation implementation = functionRegistry.getScalarFunctionImplementation(signature); argumentProperties = ImmutableList.of(implementation.getArgumentProperty(0)); MethodHandle coercion = implementation.getMethodHandle(); coercion = coercion.asType(methodType(returnType, coercion.type())); MethodHandle exceptionHandler = dropArguments(constant(returnType, null), 0, RuntimeException.class); tryCastHandle = catchException(coercion, RuntimeException.class, exceptionHandler); return new ScalarFunctionImplementation(true, argumentProperties, tryCastHandle, isDeterministic()); } }
private void castToTypeFallBack() { if (handle!=null) return; // generic fallback to castToType handle = MethodHandles.insertArguments(DTT_CAST_TO_TYPE, 1, staticTargetType); }
/** * @param f (U, S1, S2, ..., Sm)R * @param g (T1, T2, ..., Tn)U * @return (T1, T2, ..., Tn, S1, S2, ..., Sm)R */ public static MethodHandle compose(MethodHandle f, MethodHandle g) { if (f.type().parameterType(0) != g.type().returnType()) { throw new IllegalArgumentException(format("f.parameter(0) != g.return(). f: %s g: %s", f.type(), g.type())); } // Semantics: f => f // Type: (U, S1, S2, ..., Sn)R => (U, T1, T2, ..., Tm, S1, S2, ..., Sn)R MethodHandle fUTS = MethodHandles.dropArguments(f, 1, g.type().parameterList()); // Semantics: f => fg // Type: (U, T1, T2, ..., Tm, S1, S2, ..., Sn)R => (T1, T2, ..., Tm, S1, S2, ..., Sn)R return MethodHandles.foldArguments(fUTS, g); }
@Override public MethodHandle getShadowCreator(Class<?> theClass) { ShadowInfo shadowInfo = getShadowInfo(theClass); if (shadowInfo == null) return dropArguments(NO_SHADOW_HANDLE, 0, theClass); String shadowClassName = shadowInfo.shadowClassName; try { Class<?> shadowClass = Class.forName(shadowClassName, false, theClass.getClassLoader()); ShadowMetadata shadowMetadata = getShadowMetadata(shadowClass); MethodHandle mh = identity(shadowClass); // (instance) mh = dropArguments(mh, 1, theClass); // (instance) for (Field field : shadowMetadata.realObjectFields) { MethodHandle setter = LOOKUP.unreflectSetter(field); MethodType setterType = mh.type().changeReturnType(void.class); mh = foldArguments(mh, setter.asType(setterType)); } mh = foldArguments(mh, LOOKUP.unreflectConstructor(shadowMetadata.constructor)); // (shadow, instance) return mh; // (instance) } catch (IllegalAccessException | ClassNotFoundException e) { throw new RuntimeException("Could not instantiate shadow " + shadowClassName + " for " + theClass, e); } }
/** * Sets the null constant for safe navigation. * In case of foo?.bar() and foo being null, we don't call the method, * instead we simply return null. This produces a handle, which will * return the constant. */ public boolean setNullForSafeNavigation() { if (!safeNavigation) return false; handle = MethodHandles.dropArguments(NULL_REF,0,targetType.parameterArray()); if (LOG_ENABLED) LOG.info("set null returning handle for safe navigation"); return true; }
if (LOG_ENABLED) LOG.info("successfully unreflected method"); if (isStaticCategoryTypeMethod) { handle = MethodHandles.insertArguments(handle, 0, new Object[]{null}); handle = MethodHandles.dropArguments(handle, 0, targetType.parameterType(0)); } else if (!isCategoryTypeMethod && isStatic(m)) { handle = MethodHandles.dropArguments(handle, 0, Object.class);
handle = MethodHandles.guardWithTest(test, handle, fallback); if (LOG_ENABLED) LOG.info("added meta class equality check"); } else if (receiver instanceof Class) { MethodHandle test = EQUALS.bindTo(receiver); test = test.asType(MethodType.methodType(boolean.class,targetType.parameterType(0))); handle = MethodHandles.guardWithTest(test, handle, fallback); if (LOG_ENABLED) LOG.info("added class equality check"); handle = MethodHandles.guardWithTest(HAS_CATEGORY_IN_CURRENT_THREAD_GUARD, handle, fallback); if (LOG_ENABLED) LOG.info("added category-in-current-thread-guard for category method"); test = MethodHandles.dropArguments(test, 0, drops); handle = MethodHandles.guardWithTest(test, handle, fallback);
public Binding bind(Object constant, Class<?> type) { return bind(MethodHandles.constant(type, constant)); }
/** * The signature of the returned MethodHandle is (Block fromMap, int position, ConnectorSession session, BlockBuilder mapBlockBuilder)void. * The processor will get the value from fromMap, cast it and write to toBlock. */ private MethodHandle buildProcessor(FunctionRegistry functionRegistry, Type fromType, Type toType, boolean isKey) { MethodHandle getter = nativeValueGetter(fromType); // Adapt cast that takes ([ConnectorSession,] ?) to one that takes (?, ConnectorSession), where ? is the return type of getter. ScalarFunctionImplementation castImplementation = functionRegistry.getScalarFunctionImplementation(functionRegistry.getCoercion(fromType, toType)); MethodHandle cast = castImplementation.getMethodHandle(); if (cast.type().parameterArray()[0] != ConnectorSession.class) { cast = MethodHandles.dropArguments(cast, 0, ConnectorSession.class); } cast = permuteArguments(cast, methodType(cast.type().returnType(), cast.type().parameterArray()[1], cast.type().parameterArray()[0]), 1, 0); MethodHandle target = compose(cast, getter); // If the key cast function is nullable, check the result is not null. if (isKey && castImplementation.isNullable()) { target = compose(nullChecker(target.type().returnType()), target); } MethodHandle writer = nativeValueWriter(toType); writer = permuteArguments(writer, methodType(void.class, writer.type().parameterArray()[1], writer.type().parameterArray()[0]), 1, 0); return compose(writer, target.asType(methodType(unwrap(target.type().returnType()), target.type().parameterArray()))); }
private static MethodHandle bindCallSite(MethodCallSite site) throws IllegalAccessException { MethodHandle mh = RobolectricInternals.findShadowMethodHandle(site.getTheClass(), site.getName(), site.type(), site.isStatic()); if (mh == null) { // call original code mh = site.getOriginal(); } else if (mh == ShadowWrangler.DO_NOTHING) { // no-op mh = dropArguments(mh, 0, site.type().parameterList()); } else if (!site.isStatic()) { // drop arg 0 (this) for static methods Class<?> shadowType = mh.type().parameterType(0); mh = filterArguments(mh, 0, GET_SHADOW.asType(methodType(shadowType, site.thisType()))); } try { return bindWithFallback(site, cleanStackTraces(mh), BIND_CALL_SITE); } catch (Throwable t) { // The error that bubbles up is currently not very helpful so we print any error messages // here t.printStackTrace(); System.err.println(site.getTheClass()); throw t; } }
/** * Creates a MethodHandle, which will use the meta class path. * This method is called only if no handle has been created before. This * is usually the case if the method selection failed. */ public void setMetaClassCallHandleIfNedded(boolean standardMetaClass) { if (handle!=null) return; useMetaClass = true; if (LOG_ENABLED) LOG.info("set meta class invocation path"); Object receiver = getCorrectedReceiver(); if (receiver instanceof Class) { handle = META_CLASS_INVOKE_STATIC_METHOD.bindTo(mc); if (LOG_ENABLED) LOG.info("use invokeStaticMethod with bound meta class"); } else { handle = MOP_INVOKE_METHOD.bindTo(mc); if (LOG_ENABLED) LOG.info("use invokeMethod with bound meta class"); if (receiver instanceof GroovyObject) { // if the meta class call fails we may still want to fall back to call // GroovyObject#invokeMethod if the receiver is a GroovyObject if (LOG_ENABLED) LOG.info("add MissingMethod handler for GrooObject#invokeMethod fallback path"); handle = MethodHandles.catchException(handle, MissingMethodException.class, GROOVY_OBJECT_INVOKER); } } handle = MethodHandles.insertArguments(handle, 1, name); if (!spread) handle = handle.asCollector(Object[].class, targetType.parameterCount()-1); if (LOG_ENABLED) LOG.info("bind method name and create collector for arguments"); }