/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.codehaus.groovy.vmplugin.v7; import groovy.lang.GroovySystem; import groovy.lang.MetaClassRegistryChangeEvent; import groovy.lang.MetaClassRegistryChangeEventListener; import java.lang.invoke.*; import java.lang.invoke.MethodHandles.Lookup; import java.util.logging.Level; import java.util.logging.Logger; import org.codehaus.groovy.GroovyBugError; /** * Bytecode level interface for bootstrap methods used by invokedynamic. * This class provides a logging ability by using the boolean system property * groovy.indy.logging. Other than that this class contains the * interfacing methods with bytecode for invokedynamic as well as some helper * methods and classes. * * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a> */ public class IndyInterface { /** * flags for method and property calls */ public static final int SAFE_NAVIGATION = 1, THIS_CALL = 2, GROOVY_OBJECT = 4, IMPLICIT_THIS = 8, SPREAD_CALL = 16, UNCACHED_CALL = 32; /** * Enum for easy differentiation between call types * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a> */ public static enum CALL_TYPES { /**Method invocation type*/ METHOD("invoke"), /**Constructor invocation type*/ INIT("init"), /**Get property invocation type*/ GET("getProperty"), /**Set property invocation type*/ SET("setProperty"), /**Cast invocation type*/ CAST("cast"); /**The name of the call site type*/ private final String name; private CALL_TYPES(String callSiteName) { this.name = callSiteName; } /** Returns the name of the call site type */ public String getCallSiteName(){ return name; } } /** Logger */ protected static final Logger LOG; /** boolean to indicate if logging for indy is enabled */ protected static final boolean LOG_ENABLED; static { boolean enableLogger = false; LOG = Logger.getLogger(IndyInterface.class.getName()); try { if (System.getProperty("groovy.indy.logging")!=null) { LOG.setLevel(Level.ALL); enableLogger = true; } } catch (SecurityException e) { // Allow security managers to prevent system property access } LOG_ENABLED = enableLogger; } /** LOOKUP constant used for for example unreflect calls */ public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); /** handle for the selectMethod method */ private static final MethodHandle SELECT_METHOD; static { MethodType mt = MethodType.methodType(Object.class, MutableCallSite.class, Class.class, String.class, int.class, Boolean.class, Boolean.class, Boolean.class, Object.class, Object[].class); try { SELECT_METHOD = LOOKUP.findStatic(IndyInterface.class, "selectMethod", mt); } catch (Exception e) { throw new GroovyBugError(e); } } protected static SwitchPoint switchPoint = new SwitchPoint(); static { GroovySystem.getMetaClassRegistry().addMetaClassRegistryChangeEventListener(new MetaClassRegistryChangeEventListener() { public void updateConstantMetaClass(MetaClassRegistryChangeEvent cmcu) { invalidateSwitchPoints(); } }); } /** * Callback for constant meta class update change */ protected static void invalidateSwitchPoints() { if (LOG_ENABLED) { LOG.info("invalidating switch point"); } synchronized(IndyInterface.class) { SwitchPoint old = switchPoint; switchPoint = new SwitchPoint(); SwitchPoint.invalidateAll(new SwitchPoint[]{old}); } } /** * bootstrap method for method calls from Groovy compiled code with indy * enabled. This method gets a flags parameter which uses the following * encoding:<ul> * <li>{@value #SAFE_NAVIGATION} is the flag value for safe navigation see {@link #SAFE_NAVIGATION}<li/> * <li>{@value #THIS_CALL} is the flag value for a call on this see {@link #THIS_CALL}</li> * </ul> * @param caller - the caller * @param callType - the type of the call * @param type - the call site type * @param name - the real method name * @param flags - call flags * @return the produced CallSite * @since Groovy 2.1.0 */ public static CallSite bootstrap(Lookup caller, String callType, MethodType type, String name, int flags) { boolean safe = (flags&SAFE_NAVIGATION)!=0; boolean thisCall = (flags&THIS_CALL)!=0; boolean spreadCall = (flags&SPREAD_CALL)!=0; int callID; if (callType.equals(CALL_TYPES.METHOD.getCallSiteName())) { callID = CALL_TYPES.METHOD.ordinal(); } else if (callType.equals(CALL_TYPES.INIT.getCallSiteName())) { callID = CALL_TYPES.INIT.ordinal(); } else if (callType.equals(CALL_TYPES.GET.getCallSiteName())) { callID = CALL_TYPES.GET.ordinal(); } else if (callType.equals(CALL_TYPES.SET.getCallSiteName())) { callID = CALL_TYPES.SET.ordinal(); } else if (callType.equals(CALL_TYPES.CAST.getCallSiteName())) { callID = CALL_TYPES.CAST.ordinal(); }else { throw new GroovyBugError("Unknown call type: "+callType); } return realBootstrap(caller, name, callID, type, safe, thisCall, spreadCall); } /** * bootstrap method for method calls with "this" as receiver * @deprecated since Groovy 2.1.0 */ @Deprecated public static CallSite bootstrapCurrent(Lookup caller, String name, MethodType type) { return realBootstrap(caller, name, CALL_TYPES.METHOD.ordinal(), type, false, true, false); } /** * bootstrap method for method calls with "this" as receiver safe * @deprecated since Groovy 2.1.0 */ @Deprecated public static CallSite bootstrapCurrentSafe(Lookup caller, String name, MethodType type) { return realBootstrap(caller, name, CALL_TYPES.METHOD.ordinal(), type, true, true, false); } /** * bootstrap method for standard method calls * @deprecated since Groovy 2.1.0 */ @Deprecated public static CallSite bootstrap(Lookup caller, String name, MethodType type) { return realBootstrap(caller, name, CALL_TYPES.METHOD.ordinal(), type, false, false, false); } /** * bootstrap method for null safe standard method calls * @deprecated since Groovy 2.1.0 */ @Deprecated public static CallSite bootstrapSafe(Lookup caller, String name, MethodType type) { return realBootstrap(caller, name, CALL_TYPES.METHOD.ordinal(), type, true, false, false); } /** * backing bootstrap method with all parameters */ private static CallSite realBootstrap(Lookup caller, String name, int callID, MethodType type, boolean safe, boolean thisCall, boolean spreadCall) { // since indy does not give us the runtime types // we produce first a dummy call site, which then changes the target to one, // that does the method selection including the the direct call to the // real method. MutableCallSite mc = new MutableCallSite(type); MethodHandle mh = makeFallBack(mc,caller.lookupClass(),name,callID,type,safe,thisCall,spreadCall); mc.setTarget(mh); return mc; } /** * Makes a fallback method for an invalidated method selection */ protected static MethodHandle makeFallBack(MutableCallSite mc, Class<?> sender, String name, int callID, MethodType type, boolean safeNavigation, boolean thisCall, boolean spreadCall) { MethodHandle mh = MethodHandles.insertArguments(SELECT_METHOD, 0, mc, sender, name, callID, safeNavigation, thisCall, spreadCall, /*dummy receiver:*/ 1); mh = mh.asCollector(Object[].class, type.parameterCount()). asType(type); return mh; } /** * Core method for indy method selection using runtime types. */ public static Object selectMethod(MutableCallSite callSite, Class sender, String methodName, int callID, Boolean safeNavigation, Boolean thisCall, Boolean spreadCall, Object dummyReceiver, Object[] arguments) throws Throwable { Selector selector = Selector.getSelector(callSite, sender, methodName, callID, safeNavigation, thisCall, spreadCall, arguments); selector.setCallSiteTarget(); MethodHandle call = selector.handle.asSpreader(Object[].class, arguments.length); call = call.asType(MethodType.methodType(Object.class,Object[].class)); return call.invokeExact(arguments); } /** * @since 2.5.0 */ public static CallSite staticArrayAccess(MethodHandles.Lookup lookup, String name, MethodType type) { if (type.parameterCount()==2) { return new ConstantCallSite(IndyArrayAccess.arrayGet(type)); } else { return new ConstantCallSite(IndyArrayAccess.arraySet(type)); } } }