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; 012 013import gololang.FunctionReference; 014 015import java.lang.invoke.*; 016import java.lang.invoke.MethodHandles.Lookup; 017import java.util.Arrays; 018 019import static java.lang.invoke.MethodHandles.guardWithTest; 020import static java.lang.invoke.MethodType.methodType; 021 022public final class ClosureCallSupport { 023 024 private ClosureCallSupport() { 025 throw new UnsupportedOperationException("Don't instantiate invokedynamic bootstrap class"); 026 } 027 028 static class InlineCache extends MutableCallSite { 029 030 MethodHandle fallback; 031 final boolean constant; 032 final String[] argumentNames; 033 034 InlineCache(MethodType type, boolean constant, String[] argumentNames) { 035 super(type); 036 this.constant = constant; 037 this.argumentNames = argumentNames; 038 } 039 } 040 041 private static final MethodHandle GUARD; 042 private static final MethodHandle FALLBACK; 043 044 static { 045 try { 046 Lookup lookup = MethodHandles.lookup(); 047 048 GUARD = lookup.findStatic( 049 ClosureCallSupport.class, 050 "guard", 051 methodType(boolean.class, FunctionReference.class, FunctionReference.class)); 052 053 FALLBACK = lookup.findStatic( 054 ClosureCallSupport.class, 055 "fallback", 056 methodType(Object.class, InlineCache.class, Object[].class)); 057 } catch (NoSuchMethodException | IllegalAccessException e) { 058 throw new Error("Could not bootstrap the required method handles", e); 059 } 060 } 061 062 public static CallSite bootstrap(Lookup caller, String name, MethodType type, Object... bsmArgs) { 063 boolean constant = ((int) bsmArgs[0]) == 1; 064 String[] argumentNames = new String[bsmArgs.length - 1]; 065 for (int i = 0; i < bsmArgs.length - 1; i++) { 066 argumentNames[i] = (String) bsmArgs[i + 1]; 067 } 068 InlineCache callSite = new InlineCache(type, constant, argumentNames); 069 MethodHandle fallbackHandle = FALLBACK 070 .bindTo(callSite) 071 .asCollector(Object[].class, type.parameterCount()) 072 .asType(type); 073 callSite.fallback = fallbackHandle; 074 callSite.setTarget(fallbackHandle); 075 return callSite; 076 } 077 078 public static boolean guard(FunctionReference expected, FunctionReference actual) { 079 return expected == actual; 080 } 081 082 public static Object fallback(InlineCache callSite, Object[] args) throws Throwable { 083 FunctionReference targetFunctionReference = (FunctionReference) args[0]; 084 MethodHandle target = targetFunctionReference.handle(); 085 MethodHandle invoker = MethodHandles.dropArguments(target, 0, FunctionReference.class); 086 MethodType type = invoker.type(); 087 if (callSite.argumentNames.length > 0) { 088 invoker = reorderArguments( 089 targetFunctionReference.parameterNames(), 090 invoker, 091 callSite.argumentNames); 092 } 093 if (target.isVarargsCollector()) { 094 if (TypeMatching.isLastArgumentAnArray(type.parameterCount(), args)) { 095 invoker = invoker.asFixedArity().asType(callSite.type()); 096 } else { 097 invoker = invoker.asCollector( 098 Object[].class, 099 callSite.type().parameterCount() - target.type().parameterCount()) 100 .asType(callSite.type()); 101 } 102 } else { 103 invoker = invoker.asType(callSite.type()); 104 } 105 if (callSite.constant) { 106 Object constantValue = invoker.invokeWithArguments(args); 107 MethodHandle constant; 108 if (constantValue == null) { 109 constant = MethodHandles.constant(Object.class, null); 110 } else { 111 constant = MethodHandles.constant(constantValue.getClass(), constantValue); 112 } 113 constant = MethodHandles.dropArguments(constant, 0, type.parameterArray()); 114 callSite.setTarget(constant.asType(type)); 115 return constantValue; 116 } else { 117 MethodHandle guard = GUARD.bindTo(targetFunctionReference); 118 MethodHandle root = guardWithTest(guard, invoker, callSite.fallback); 119 callSite.setTarget(root); 120 return invoker.invokeWithArguments(args); 121 } 122 } 123 124 private static MethodHandle reorderArguments(String[] parameterNames, MethodHandle handle, String[] argumentNames) { 125 return NamedArgumentsHelper.reorderArguments( 126 "closure " + Arrays.toString(parameterNames), 127 Arrays.asList(parameterNames), 128 handle, 129 argumentNames, 1, 1); 130 } 131}