/* * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package java.dyn; import sun.dyn.util.MethodHandleInvoker; import java.util.WeakHashMap; import sun.reflect.Reflection; /** * Static methods which control the linkage of invokedynamic call sites. * @author John Rose, JSR 292 EG */ public class Linkage { private Linkage() {} // do not instantiate /** * Register a bootstrap method for use for a given caller class. * The method handle must be of a type equivalent to {@link Linkage#bootstrapInvokeDynamic}. *

* The operation will fail with an exception if any of the following conditions hold: *

* Because of these rules, a class may install its own bootstrap method in * a static initializer. */ public static void registerBootstrapMethod(Class callerClass, MethodHandle mh) { Class callc = Reflection.getCallerClass(2); checkPackagePrivilege(callc, callerClass, "registerBootstrapMethod"); if (mh != null && mh.type() != BOOTSTRAP_METHOD_TYPE) throw new WrongMethodTypeException(mh.type().toString()); synchronized (bootstrapMethods) { if (bootstrapMethods.containsKey(callerClass)) throw new IllegalStateException("bootstrap method already declared in "+callerClass); bootstrapMethods.put(callerClass, mh); } } /** * Simplified version of registerBootstrapMethod for self-registration, * to be called from a static initializer. * Finds a static method of type (CallSite, Object[]) -> Object in the * caller's class, and installs it on the caller. * @throws IllegalArgumentException if there is no such method */ public static void registerBootstrapMethod(String name) { Class callc = Reflection.getCallerClass(2); MethodHandle bootstrapMethod = MethodHandles.findStaticFrom(callc, callc, name, BOOTSTRAP_METHOD_TYPE); if (bootstrapMethod == null) throw new IllegalArgumentException("cannot find bootstrap method: "+name); Linkage.registerBootstrapMethod(callc, bootstrapMethod); } /** * Report the bootstrap method registered for a given class. * Returns null if the class has never yet registered a bootstrap method, * or if the class has explicitly registered a null bootstrap method. * Only callers privileged to set the bootstrap method may inquire * about it, because a bootstrap method is potentially a back-door entry * point into its class. */ public static MethodHandle getBootstrapMethod(Class callerClass) { Class callc = Reflection.getCallerClass(2); checkPackagePrivilege(callc, callerClass, "registerBootstrapMethod"); synchronized (bootstrapMethods) { return bootstrapMethods.get(callerClass); } } /** The type of any bootstrap method is (CallSite,Object...)Object. * The varargs marker is required. */ public static final MethodType BOOTSTRAP_METHOD_TYPE = MethodType.make(Object.class, CallSite.class, Object[].class); private static MethodHandleInvoker bootstrapMethodInvoker; private static final WeakHashMap bootstrapMethods = new WeakHashMap(); /** * Determine if the caller class has declared or registered its own bootstrap method. * If so, delegate this call to it. Otherwise, throw an IncompatibleClassChangeError. *

* This routine, or an equivalent sequence of actions, is called from the * JVM when an {@code invokedynamic} instruction is executed * but its call site is unlinked (has a null target method). *

* This routine can be called from Java code whether or not the call site * currently has a target, and will invoke the bootstrap method regardless * of he call site's linkage state. *

* Although invoking the bootstrap method does not in and of itself cause * state change in the call site, the actions eventually performed by * the bootstrap method may include installed a new target on the call site. *

* Note that linkage state changes are individually atomic, but are not * serialized in any way with respect to calls to the bootstrap method, * or executions of the {@code invokedynamic} instruction. Therefore, * an {@code invokedynamic} call site may be linked several times if * several threads concurrently execute it in an unlinked state. * It is up to the user-defined bootstrap method to make sure this * race condition is resolved safely, either by performing linkage * decisions under suitable locks (as the JVM does) or by ensuring * that all racing threads come to the same conclusion, and independently * install equivalent target methods. */ public static Object bootstrapInvokeDynamic(CallSite site, Object... arguments) { Class callerClass = site.callerClass(); MethodHandle mh; synchronized (bootstrapMethods) { mh = bootstrapMethods.get(callerClass); } if (mh == null) throw new IllegalStateException("no bootstrap method declared in "+callerClass); System.out.println(site+": calling bootstrap "+mh); // FIXME if (bootstrapMethodInvoker == null) bootstrapMethodInvoker = MethodHandleInvoker.make(BOOTSTRAP_METHOD_TYPE); return bootstrapMethodInvoker.invoke(mh, site, arguments); } /** * Invalidate all invokedynamic call sites everywhere. *

* When this method returns, every invokedynamic instruction * will invoke its bootstrap method on next call. *

* It is unspecified whether call sites already known to the Java * code will continue to be associated with invokedynamic * instructions. If any call site is still so associated, its * {@link CallSite#getTarget()} method is guaranteed to return null * the invalidation operation completes. *

* Invalidation operations are likely to be slow. Use them sparingly. */ public static Object invalidateAll() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(new LinkagePermission("invalidateAll")); } throw new UnsupportedOperationException("NYI"); } /** * Invalidate all invokedynamic call sites associated * with the given class. * (These are exactly those sites which report the given class * via the {@link CallSite#callerClass()} method.) *

* When this method returns, every matching invokedynamic * instruction will invoke its bootstrap method on next call. *

* For additional semantics of call site invalidation, * see {@link #invalidateAll()}. */ public static Object invalidateCallerClass(Class callerClass) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(new LinkagePermission("invalidateAll", callerClass)); } throw new UnsupportedOperationException("NYI"); } /** * Ensure the requesting class have privileges to perform invokedynamic * linkage operations on subjectClass. True if requestingClass is * null (meaning the request originates from the JVM) or if the * classes are in the same package and have consistent class loaders. * (The subject class loader must be identical with or be a child of * the requesting class loader.) * @param requestingClass * @param subjectClass */ // FIXME: factor this logic into sun.dyn.util.VerifyAccess static void checkPackagePrivilege(Class requestingClass, Class subjectClass, String permissionName) { if (requestingClass == null) return; if (requestingClass == subjectClass) return; SecurityManager security = System.getSecurityManager(); if (security == null) return; // open season ClassLoader rcl = requestingClass.getClassLoader(); ClassLoader scl = subjectClass.getClassLoader(); if (isParent(rcl, scl)) { String rn = requestingClass.getName(); if (rn.startsWith("java.dyn.")) return; String sn = subjectClass.getName(); if (samePackage(rn, sn)) return; } security.checkPermission(new LinkagePermission(permissionName, requestingClass)); } static MethodHandle findBootstrapMethod(Class callerClass, Class searchBootstrapClass) { if (searchBootstrapClass != null) throw new UnsupportedOperationException("NYI"); MethodHandle mh = getBootstrapMethod(callerClass); System.out.println("reporting bootstrap method to JVM: "+mh); //FIXME return mh; } private static boolean isParent(ClassLoader rcl, ClassLoader scl) { while (scl != null && scl != rcl) scl = scl.getParent(); return (scl == rcl); } private static boolean samePackage(String rn, String sn) { assert((rn.indexOf('/') & sn.indexOf('/')) < 0); // no bytecode names int lastDot = rn.lastIndexOf('.'); if (lastDot != sn.lastIndexOf('.')) return false; return rn.startsWith(sn.substring(0, lastDot+1)); } }