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 */
009
010package org.eclipse.golo.runtime.adapters;
011
012import gololang.FunctionReference;
013
014import java.lang.invoke.*;
015import java.util.Map;
016
017import static java.lang.invoke.MethodType.genericMethodType;
018
019public final class AdapterSupport {
020
021  private AdapterSupport() {
022    throw new UnsupportedOperationException("Don't instantiate invokedynamic bootstrap class");
023  }
024
025  public static final String DEFINITION_FIELD = "_$_$adapter_$definition";
026
027  private static final MethodHandle FALLBACK;
028
029  static {
030    MethodHandles.Lookup lookup = MethodHandles.lookup();
031    try {
032      FALLBACK = lookup.findStatic(AdapterSupport.class, "fallback",
033          MethodType.methodType(Object.class, AdapterCallSite.class, Object[].class));
034    } catch (NoSuchMethodException | IllegalAccessException e) {
035      throw new Error("Could not bootstrap the required method handles", e);
036    }
037  }
038
039  static final class AdapterCallSite extends MutableCallSite {
040
041    final MethodHandles.Lookup callerLookup;
042    final String name;
043
044    AdapterCallSite(MethodType type, MethodHandles.Lookup callerLookup, String name) {
045      super(type);
046      this.callerLookup = callerLookup;
047      this.name = name;
048    }
049  }
050
051  public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) {
052    AdapterCallSite callSite = new AdapterCallSite(type, caller, name);
053    MethodHandle fallbackHandle = FALLBACK
054        .bindTo(callSite)
055        .asCollector(Object[].class, type.parameterCount())
056        .asType(type);
057    callSite.setTarget(fallbackHandle);
058    return callSite;
059  }
060
061  public static Object fallback(AdapterCallSite callSite, Object[] args) throws Throwable {
062    Class<?> receiverClass = args[0].getClass();
063    Class<?> receiverParentClass = receiverClass.getSuperclass();
064    AdapterDefinition definition = (AdapterDefinition) receiverClass.getField(DEFINITION_FIELD).get(args[0]);
065    Map<String, FunctionReference> implementations = definition.getImplementations();
066    MethodHandle target = null;
067    if (implementations.containsKey(callSite.name)) {
068      target = implementations.get(callSite.name).handle();
069    }
070    if (target == null) {
071      if (implementations.containsKey("*")) {
072        target = implementations.get("*").handle();
073        target = target.bindTo(callSite.name).asCollector(Object[].class, args.length);
074      }
075    }
076    if (target == null) {
077      Map<String, FunctionReference> overrides = definition.getOverrides();
078      MethodHandle superTarget = callSite.callerLookup.findSpecial(receiverParentClass,
079          callSite.name, callSite.type().dropParameterTypes(0, 1), receiverClass);
080      if (superTarget.isVarargsCollector()) {
081        superTarget = superTarget.asType(genericMethodType(superTarget.type().parameterCount() - 1, true))
082          .asVarargsCollector(Object[].class);
083      } else {
084        superTarget = superTarget.asType(genericMethodType(superTarget.type().parameterCount()));
085      }
086      if (overrides.containsKey(callSite.name)) {
087        target = overrides.get(callSite.name).handle();
088      }
089      boolean star = false;
090      if (target == null) {
091        if (overrides.containsKey("*")) {
092          target = overrides.get("*").handle();
093        }
094        star = true;
095      }
096      if (target == null) {
097        target = superTarget;
098      } else {
099        target = target.bindTo(new FunctionReference(superTarget));
100        if (star) {
101          target = target.bindTo(callSite.name);
102          target = target.asCollector(Object[].class, args.length);
103        }
104      }
105    }
106    callSite.setTarget(target.asType(callSite.type()));
107    return target.invokeWithArguments(args);
108  }
109}