Linkage.java 11.2 KB
Newer Older
1
/*
2
 * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
3 4 5 6
 * 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
7
 * published by the Free Software Foundation.  Oracle designates this
8
 * particular file as subject to the "Classpath" exception as provided
9
 * by Oracle in the LICENSE file that accompanied this code.
10 11 12 13 14 15 16 17 18 19 20
 *
 * 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.
 *
21 22 23
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
24 25 26 27
 */

package java.dyn;

28
import java.lang.annotation.Annotation;
29
import java.dyn.MethodHandles.Lookup;
30
import java.util.WeakHashMap;
31
import sun.dyn.Access;
32
import sun.dyn.MethodHandleImpl;
33 34
import sun.reflect.Reflection;
import static sun.dyn.util.VerifyAccess.checkBootstrapPrivilege;
35
import static sun.dyn.MemberName.newIllegalArgumentException;
36 37

/**
38 39 40
 * This class consists exclusively of static methods that control
 * the linkage of {@code invokedynamic} instructions, and specifically
 * their reification as {@link CallSite} objects.
41 42 43
 * @author John Rose, JSR 292 EG
 */
public class Linkage {
44 45
    private static final Access IMPL_TOKEN = Access.getToken();

46 47 48
    private Linkage() {}  // do not instantiate

    /**
49
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
     * Register a <em>bootstrap method</em> to use when linking dynamic call sites within
     * a given caller class.
     * <p>
     * A bootstrap method must be a method handle with a return type of {@link CallSite}
     * and the following arguments:
     * <ul>
     * <li>the class containing the {@code invokedynamic} instruction, for which the bootstrap method was registered
     * <li>the name of the method being invoked (a {@link String})
     * <li>the type of the method being invoked (a {@link MethodType})
     * <li><em>TBD</em> optionally, an unordered array of {@link Annotation}s attached to the call site
     *     <em>(Until this feature is implemented, this will always receive an empty array.)</em>
     * </ul>
     * <em>(TBD: The final argument type may be missing from the method handle's type.
     * Additional arguments may be added in the future.)</em>
     * The bootstrap method acts as a factory method which accepts the given arguments
65
     * and returns a {@code CallSite} object (possibly of a subclass of {@code CallSite}).
66
     * <p>
67 68 69
     * The registration must take place exactly once, either before the class has begun
     * being initialized, or from within the class's static initializer.
     * Registration will fail with an exception if any of the following conditions hold:
70
     * <ul>
71
     * <li>The immediate caller of this method is in a different package than the given caller class,
72 73
     *     and there is a security manager, and its {@code checkPermission} call throws
     *     when passed {@link LinkagePermission}("registerBootstrapMethod",callerClass).
74 75 76
     * <li>The given caller class already has a bootstrap method registered.
     * <li>The given caller class is already fully initialized.
     * <li>The given caller class is in the process of initialization, in another thread.
77 78 79
     * </ul>
     * Because of these rules, a class may install its own bootstrap method in
     * a static initializer.
80 81
     * @param callerClass a class that may have {@code invokedynamic} sites
     * @param bootstrapMethod the method to use to bootstrap all such sites
82 83 84 85 86 87 88
     * @exception IllegalArgumentException if the class argument is null or
     *            a primitive class, or if the bootstrap method is the wrong type
     * @exception IllegalStateException if the class already has a bootstrap
     *            method, or if the its static initializer has already run
     *            or is already running in another thread
     * @exception SecurityException if there is a security manager installed,
     *            and a {@link LinkagePermission} check fails for "registerBootstrapMethod"
89 90
     */
    public static
91
    void registerBootstrapMethod(Class callerClass, MethodHandle bootstrapMethod) {
92 93
        Class callc = Reflection.getCallerClass(2);
        checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod");
94
        checkBSM(bootstrapMethod);
95
        MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod);
96 97
    }

