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}