CallSite.java 15.7 KB
Newer Older
1
/*
2
 * Copyright (c) 2008, 2013, 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
package java.lang.invoke;
27

28
import sun.invoke.empty.Empty;
29
import static java.lang.invoke.MethodHandleStatics.*;
30
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
31 32

/**
33 34
 * A {@code CallSite} is a holder for a variable {@link MethodHandle},
 * which is called its {@code target}.
35 36
 * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
 * all calls to the site's current target.
37 38 39 40
 * A {@code CallSite} may be associated with several {@code invokedynamic}
 * instructions, or it may be "free floating", associated with none.
 * In any case, it may be invoked through an associated method handle
 * called its {@linkplain #dynamicInvoker dynamic invoker}.
41
 * <p>
42 43 44 45 46 47 48 49 50 51 52 53
 * {@code CallSite} is an abstract class which does not allow
 * direct subclassing by users.  It has three immediate,
 * concrete subclasses that may be either instantiated or subclassed.
 * <ul>
 * <li>If a mutable target is not required, an {@code invokedynamic} instruction
 * may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}.
 * <li>If a mutable target is required which has volatile variable semantics,
 * because updates to the target must be immediately and reliably witnessed by other threads,
 * a {@linkplain VolatileCallSite volatile call site} may be used.
 * <li>Otherwise, if a mutable target is required,
 * a {@linkplain MutableCallSite mutable call site} may be used.
 * </ul>
54
 * <p>
55
 * A non-constant call site may be <em>relinked</em> by changing its target.
56
 * The new target must have the same {@linkplain MethodHandle#type() type}
57 58 59
 * as the previous target.
 * Thus, though a call site can be relinked to a series of
 * successive targets, it cannot change its type.
60 61 62
 * <p>
 * Here is a sample use of call sites and bootstrap methods which links every
 * dynamic call site to print its arguments:
63
<blockquote><pre>{@code
64
static void test() throws Throwable {
65 66
    // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
    InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
67
}
68 69 70 71 72 73 74 75 76 77
private static void printArgs(Object... args) {
  System.out.println(java.util.Arrays.deepToString(args));
}
private static final MethodHandle printArgs;
static {
  MethodHandles.Lookup lookup = MethodHandles.lookup();
  Class thisClass = lookup.lookupClass();  // (who am I?)
  printArgs = lookup.findStatic(thisClass,
      "printArgs", MethodType.methodType(void.class, Object[].class));
}
78
private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
79
  // ignore caller and name, but match the type:
80
  return new ConstantCallSite(printArgs.asType(type));
81
}
82
}</pre></blockquote>
83 84
 * @author John Rose, JSR 292 EG
 */
85
abstract
86
public class CallSite {
87
    static { MethodHandleImpl.initStatics(); }
88

89
    // The actual payload of this call site:
90
    /*package-private*/
91
    MethodHandle target;    // Note: This field is known to the JVM.  Do not change.
92

93
    /**
94 95 96 97 98 99 100
     * Make a blank call site object with the given method type.
     * An initial target method is supplied which will throw
     * an {@link IllegalStateException} if called.
     * <p>
     * Before this {@code CallSite} object is returned from a bootstrap method,
     * it is usually provided with a more useful target method,
     * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
101
     * @throws NullPointerException if the proposed type is null
102
     */
103 104
    /*package-private*/
    CallSite(MethodType type) {
105
        target = makeUninitializedCallSite(type);
106 107
    }

108
    /**
109
     * Make a call site object equipped with an initial target method handle.
110
     * @param target the method handle which will be the initial target of the call site
111
     * @throws NullPointerException if the proposed target is null
112
     */
113 114
    /*package-private*/
    CallSite(MethodHandle target) {
115
        target.type();  // null check
116
        this.target = target;
117
    }
118

119 120 121 122 123 124 125 126
    /**
     * Make a call site object equipped with an initial target method handle.
     * @param targetType the desired type of the call site
     * @param createTargetHook a hook which will bind the call site to the target method handle
     * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
     *         or if the target returned by the hook is not of the given {@code targetType}
     * @throws NullPointerException if the hook returns a null value
     * @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
127
     * @throws Throwable anything else thrown by the hook function
128 129 130 131 132 133 134 135 136 137
     */
    /*package-private*/
    CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
        this(targetType);
        ConstantCallSite selfCCS = (ConstantCallSite) this;
        MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
        checkTargetChange(this.target, boundTarget);
        this.target = boundTarget;
    }

