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}