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