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