98 99 100 101 102 103 104 105
    static private void checkBSM(MethodHandle mh) {
        if (mh == null)  throw newIllegalArgumentException("null bootstrap method");
        if (mh.type() == BOOTSTRAP_METHOD_TYPE_2)
            // For now, always pass an empty array for the Annotations argument
            mh = MethodHandles.insertArguments(mh, BOOTSTRAP_METHOD_TYPE_2.parameterCount()-1,
                                               (Object)NO_ANNOTATIONS);
        if (mh.type() == BOOTSTRAP_METHOD_TYPE)  return;
        throw new WrongMethodTypeException(mh.toString());
106
    }
107
    static private final Annotation[] NO_ANNOTATIONS = { };
108 109

    /**
110
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
111
     * Simplified version of {@code registerBootstrapMethod} for self-registration,
112
     * to be called from a static initializer.
113
     * Finds a static method of the required type in the
114 115 116 117
     * given runtime class, and installs it on the caller class.
     * @throws NoSuchMethodException if there is no such method
     * @throws IllegalStateException if the caller class's static initializer
     *         has already run, or is already running in another thread
118 119 120
     */
    public static
    void registerBootstrapMethod(Class<?> runtime, String name) {
121 122
        Class callerClass = Reflection.getCallerClass(2);
        registerBootstrapMethodLookup(callerClass, runtime, name);
123 124 125
    }

    /**
126
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
127
     * Simplified version of {@code registerBootstrapMethod} for self-registration,
128
     * to be called from a static initializer.
129
     * Finds a static method of the required type in the
130
     * caller class itself, and installs it on the caller class.
131
     * @throws IllegalArgumentException if there is no such method
132 133
     * @throws IllegalStateException if the caller class's static initializer
     *         has already run, or is already running in another thread
134 135 136
     */
    public static
    void registerBootstrapMethod(String name) {
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
        Class callerClass = Reflection.getCallerClass(2);
        registerBootstrapMethodLookup(callerClass, callerClass, name);
    }

    private static
    void registerBootstrapMethodLookup(Class<?> callerClass, Class<?> runtime, String name) {
        Lookup lookup = new Lookup(IMPL_TOKEN, callerClass);
        MethodHandle bootstrapMethod;
        // Try both types.  TBD
        try {
            bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE_2);
        } catch (NoAccessException ex) {
            bootstrapMethod = null;
        }
        if (bootstrapMethod == null) {
            try {
                bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE);
            } catch (NoAccessException ex) {
                throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex);
            }
        }
158
        checkBSM(bootstrapMethod);
159
        MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod);
160 161 162
    }

    /**
163
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
164
     * Report the bootstrap method registered for a given caller class.
165
     * Returns null if the class has never yet registered a bootstrap method.
166 167 168
     * 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.
169 170 171 172 173 174
     * @exception IllegalArgumentException if the argument is null or
     *            a primitive class
     * @exception SecurityException if there is a security manager installed,
     *            and the immediate caller of this method is not in the same
     *            package as the caller class
     *            and a {@link LinkagePermission} check fails for "getBootstrapMethod"
175 176 177 178
     */
    public static
    MethodHandle getBootstrapMethod(Class callerClass) {
        Class callc = Reflection.getCallerClass(2);
179 180
        checkBootstrapPrivilege(callc, callerClass, "getBootstrapMethod");
        return MethodHandleImpl.getBootstrap(IMPL_TOKEN, callerClass);
181 182
    }

183 184 185 186
    /**
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
     * The type of any bootstrap method is a three-argument method
     * {@code (Class, String, MethodType)} returning a {@code CallSite}.
187 188
     */
    public static final MethodType BOOTSTRAP_METHOD_TYPE
189 190
            = MethodType.methodType(CallSite.class,
                                    Class.class, String.class, MethodType.class);
191 192 193 194
    static final MethodType BOOTSTRAP_METHOD_TYPE_2
            = MethodType.methodType(CallSite.class,
                                    Class.class, String.class, MethodType.class,
                                    Annotation[].class);
195 196

    /**
197
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
     * 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");
    }

    /**
221
     * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
222
     * Invalidate all {@code invokedynamic} call sites in the bytecodes
223
     * of any methods of the given class.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
     * <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");
    }
}