Linkage.java 8.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
/*
 * 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 java.util.WeakHashMap;
import sun.reflect.Reflection;
import static sun.dyn.util.VerifyAccess.checkBootstrapPrivilege;

/**
 * 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#makeCallSite}.
     * <p>
     * The operation will fail with an exception if any of the following conditions hold:
     * <ul>
     * <li>The caller of this method is in a different package than the {@code callerClass},
     *     and there is a security manager, and its {@code checkPermission} call throws
     *     when passed {@link LinkagePermission}("registerBootstrapMethod",callerClass).
     * <li>The given class already has a bootstrap method, either from an embedded
     *     {@code BootstrapInvokeDynamic} classfile attribute, or from a previous
     *     call to this method.
     * <li>The given class is already fully initialized.
     * <li>The given class is in the process of initialization, in another thread.
     * </ul>
     * 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);
        checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod");
        checkBSM(mh);
        synchronized (bootstrapMethods) {
            if (bootstrapMethods.containsKey(callerClass))
                throw new IllegalStateException("bootstrap method already declared in "+callerClass);
            bootstrapMethods.put(callerClass, mh);
        }
    }

    static void checkBSM(MethodHandle mh) {
        if (mh == null)  throw new IllegalArgumentException("null bootstrap method");
        if (mh.type() == OLD_BOOTSTRAP_METHOD_TYPE) // FIXME: delete at EDR/PFD
            throw new WrongMethodTypeException("bootstrap method must be a CallSite factory");
        if (mh.type() != BOOTSTRAP_METHOD_TYPE)
            throw new WrongMethodTypeException(mh.toString());
    }

    /**
     * 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
     * given class, and installs it on the caller.
     * @throws IllegalArgumentException if there is no such method
     */
    public static
    void registerBootstrapMethod(Class<?> runtime, String name) {
        Class callc = Reflection.getCallerClass(2);
        MethodHandle bootstrapMethod =
            MethodHandles.findStaticFrom(callc, runtime, name, BOOTSTRAP_METHOD_TYPE);
        // FIXME: exception processing wrong here
        checkBSM(bootstrapMethod);
        Linkage.registerBootstrapMethod(callc, bootstrapMethod);
    }

    /**
     * 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);
        // FIXME: exception processing wrong here
        checkBSM(bootstrapMethod);
        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);
        checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod");
        synchronized (bootstrapMethods) {
            return bootstrapMethods.get(callerClass);
        }
    }

    /** The type of any bootstrap method is a three-argument method
     * {@code (Class<?>, String, MethodType)} returning a {@code CallSite}.
     */
    public static final MethodType BOOTSTRAP_METHOD_TYPE
            = MethodType.make(CallSite.class,
                              Class.class, String.class, MethodType.class);

    private static final MethodType OLD_BOOTSTRAP_METHOD_TYPE
            = MethodType.make(Object.class,
                              CallSite.class, Object[].class);

    private static final WeakHashMap<Class, MethodHandle> bootstrapMethods =
            new WeakHashMap<Class, MethodHandle>();

    /**
     * Invalidate all <code>invokedynamic</code> call sites everywhere.
     * <p>
     * When this method returns, every <code>invokedynamic</code> instruction
     * will invoke its bootstrap method on next call.
     * <p>
     * It is unspecified whether call sites already known to the Java
     * code will continue to be associated with <code>invokedynamic</code>
     * instructions.  If any call site is still so associated, its
     * {@link CallSite#getTarget()} method is guaranteed to return null
     * the invalidation operation completes.
     * <p>
     * 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 <code>invokedynamic</code> call sites associated
     * with the given class.
     * (These are exactly those sites which report the given class
     * via the {@link CallSite#callerClass()} method.)
     * <p>
     * When this method returns, every matching <code>invokedynamic</code>
     * instruction will invoke its bootstrap method on next call.
     * <p>
     * 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");
    }

    private static Object doNotBootstrap(CallSite site, Object... arguments) {
        throw new UnsupportedOperationException("call site must not have null target: "+site);
    }

    private static final MethodHandle DO_NOT_BOOTSTRAP =
            MethodHandles.Lookup.IMPL_LOOKUP.findStatic(Linkage.class, "doNotBootstrap",
                OLD_BOOTSTRAP_METHOD_TYPE);

    // Up-call from the JVM.  Obsolete.  FIXME: Delete from VM then from here.
    static
    MethodHandle findBootstrapMethod(Class callerClass, Class searchBootstrapClass) {
        return DO_NOT_BOOTSTRAP;
    }
}