138
    /**
139 140
     * Returns the type of this call site's target.
     * Although targets may change, any call site's type is permanent, and can never change to an unequal type.
141 142 143
     * The {@code setTarget} method enforces this invariant by refusing any new target that does
     * not have the previous target's type.
     * @return the type of the current target, which is also the type of any future target
144
     */
145
    public MethodType type() {
146
        // warning:  do not call getTarget here, because CCS.getTarget can throw IllegalStateException
147
        return target.type();
148 149
    }

150
    /**
151 152 153 154 155
     * Returns the target method of the call site, according to the
     * behavior defined by this call site's specific class.
     * The immediate subclasses of {@code CallSite} document the
     * class-specific behaviors of this method.
     *
156 157 158
     * @return the current linkage state of the call site, its target method handle
     * @see ConstantCallSite
     * @see VolatileCallSite
159
     * @see #setTarget
160 161 162
     * @see ConstantCallSite#getTarget
     * @see MutableCallSite#getTarget
     * @see VolatileCallSite#getTarget
163
     */
164
    public abstract MethodHandle getTarget();
165 166

    /**
167 168 169 170
     * Updates the target method of this call site, according to the
     * behavior defined by this call site's specific class.
     * The immediate subclasses of {@code CallSite} document the
     * class-specific behaviors of this method.
171
     * <p>
172 173 174
     * The type of the new target must be {@linkplain MethodType#equals equal to}
     * the type of the old target.
     *
175
     * @param newTarget the new target
176
     * @throws NullPointerException if the proposed new target is null
177
     * @throws WrongMethodTypeException if the proposed new target
178
     *         has a method type that differs from the previous target
179 180 181 182
     * @see CallSite#getTarget
     * @see ConstantCallSite#setTarget
     * @see MutableCallSite#setTarget
     * @see VolatileCallSite#setTarget
183
     */
184
    public abstract void setTarget(MethodHandle newTarget);
185 186

    void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
187
        MethodType oldType = oldTarget.type();
188 189
        MethodType newType = newTarget.type();  // null check!
        if (!newType.equals(oldType))
190
            throw wrongTargetType(newTarget, oldType);
191 192
    }

193
    private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
194
        return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
195 196
    }

197
    /**
198
     * Produces a method handle equivalent to an invokedynamic instruction
199
     * which has been linked to this call site.
200 201
     * <p>
     * This method is equivalent to the following code:
202
     * <blockquote><pre>{@code
203
     * MethodHandle getTarget, invoker, result;
204
     * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
205 206
     * invoker = MethodHandles.exactInvoker(this.type());
     * result = MethodHandles.foldArguments(invoker, getTarget)
207
     * }</pre></blockquote>
208
     *
209 210
     * @return a method handle which always invokes this call site's current target
     */
211 212 213
    public abstract MethodHandle dynamicInvoker();

    /*non-public*/ MethodHandle makeDynamicInvoker() {
214
        MethodHandle getTarget = GET_TARGET.bindArgumentL(0, this);
215 216 217
        MethodHandle invoker = MethodHandles.exactInvoker(this.type());
        return MethodHandles.foldArguments(invoker, getTarget);
    }
218

219
    private static final MethodHandle GET_TARGET;
220
    private static final MethodHandle THROW_UCS;
221 222
    static {
        try {
223
            GET_TARGET = IMPL_LOOKUP.
224
                findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
225 226
            THROW_UCS = IMPL_LOOKUP.
                findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Object.class, Object[].class));
227
        } catch (ReflectiveOperationException e) {
228
            throw newInternalError(e);
229 230
        }
    }
231

232
    /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
233
    private static Object uninitializedCallSite(Object... ignore) {
234 235 236
        throw new IllegalStateException("uninitialized call site");
    }

