001/* 002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010package org.eclipse.golo.runtime; 011 012import gololang.annotations.DecoratedBy; 013 014import java.lang.invoke.MethodHandle; 015import java.lang.invoke.MethodHandles; 016import java.lang.invoke.MethodHandles.Lookup; 017import java.lang.invoke.MethodType; 018import java.lang.reflect.Method; 019 020import static java.lang.invoke.MethodType.methodType; 021 022public final class DecoratorsHelper { 023 024 private static final MethodHandle FUNCTION_REFERENCE_TO_METHODHANDLE; 025 private static final MethodHandle INVOKE_WITH_ARGUMENTS; 026 027 static { 028 try { 029 Lookup lookup = MethodHandles.lookup(); 030 FUNCTION_REFERENCE_TO_METHODHANDLE = lookup.findStatic( 031 DecoratorsHelper.class, 032 "functionReferenceToMethodHandle", 033 MethodType.methodType(Object.class, Object.class)); 034 INVOKE_WITH_ARGUMENTS = lookup.findVirtual( 035 MethodHandle.class, 036 "invokeWithArguments", 037 MethodType.methodType(Object.class, Object[].class)); 038 } catch (NoSuchMethodException | IllegalAccessException e) { 039 throw new Error("Could not bootstrap the required method handles", e); 040 } 041 } 042 043 private DecoratorsHelper() { 044 } 045 046 public static boolean isMethodDecorated(Method method) { 047 return method.isAnnotationPresent(DecoratedBy.class); 048 } 049 050 public static Method getDecoratorMethod(Method decorated) { 051 try { 052 return decorated.getDeclaringClass().getDeclaredMethod(decorated.getAnnotation(DecoratedBy.class).value(), Object.class); 053 } catch (NoSuchMethodException | SecurityException ex) { 054 throw new IllegalStateException("Unable to get the decorator for a method marked as decorated", ex); 055 } 056 } 057 058 private static Object functionReferenceToMethodHandle(Object retValue) { 059 return ((gololang.FunctionReference) retValue).handle(); 060 } 061 062 public static MethodHandle getDecoratedMethodHandle(Lookup caller, Method originalMethod, int arity) { 063 try { 064 Method decoratorMethod = getDecoratorMethod(originalMethod); 065 MethodHandle decorator = caller.unreflect(decoratorMethod); 066 decorator = MethodHandles.filterReturnValue(decorator, FUNCTION_REFERENCE_TO_METHODHANDLE); 067 MethodHandle original = caller.unreflect(originalMethod); 068 decorator = decorator.bindTo(new gololang.FunctionReference(original)).asType(methodType(MethodHandle.class)); 069 if (arity < 0) { 070 MethodHandle combined = MethodHandles.foldArguments(INVOKE_WITH_ARGUMENTS, decorator); 071 return combined.asVarargsCollector(Object[].class); 072 } else { 073 MethodHandle invoker = MethodHandles.invoker(MethodType.genericMethodType(arity)); 074 return MethodHandles.foldArguments(invoker, decorator); 075 } 076 } catch (IllegalAccessException ex) { 077 throw new IllegalStateException("Unable to get the decorator for a method marked as decorated", ex); 078 } 079 } 080 081 public static MethodHandle getDecoratedMethodHandle(Method originalMethod, int arity) { 082 return getDecoratedMethodHandle(MethodHandles.lookup(), originalMethod, arity); 083 } 084 085 public static MethodHandle getDecoratedMethodHandle(Method originalMethod) { 086 return getDecoratedMethodHandle(MethodHandles.lookup(), originalMethod, -1); 087 } 088}