/** * 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; }
static MethodHandle alwaysSet() { return MethodHandles.dropArguments(MethodHandles.constant(boolean.class, true), 0, Object.class); }
@Override public MethodHandle getMethodHandle(String methodName, MethodType type) throws NoSuchMethodException, IllegalAccessException { MethodHandle nothing = constant(Void.class, null).asType(methodType(void.class)); if (type.parameterCount() != 0) { return dropArguments(nothing, 0, type.parameterArray()); } else { return nothing; } } }
/** * Set MOP based constructor invocation path. */ @Override public void setMetaClassCallHandleIfNedded(boolean standardMetaClass) { if (handle!=null) return; useMetaClass = true; if (LOG_ENABLED) LOG.info("set meta class invocation path"); handle = MOP_INVOKE_CONSTRUCTOR.bindTo(mc); handle = handle.asCollector(Object[].class, targetType.parameterCount()-1); handle = MethodHandles.dropArguments(handle, 0, Class.class); if (LOG_ENABLED) LOG.info("create collector for arguments"); } }
/** * Gives a replacement receiver for null. * In case of the receiver being null we want to do the method * invocation on NullObject instead. */ public void correctNullReceiver() { if (args[0]!=null) return; handle = handle.bindTo(NullObject.getNullObject()); handle = MethodHandles.dropArguments(handle, 0, targetType.parameterType(0)); if (LOG_ENABLED) LOG.info("binding null object receiver and dropping old receiver"); }
@Override public MethodHandle getShadowCreator(Class<?> theClass) { return dropArguments(constant(String.class, "a shadow!"), 0, theClass); }
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; }
private static MethodHandle getMethodHandle(String className, String methodName, MethodType type) { Interceptor interceptor = INTERCEPTORS.findInterceptor(className, methodName); if (interceptor != null) { try { // reload interceptor in sandbox... Class<Interceptor> theClass = (Class<Interceptor>) ReflectionHelpers.loadClass( RobolectricInternals.getClassLoader(), interceptor.getClass().getName()).asSubclass(Interceptor.class); return ReflectionHelpers.newInstance(theClass).getMethodHandle(methodName, type); } catch (NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e); } } if (type.parameterCount() != 0) { return dropArguments(NOTHING, 0, type.parameterArray()); } else { return NOTHING; } }
private void handleBoolean() { if (handle!=null) return; // boolean->boolean, Boolean->boolean, boolean->Boolean // is handled by compiler // that leaves (T)Z and (T)Boolean, where T is the static type // but runtime type of T might be Boolean boolean primitive = staticTargetType==boolean.class; if (!primitive && staticTargetType!=Boolean.class) return; if (args[0]==null) { if (primitive) { handle = MethodHandles.constant(boolean.class, false); handle = MethodHandles.dropArguments(handle, 0, staticSourceType); } else { handle = BOOLEAN_IDENTITY; } } else if (args[0] instanceof Boolean) { // give value through or unbox handle = BOOLEAN_IDENTITY; } else { //call asBoolean name = "asBoolean"; super.setCallSiteTarget(); } } }
@SuppressWarnings("ReferenceEquality") @Override public MethodHandle findShadowMethodHandle(Class<?> definingClass, String name, MethodType methodType, boolean isStatic) throws IllegalAccessException { return PerfStatsCollector.getInstance().measure("find shadow method handle", () -> { MethodType actualType = isStatic ? methodType : methodType.dropParameterTypes(0, 1); Class<?>[] paramTypes = actualType.parameterArray(); Method shadowMethod = pickShadowMethod(definingClass, name, paramTypes); if (shadowMethod == CALL_REAL_CODE) { return null; } else if (shadowMethod == DO_NOTHING_METHOD) { return DO_NOTHING; } shadowMethod.setAccessible(true); MethodHandle mh = LOOKUP.unreflect(shadowMethod); // Robolectric doesn't actually look for static, this for example happens // in MessageQueue.nativeInit() which used to be void non-static in 4.2. if (!isStatic && Modifier.isStatic(shadowMethod.getModifiers())) { return dropArguments(mh, 0, Object.class); } else { return mh; } }); }
@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); } }
/** * @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); }
/** * @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); }
cast = MethodHandles.dropArguments(cast, 0, ConnectorSession.class); cast = MethodHandles.dropArguments(cast, 1, int.class); cast = MethodHandles.dropArguments(cast, 1, Block.class); cast = MethodHandles.foldArguments(cast, getter.bindTo(type));
con = MethodHandles.dropArguments(con, 1, targetType.parameterType(1)); foldTargetType = foldTargetType.insertParameterTypes(0, targetType.parameterType(1)); handle = MethodHandles.dropArguments(handle, 0, Class.class);
@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 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; }
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; }
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; } }
/** * 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()))); }