237 238 239 240 241 242 243 244
    private MethodHandle makeUninitializedCallSite(MethodType targetType) {
        MethodType basicType = targetType.basicType();
        MethodHandle invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_UNINIT_CS);
        if (invoker == null) {
            invoker = THROW_UCS.asType(basicType);
            invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker);
        }
        // unchecked view is OK since no values will be received or returned
245
        return invoker.viewAsType(targetType, false);
246 247
    }

248 249 250 251
    // unsafe stuff:
    private static final long TARGET_OFFSET;
    static {
        try {
252
            TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
253 254
        } catch (Exception ex) { throw new Error(ex); }
    }
255

256 257
    /*package-private*/
    void setTargetNormal(MethodHandle newTarget) {
258
        MethodHandleNatives.setCallSiteTargetNormal(this, newTarget);
259 260 261
    }
    /*package-private*/
    MethodHandle getTargetVolatile() {
262
        return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
263 264 265
    }
    /*package-private*/
    void setTargetVolatile(MethodHandle newTarget) {
266
        MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
267 268 269 270 271 272 273 274 275
    }

    // this implements the upcall from the JVM, MethodHandleNatives.makeDynamicCallSite:
    static CallSite makeSite(MethodHandle bootstrapMethod,
                             // Callee information:
                             String name, MethodType type,
                             // Extra arguments for BSM, if any:
                             Object info,
                             // Caller information:
276
                             Class<?> callerClass) {
277
        MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
278 279 280 281 282
        CallSite site;
        try {
            Object binding;
            info = maybeReBox(info);
            if (info == null) {
283
                binding = bootstrapMethod.invoke(caller, name, type);
284
            } else if (!info.getClass().isArray()) {
285
                binding = bootstrapMethod.invoke(caller, name, type, info);
286 287 288
            } else {
                Object[] argv = (Object[]) info;
                maybeReBoxElements(argv);
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
                switch (argv.length) {
                case 0:
                    binding = bootstrapMethod.invoke(caller, name, type);
                    break;
                case 1:
                    binding = bootstrapMethod.invoke(caller, name, type,
                                                     argv[0]);
                    break;
                case 2:
                    binding = bootstrapMethod.invoke(caller, name, type,
                                                     argv[0], argv[1]);
                    break;
                case 3:
                    binding = bootstrapMethod.invoke(caller, name, type,
                                                     argv[0], argv[1], argv[2]);
                    break;
                case 4:
                    binding = bootstrapMethod.invoke(caller, name, type,
                                                     argv[0], argv[1], argv[2], argv[3]);
                    break;
                case 5:
                    binding = bootstrapMethod.invoke(caller, name, type,
                                                     argv[0], argv[1], argv[2], argv[3], argv[4]);
                    break;
                case 6:
                    binding = bootstrapMethod.invoke(caller, name, type,
                                                     argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
                    break;
                default:
                    final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
                    if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY)
                        throw new BootstrapMethodError("too many bootstrap method arguments");
                    MethodType bsmType = bootstrapMethod.type();
                    MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
                    MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
                    MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
                    binding = spreader.invokeExact(typedBSM, (Object)caller, (Object)name, (Object)type, argv);
                }
327 328 329 330
            }
            //System.out.println("BSM for "+name+type+" => "+binding);
            if (binding instanceof CallSite) {
                site = (CallSite) binding;
331
            }  else {
332 333
                throw new ClassCastException("bootstrap method failed to produce a CallSite");
            }
334
            if (!site.getTarget().type().equals(type))
335
                throw wrongTargetType(site.getTarget(), type);
336
        } catch (Throwable ex) {
337 338 339
            BootstrapMethodError bex;
            if (ex instanceof BootstrapMethodError)
                bex = (BootstrapMethodError) ex;
340
            else
341
                bex = new BootstrapMethodError("call site initialization exception", ex);
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
            throw bex;
        }
        return site;
    }

    private static Object maybeReBox(Object x) {
        if (x instanceof Integer) {
            int xi = (int) x;
            if (xi == (byte) xi)
                x = xi;  // must rebox; see JLS 5.1.7
        }
        return x;
    }
    private static void maybeReBoxElements(Object[] xa) {
        for (int i = 0; i < xa.length; i++) {
            xa[i] = maybeReBox(xa[i]);
        }
    }
360
}