/** * Returns a correctly typed {@link MethodHandle} for the no-arg ctor of the given class. */ static final MethodHandle findAttributeImplCtor(Class<? extends AttributeImpl> clazz) { try { return lookup.findConstructor(clazz, NO_ARG_CTOR).asType(NO_ARG_RETURNING_ATTRIBUTEIMPL); } catch (NoSuchMethodException | IllegalAccessException e) { throw new IllegalArgumentException("Cannot lookup accessible no-arg constructor for: " + clazz.getName(), e); } }
@Override public ScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) { List<Type> types = this.typeParameters.stream().map(boundVariables::getTypeVariable).collect(toImmutableList()); List<ArgumentProperty> argumentProperties = nCopies(types.size(), valueTypeArgumentProperty(RETURN_NULL_ON_NULL)); List<Class<?>> javaArgumentTypes = nCopies(types.size(), Block.class); MethodHandle methodHandle = METHOD_HANDLE.bindTo(types).asVarargsCollector(Block[].class).asType(methodType(Block.class, javaArgumentTypes)); return new ScalarFunctionImplementation(false, argumentProperties, methodHandle, isDeterministic()); }
@SuppressWarnings("UnusedDeclaration") public static CallSite bootstrapIntrinsic(MethodHandles.Lookup caller, String name, MethodType type, String callee) throws IllegalAccessException { MethodHandle mh = getMethodHandle(callee, name, type); if (mh == null) { throw new IllegalArgumentException("Could not find intrinsic for " + callee + ":" + name); } return new ConstantCallSite(mh.asType(type)); }
private ObjectStrategy(FunctionRegistry registry, Type type) { hashCodeHandle = registry.getScalarFunctionImplementation(registry.resolveOperator(HASH_CODE, ImmutableList.of(type))) .getMethodHandle() .asType(MethodType.methodType(long.class, Object.class)); equalsHandle = registry.getScalarFunctionImplementation(registry.resolveOperator(EQUAL, ImmutableList.of(type, type))) .getMethodHandle() .asType(MethodType.methodType(Boolean.class, Object.class, Object.class)); }
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 Optional<MethodHandle> getConstructor(Method method, Optional<Constructor<?>> optionalConstructor) { if (isStatic(method.getModifiers())) { return Optional.empty(); } checkArgument(optionalConstructor.isPresent(), "Method [%s] is an instance method. It must be in a class annotated with @ScalarFunction, and the class is required to have a public constructor.", method); Constructor<?> constructor = optionalConstructor.get(); Set<TypeParameter> constructorTypeParameters = Stream.of(constructor.getAnnotationsByType(TypeParameter.class)) .collect(ImmutableSet.toImmutableSet()); checkArgument(constructorTypeParameters.containsAll(typeParameters), "Method [%s] is an instance method and requires a public constructor containing all type parameters: %s", method, typeParameters); for (int i = 0; i < constructor.getParameterCount(); i++) { Annotation[] annotations = constructor.getParameterAnnotations()[i]; checkArgument(containsImplementationDependencyAnnotation(annotations), "Constructors may only have meta parameters [%s]", constructor); checkArgument(annotations.length == 1, "Meta parameters may only have a single annotation [%s]", constructor); Annotation annotation = annotations[0]; if (annotation instanceof TypeParameter) { checkTypeParameters(parseTypeSignature(((TypeParameter) annotation).value()), typeParameterNames, method); } constructorDependencies.add(createDependency(annotation, literalParameters)); } MethodHandle result = constructorMethodHandle(FUNCTION_IMPLEMENTATION_ERROR, constructor); // Change type of return value to Object to make sure callers won't have classloader issues return Optional.of(result.asType(result.type().changeReturnType(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; } } }
/** * Core method for indy method selection using runtime types. */ public static Object selectMethod(MutableCallSite callSite, Class sender, String methodName, int callID, Boolean safeNavigation, Boolean thisCall, Boolean spreadCall, Object dummyReceiver, Object[] arguments) throws Throwable { Selector selector = Selector.getSelector(callSite, sender, methodName, callID, safeNavigation, thisCall, spreadCall, arguments); selector.setCallSiteTarget(); MethodHandle call = selector.handle.asSpreader(Object[].class, arguments.length); call = call.asType(MethodType.methodType(Object.class,Object[].class)); return call.invokeExact(arguments); }
/** * Sets a handle to call {@link GroovyInterceptable#invokeMethod(String, Object)} */ public boolean setInterceptor() { if (!(this.args[0] instanceof GroovyInterceptable)) return false; handle = MethodHandles.insertArguments(INTERCEPTABLE_INVOKER, 1, this.name); handle = handle.asCollector(Object[].class, targetType.parameterCount()-1); handle = handle.asType(targetType); return true; }
@Override public ScalarFunctionImplementation specialize( BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) { Type toType = boundVariables.getTypeVariable("E"); MethodHandle methodHandle = METHOD_HANDLE_NON_NULL.asType(METHOD_HANDLE_NON_NULL.type().changeReturnType(toType.getJavaType())); return new ScalarFunctionImplementation( false, ImmutableList.of(valueTypeArgumentProperty(RETURN_NULL_ON_NULL)), methodHandle, isDeterministic()); }
private MethodHandle getMethodHandle(Method method) { MethodHandle methodHandle = methodHandle(FUNCTION_IMPLEMENTATION_ERROR, method); if (!isStatic(method.getModifiers())) { // Change type of "this" argument to Object to make sure callers won't have classloader issues methodHandle = methodHandle.asType(methodHandle.type().changeParameterType(0, Object.class)); // Re-arrange the parameters, so that the "this" parameter is after the meta parameters int[] permutedIndices = new int[methodHandle.type().parameterCount()]; permutedIndices[0] = dependencies.size(); MethodType newType = methodHandle.type().changeParameterType(dependencies.size(), methodHandle.type().parameterType(0)); for (int i = 0; i < dependencies.size(); i++) { permutedIndices[i + 1] = i; newType = newType.changeParameterType(i, methodHandle.type().parameterType(i + 1)); } for (int i = dependencies.size() + 1; i < permutedIndices.length; i++) { permutedIndices[i] = i; } methodHandle = permuteArguments(methodHandle, newType, permutedIndices); } return methodHandle; }
@Override public ScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) { Type returnType = boundVariables.getTypeVariable("T"); return new ScalarFunctionImplementation( true, ImmutableList.of(functionTypeArgumentProperty(InvokeLambda.class)), METHOD_HANDLE.asType( METHOD_HANDLE.type() .changeReturnType(wrap(returnType.getJavaType()))), isDeterministic()); }
private static MethodHandle cleanStackTraces(MethodHandle mh) { MethodType type = EXCEPTION_HANDLER.type().changeReturnType(mh.type().returnType()); return catchException(mh, Throwable.class, EXCEPTION_HANDLER.asType(type)); } }
@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); } }
private static BlockBuilder createMapBuilder(int expectedEntries) { MethodHandle varcharNativeEquals = MethodHandleUtil.methodHandle(Slice.class, "equals", Object.class).asType(MethodType.methodType(boolean.class, Slice.class, Slice.class)); MethodHandle varcharBlockNativeEquals = compose(varcharNativeEquals, nativeValueGetter(VARCHAR)); MethodHandle varcharBlockEquals = compose(varcharNativeEquals, nativeValueGetter(VARCHAR), nativeValueGetter(VARCHAR)); return new MapBlockBuilder( VARCHAR, VARCHAR, varcharBlockNativeEquals, varcharBlockEquals, MethodHandleUtil.methodHandle(Slice.class, "hashCode").asType(MethodType.methodType(long.class, Slice.class)), MethodHandleUtil.methodHandle(TestColumnarMap.class, "blockVarcharHashCode", Block.class, int.class), null, expectedEntries); }
@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 bindWithFallback(RoboCallSite site, MethodHandle mh, MethodHandle fallback) { SwitchPoint switchPoint = getInvalidator(site.getTheClass()); MethodType type = site.type(); MethodHandle boundFallback = foldArguments(exactInvoker(type), fallback.bindTo(site)); try { mh = switchPoint.guardWithTest(mh.asType(type), boundFallback); } catch (WrongMethodTypeException e) { if (site instanceof MethodCallSite) { MethodCallSite methodCallSite = (MethodCallSite) site; throw new RuntimeException("failed to bind " + methodCallSite.thisType() + "." + methodCallSite.getName(), e); } else { throw e; } } site.setTarget(mh); return mh; }
@Override public ScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) { Type argumentType = boundVariables.getTypeVariable("T"); Type returnType = boundVariables.getTypeVariable("U"); return new ScalarFunctionImplementation( true, ImmutableList.of( valueTypeArgumentProperty(USE_BOXED_TYPE), functionTypeArgumentProperty(UnaryFunctionInterface.class)), METHOD_HANDLE.asType( METHOD_HANDLE.type() .changeReturnType(wrap(returnType.getJavaType())) .changeParameterType(0, wrap(argumentType.getJavaType()))), isDeterministic()); }
@Override public ScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) { Type inputType = boundVariables.getTypeVariable("T"); Type intermediateType = boundVariables.getTypeVariable("S"); Type outputType = boundVariables.getTypeVariable("R"); MethodHandle methodHandle = METHOD_HANDLE.bindTo(inputType); return new ScalarFunctionImplementation( true, ImmutableList.of( valueTypeArgumentProperty(RETURN_NULL_ON_NULL), valueTypeArgumentProperty(USE_BOXED_TYPE), functionTypeArgumentProperty(BinaryFunctionInterface.class), functionTypeArgumentProperty(UnaryFunctionInterface.class)), methodHandle.asType( methodHandle.type() .changeParameterType(1, Primitives.wrap(intermediateType.getJavaType())) .changeReturnType(Primitives.wrap(outputType.getJavaType()))), isDeterministic()); }
/** * 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()))); }