提交 80198140 编写于 作者: T twisti

7023639: JSR 292 method handle invocation needs a fast path for compiled code

6984705: JSR 292 method handle creation should not go through JNI
Summary: remove assembly code for JDK 7 chained method handles
Reviewed-by: jrose, twisti, mhaupt, forax
Contributed-by: NJohn Rose &lt;john.r.rose@oracle.com&gt;, Christian Thalinger &lt;christian.thalinger@oracle.com&gt;, Michael Haupt <michael.haupt@oracle.com>
上级 5e72859c
......@@ -26,7 +26,7 @@
package java.lang.invoke;
import sun.invoke.empty.Empty;
import sun.misc.Unsafe;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
......@@ -86,13 +86,9 @@ abstract
public class CallSite {
static { MethodHandleImpl.initStatics(); }
// Fields used only by the JVM. Do not use or change.
private MemberName vmmethod; // supplied by the JVM (ref. to calling method)
private int vmindex; // supplied by the JVM (BCI within calling method)
// The actual payload of this call site:
/*package-private*/
MethodHandle target;
MethodHandle target; // Note: This field is known to the JVM. Do not change.
/**
* Make a blank call site object with the given method type.
......@@ -151,24 +147,6 @@ public class CallSite {
return target.type();
}
/** Called from JVM (or low-level Java code) after the BSM returns the newly created CallSite.
* The parameters are JVM-specific.
*/
void initializeFromJVM(String name,
MethodType type,
MemberName callerMethod,
int callerBCI) {
if (this.vmmethod != null) {
// FIXME
throw new BootstrapMethodError("call site has already been linked to an invokedynamic instruction");
}
if (!this.type().equals(type)) {
throw wrongTargetType(target, type);
}
this.vmindex = callerBCI;
this.vmmethod = callerMethod;
}
/**
* Returns the target method of the call site, according to the
* behavior defined by this call site's specific class.
......@@ -233,7 +211,7 @@ public class CallSite {
public abstract MethodHandle dynamicInvoker();
/*non-public*/ MethodHandle makeDynamicInvoker() {
MethodHandle getTarget = MethodHandleImpl.bindReceiver(GET_TARGET, this);
MethodHandle getTarget = GET_TARGET.bindReceiver(this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget);
}
......@@ -255,12 +233,10 @@ public class CallSite {
}
// unsafe stuff:
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long TARGET_OFFSET;
static {
try {
TARGET_OFFSET = unsafe.objectFieldOffset(CallSite.class.getDeclaredField("target"));
TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
} catch (Exception ex) { throw new Error(ex); }
}
......@@ -270,7 +246,7 @@ public class CallSite {
}
/*package-private*/
MethodHandle getTargetVolatile() {
return (MethodHandle) unsafe.getObjectVolatile(this, TARGET_OFFSET);
return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
}
/*package-private*/
void setTargetVolatile(MethodHandle newTarget) {
......@@ -284,8 +260,7 @@ public class CallSite {
// Extra arguments for BSM, if any:
Object info,
// Caller information:
MemberName callerMethod, int callerBCI) {
Class<?> callerClass = callerMethod.getDeclaringClass();
Class<?> callerClass) {
Object caller = IMPL_LOOKUP.in(callerClass);
CallSite site;
try {
......
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, Oracle and/or its affiliates. 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
......@@ -25,26 +25,13 @@
package java.lang.invoke;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import java.lang.annotation.*;
/**
* This method handle is used to optionally provide a count of how
* many times it was invoked.
*
* @author never
* Internal marker for some methods in the JSR 292 implementation.
*/
class CountingMethodHandle extends AdapterMethodHandle {
private int vmcount;
private CountingMethodHandle(MethodHandle target) {
super(target, target.type(), AdapterMethodHandle.makeConv(OP_RETYPE_ONLY));
}
/** Wrap the incoming MethodHandle in a CountingMethodHandle if they are enabled */
static MethodHandle wrap(MethodHandle mh) {
if (MethodHandleNatives.COUNT_GWT) {
return new CountingMethodHandle(mh);
}
return mh;
}
/*non-public*/
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@interface DontInline {
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
import java.lang.annotation.*;
/**
* Internal marker for some methods in the JSR 292 implementation.
*/
/*non-public*/
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@interface ForceInline {
}
......@@ -25,8 +25,11 @@
package java.lang.invoke;
import java.util.Arrays;
import sun.invoke.empty.Empty;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
import static java.lang.invoke.LambdaForm.*;
/**
* Construction and caching of often-used invokers.
......@@ -36,11 +39,15 @@ class Invokers {
// exact type (sans leading taget MH) for the outgoing call
private final MethodType targetType;
// FIXME: Get rid of the invokers that are not useful.
// exact invoker for the outgoing call
private /*lazy*/ MethodHandle exactInvoker;
// erased (partially untyped but with primitives) invoker for the outgoing call
// FIXME: get rid of
private /*lazy*/ MethodHandle erasedInvoker;
// FIXME: get rid of
/*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric
// general invoker for the outgoing call
......@@ -63,14 +70,13 @@ class Invokers {
this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
}
/*non-public*/ static MethodType invokerType(MethodType targetType) {
return targetType.insertParameterTypes(0, MethodHandle.class);
}
/*non-public*/ MethodHandle exactInvoker() {
MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker;
invoker = lookupInvoker("invokeExact");
MethodType mtype = targetType;
LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_INVOKER);
invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
assert(checkInvoker(invoker));
exactInvoker = invoker;
return invoker;
}
......@@ -78,29 +84,50 @@ class Invokers {
/*non-public*/ MethodHandle generalInvoker() {
MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker;
invoker = lookupInvoker("invoke");
MethodType mtype = targetType;
prepareForGenericCall(mtype);
LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_INVOKER);
invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
assert(checkInvoker(invoker));
generalInvoker = invoker;
return invoker;
}
private MethodHandle lookupInvoker(String name) {
MethodHandle invoker;
/*non-public*/ MethodHandle makeBasicInvoker() {
MethodHandle invoker = DirectMethodHandle.make(invokeBasicMethod(targetType));
assert(targetType == targetType.basicType());
// Note: This is not cached here. It is cached by the calling MethodTypeForm.
assert(checkInvoker(invoker));
return invoker;
}
static MemberName invokeBasicMethod(MethodType type) {
String name = "invokeBasic";
try {
invoker = IMPL_LOOKUP.findVirtual(MethodHandle.class, name, targetType);
//Lookup.findVirtual(MethodHandle.class, name, type);
return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, name, type);
} catch (ReflectiveOperationException ex) {
throw new InternalError("JVM cannot find invoker for "+targetType, ex);
throw new InternalError("JVM cannot find invoker for "+type, ex);
}
assert(invokerType(targetType) == invoker.type());
}
private boolean checkInvoker(MethodHandle invoker) {
assert(targetType.invokerType().equals(invoker.type()))
: java.util.Arrays.asList(targetType, targetType.invokerType(), invoker);
assert(invoker.internalMemberName() == null ||
invoker.internalMemberName().getMethodType().equals(targetType));
assert(!invoker.isVarargsCollector());
return invoker;
return true;
}
// FIXME: get rid of
/*non-public*/ MethodHandle erasedInvoker() {
MethodHandle xinvoker = exactInvoker();
MethodHandle invoker = erasedInvoker;
if (invoker != null) return invoker;
MethodType erasedType = targetType.erase();
invoker = xinvoker.asType(invokerType(erasedType));
invoker = xinvoker.asType(erasedType.invokerType());
erasedInvoker = invoker;
return invoker;
}
......@@ -118,7 +145,7 @@ class Invokers {
/*non-public*/ MethodHandle varargsInvoker() {
MethodHandle vaInvoker = varargsInvoker;
if (vaInvoker != null) return vaInvoker;
vaInvoker = spreadInvoker(0).asType(invokerType(MethodType.genericMethodType(0, true)));
vaInvoker = spreadInvoker(0).asType(MethodType.genericMethodType(0, true).invokerType());
varargsInvoker = vaInvoker;
return vaInvoker;
}
......@@ -137,16 +164,18 @@ class Invokers {
uninitializedCallSite = invoker;
return invoker;
}
if (THROW_UCS == null) {
invoker = THROW_UCS;
if (invoker == null) {
try {
THROW_UCS = IMPL_LOOKUP
THROW_UCS = invoker = IMPL_LOOKUP
.findStatic(CallSite.class, "uninitializedCallSite",
MethodType.methodType(Empty.class));
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
invoker = AdapterMethodHandle.makeRetypeRaw(targetType, THROW_UCS);
invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType()));
invoker = invoker.dropArguments(targetType, 0, targetType.parameterCount());
assert(invoker.type().equals(targetType));
uninitializedCallSite = invoker;
return invoker;
......@@ -155,4 +184,208 @@ class Invokers {
public String toString() {
return "Invokers"+targetType;
}
private static MethodType fixMethodType(Class<?> callerClass, Object type) {
if (type instanceof MethodType)
return (MethodType) type;
else
return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader());
}
static MemberName exactInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
MethodType mtype = fixMethodType(callerClass, type);
LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_LINKER);
appendixResult[0] = mtype;
return lform.vmentry;
}
static MemberName genericInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
MethodType mtype = fixMethodType(callerClass, type);
LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_LINKER);
prepareForGenericCall(mtype);
appendixResult[0] = mtype;
return lform.vmentry;
}
private static LambdaForm invokeForm(MethodType mtype, int which) {
mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
boolean isLinker, isGeneric;
String debugName;
switch (which) {
case MethodTypeForm.LF_EX_LINKER: isLinker = true; isGeneric = false; debugName = "invokeExact_MT"; break;
case MethodTypeForm.LF_EX_INVOKER: isLinker = false; isGeneric = false; debugName = "exactInvoker"; break;
case MethodTypeForm.LF_GEN_LINKER: isLinker = true; isGeneric = true; debugName = "invoke_MT"; break;
case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true; debugName = "invoker"; break;
default: throw new InternalError();
}
LambdaForm lform = mtype.form().cachedLambdaForm(which);
if (lform != null) return lform;
// exactInvokerForm (Object,Object)Object
// link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
final int THIS_MH = 0;
final int CALL_MH = THIS_MH + (isLinker ? 0 : 1);
final int ARG_BASE = CALL_MH + 1;
final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
final int INARG_LIMIT = OUTARG_LIMIT + (isLinker ? 1 : 0);
int nameCursor = OUTARG_LIMIT;
final int MTYPE_ARG = nameCursor++; // might be last in-argument
final int CHECK_TYPE = nameCursor++;
final int LINKER_CALL = nameCursor++;
MethodType invokerFormType = mtype.invokerType();
if (isLinker) {
invokerFormType = invokerFormType.appendParameterTypes(MemberName.class);
} else {
invokerFormType = invokerFormType.invokerType();
}
Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
assert(names.length == nameCursor);
if (MTYPE_ARG >= INARG_LIMIT) {
assert(names[MTYPE_ARG] == null);
names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0);
// else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM)
}
// Make the final call. If isGeneric, then prepend the result of type checking.
MethodType outCallType;
Object[] outArgs;
if (!isGeneric) {
names[CHECK_TYPE] = new Name(NF_checkExactType, names[CALL_MH], names[MTYPE_ARG]);
// mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*)
outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class);
outCallType = mtype;
} else {
names[CHECK_TYPE] = new Name(NF_checkGenericType, names[CALL_MH], names[MTYPE_ARG]);
// mh.invokeGeneric(a*):R =>
// let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt);
// gamh.invokeBasic(mt, mh, a*)
final int PREPEND_GAMH = 0, PREPEND_MT = 1, PREPEND_COUNT = 2;
outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
// prepend arguments:
System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
outArgs[PREPEND_GAMH] = names[CHECK_TYPE];
outArgs[PREPEND_MT] = names[MTYPE_ARG];
outCallType = mtype.insertParameterTypes(0, MethodType.class, MethodHandle.class);
}
names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs);
lform = new LambdaForm(debugName, INARG_LIMIT, names);
if (isLinker)
lform.compileToBytecode(); // JVM needs a real methodOop
lform = mtype.form().setCachedLambdaForm(which, lform);
return lform;
}
/*non-public*/ static
WrongMethodTypeException newWrongMethodTypeException(MethodType actual, MethodType expected) {
// FIXME: merge with JVM logic for throwing WMTE
return new WrongMethodTypeException("expected "+expected+" but found "+actual);
}
/** Static definition of MethodHandle.invokeExact checking code. */
/*non-public*/ static
@ForceInline
void checkExactType(Object mhObj, Object expectedObj) {
MethodHandle mh = (MethodHandle) mhObj;
MethodType expected = (MethodType) expectedObj;
MethodType actual = mh.type();
if (actual != expected)
throw newWrongMethodTypeException(expected, actual);
}
/** Static definition of MethodHandle.invokeGeneric checking code. */
/*non-public*/ static
@ForceInline
Object checkGenericType(Object mhObj, Object expectedObj) {
MethodHandle mh = (MethodHandle) mhObj;
MethodType expected = (MethodType) expectedObj;
//MethodType actual = mh.type();
MethodHandle gamh = expected.form().genericInvoker;
if (gamh != null) return gamh;
return prepareForGenericCall(expected);
}
/**
* Returns an adapter GA for invoking a MH with type adjustments.
* The MethodType of the generic invocation site is prepended to MH
* and its arguments as follows:
* {@code (R)MH.invoke(A*) => GA.invokeBasic(TYPEOF<A*,R>, MH, A*)}
*/
/*non-public*/ static MethodHandle prepareForGenericCall(MethodType mtype) {
// force any needed adapters to be preconstructed
MethodTypeForm form = mtype.form();
MethodHandle gamh = form.genericInvoker;
if (gamh != null) return gamh;
try {
// Trigger adapter creation.
gamh = InvokeGeneric.generalInvokerOf(form.erasedType);
form.genericInvoker = gamh;
return gamh;
} catch (Exception ex) {
throw new InternalError("Exception while resolving inexact invoke", ex);
}
}
static MemberName linkToCallSiteMethod(MethodType mtype) {
LambdaForm lform = callSiteForm(mtype);
return lform.vmentry;
}
private static LambdaForm callSiteForm(MethodType mtype) {
mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
LambdaForm lform = mtype.form().cachedLambdaForm(MethodTypeForm.LF_CS_LINKER);
if (lform != null) return lform;
// exactInvokerForm (Object,Object)Object
// link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
final int ARG_BASE = 0;
final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
final int INARG_LIMIT = OUTARG_LIMIT + 1;
int nameCursor = OUTARG_LIMIT;
final int CSITE_ARG = nameCursor++; // the last in-argument
final int CALL_MH = nameCursor++; // result of getTarget
final int LINKER_CALL = nameCursor++;
MethodType invokerFormType = mtype.appendParameterTypes(CallSite.class);
Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
assert(names.length == nameCursor);
assert(names[CSITE_ARG] != null);
names[CALL_MH] = new Name(NF_getCallSiteTarget, names[CSITE_ARG]);
// (site.)invokedynamic(a*):R => mh = site.getTarget(); mh.invokeBasic(a*)
final int PREPEND_MH = 0, PREPEND_COUNT = 1;
Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
// prepend MH argument:
System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
outArgs[PREPEND_MH] = names[CALL_MH];
names[LINKER_CALL] = new Name(invokeBasicMethod(mtype), outArgs);
lform = new LambdaForm("linkToCallSite", INARG_LIMIT, names);
lform.compileToBytecode(); // JVM needs a real methodOop
lform = mtype.form().setCachedLambdaForm(MethodTypeForm.LF_CS_LINKER, lform);
return lform;
}
/** Static definition of MethodHandle.invokeGeneric checking code. */
/*non-public*/ static
@ForceInline
Object getCallSiteTarget(Object site) {
return ((CallSite)site).getTarget();
}
// Local constant functions:
private static final NamedFunction NF_checkExactType;
private static final NamedFunction NF_checkGenericType;
private static final NamedFunction NF_getCallSiteTarget;
static {
try {
NF_checkExactType = new NamedFunction(Invokers.class
.getDeclaredMethod("checkExactType", Object.class, Object.class));
NF_checkGenericType = new NamedFunction(Invokers.class
.getDeclaredMethod("checkGenericType", Object.class, Object.class));
NF_getCallSiteTarget = new NamedFunction(Invokers.class
.getDeclaredMethod("getCallSiteTarget", Object.class));
NF_checkExactType.resolve();
NF_checkGenericType.resolve();
NF_getCallSiteTarget.resolve();
// bound
} catch (ReflectiveOperationException ex) {
throw new InternalError(ex);
}
}
}
此差异已折叠。
......@@ -26,9 +26,13 @@
package java.lang.invoke;
import java.util.ArrayList;
import sun.invoke.util.ValueConversions;
import java.util.*;
import sun.invoke.util.*;
import sun.misc.Unsafe;
import static java.lang.invoke.MethodHandleStatics.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A method handle is a typed, directly executable reference to an underlying method,
......@@ -208,8 +212,8 @@ import static java.lang.invoke.MethodHandleStatics.*;
* refers directly to an associated {@code CONSTANT_Methodref},
* {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref}
* constant pool entry.
* (For more details on method handle constants,
* see the <a href="package-summary.html#mhcon">package summary</a>.)
* (For full details on method handle constants,
* see sections 4.4.8 and 5.4.3.5 of the Java Virtual Machine Specification.)
* <p>
* Method handles produced by lookups or constant loads from methods or
* constructors with the variable arity modifier bit ({@code 0x0080})
......@@ -224,6 +228,19 @@ import static java.lang.invoke.MethodHandleStatics.*;
* (E.g., if a non-static method handle is obtained via {@code ldc},
* the type of the receiver is the class named in the constant pool entry.)
* <p>
* Method handle constants are subject to the same link-time access checks
* their corresponding bytecode instructions, and the {@code ldc} instruction
* will throw corresponding linkage errors if the bytecode behaviors would
* throw such errors.
* <p>
* As a corollary of this, access to protected members is restricted
* to receivers only of the accessing class, or one of its subclasses,
* and the accessing class must in turn be a subclass (or package sibling)
* of the protected member's defining class.
* If a method reference refers to a protected non-static method or field
* of a class outside the current package, the receiver argument will
* be narrowed to the type of the accessing class.
* <p>
* When a method handle to a virtual method is invoked, the method is
* always looked up in the receiver (that is, the first argument).
* <p>
......@@ -390,39 +407,8 @@ mh.invokeExact(System.out, "Hello, world.");
* @author John Rose, JSR 292 EG
*/
public abstract class MethodHandle {
// { JVM internals:
private byte vmentry; // adapter stub or method entry point
//private int vmslots; // optionally, hoist type.form.vmslots
/*non-public*/ Object vmtarget; // VM-specific, class-specific target value
// TO DO: vmtarget should be invisible to Java, since the JVM puts internal
// managed pointers into it. Making it visible exposes it to debuggers,
// which can cause errors when they treat the pointer as an Object.
// These two dummy fields are present to force 'I' and 'J' signatures
// into this class's constant pool, so they can be transferred
// to vmentry when this class is loaded.
static final int INT_FIELD = 0;
static final long LONG_FIELD = 0;
// vmentry (a void* field) is used *only* by the JVM.
// The JVM adjusts its type to int or long depending on system wordsize.
// Since it is statically typed as neither int nor long, it is impossible
// to use this field from Java bytecode. (Please don't try to, either.)
// The vmentry is an assembly-language stub which is jumped to
// immediately after the method type is verified.
// For a direct MH, this stub loads the vmtarget's entry point
// and jumps to it.
// } End of JVM internals.
static { MethodHandleImpl.initStatics(); }
// interface MethodHandle<R throws X extends Exception,A...>
// { MethodType<R throws X,A...> type(); public R invokeExact(A...) throws X; }
/**
* Internal marker interface which distinguishes (to the Java compiler)
* those methods which are <a href="MethodHandle.html#sigpoly">signature polymorphic</a>.
......@@ -431,7 +417,9 @@ public abstract class MethodHandle {
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@interface PolymorphicSignature { }
private MethodType type;
private final MethodType type;
/*private*/ final LambdaForm form;
// form is not private so that invokers can easily fetch it
/**
* Reports the type of this method handle.
......@@ -448,9 +436,13 @@ public abstract class MethodHandle {
* the {@code java.lang.invoke} package.
*/
// @param type type (permanently assigned) of the new method handle
/*non-public*/ MethodHandle(MethodType type) {
type.getClass(); // elicit NPE
/*non-public*/ MethodHandle(MethodType type, LambdaForm form) {
type.getClass(); // explicit NPE
form.getClass(); // explicit NPE
this.type = type;
this.form = form;
form.prepare(); // TO DO: Try to delay this step until just before invocation.
}
/**
......@@ -505,6 +497,46 @@ public abstract class MethodHandle {
*/
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
/**
* Private method for trusted invocation of a method handle respecting simplified signatures.
* Type mismatches will not throw {@code WrongMethodTypeException}, but could crash the JVM.
* <p>
* The caller signature is restricted to the following basic types:
* Object, int, long, float, double, and void return.
* <p>
* The caller is responsible for maintaining type correctness by ensuring
* that the each outgoing argument value is a member of the range of the corresponding
* callee argument type.
* (The caller should therefore issue appropriate casts and integer narrowing
* operations on outgoing argument values.)
* The caller can assume that the incoming result value is part of the range
* of the callee's return type.
*/
/*non-public*/ final native @PolymorphicSignature Object invokeBasic(Object... args) throws Throwable;
/*non-public*/ static native @PolymorphicSignature Object linkToVirtual(Object... args) throws Throwable;
/**
* Private method for trusted invocation of a MemberName of kind {@code REF_invokeStatic}.
* The caller signature is restricted to basic types as with {@code invokeBasic}.
* The trailing (not leading) argument must be a MemberName.
*/
/*non-public*/ static native @PolymorphicSignature Object linkToStatic(Object... args) throws Throwable;
/**
* Private method for trusted invocation of a MemberName of kind {@code REF_invokeSpecial}.
* The caller signature is restricted to basic types as with {@code invokeBasic}.
* The trailing (not leading) argument must be a MemberName.
*/
/*non-public*/ static native @PolymorphicSignature Object linkToSpecial(Object... args) throws Throwable;
/**
* Private method for trusted invocation of a MemberName of kind {@code REF_invokeInterface}.
* The caller signature is restricted to basic types as with {@code invokeBasic}.
* The trailing (not leading) argument must be a MemberName.
*/
/*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable;
/**
* Performs a variable arity invocation, passing the arguments in the given array
* to the method handle, as if via an inexact {@link #invoke invoke} from a call site
......@@ -557,6 +589,7 @@ public abstract class MethodHandle {
*/
public Object invokeWithArguments(Object... arguments) throws Throwable {
int argc = arguments == null ? 0 : arguments.length;
@SuppressWarnings("LocalVariableHidesMemberVariable")
MethodType type = type();
if (type.parameterCount() != argc || isVarargsCollector()) {
// simulate invoke
......@@ -690,7 +723,7 @@ public abstract class MethodHandle {
if (!type.isConvertibleTo(newType)) {
throw new WrongMethodTypeException("cannot convert "+this+" to "+newType);
}
return MethodHandleImpl.convertArguments(this, newType, 1);
return convertArguments(newType);
}
/**
......@@ -772,7 +805,8 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray
*/
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
asSpreaderChecks(arrayType, arrayLength);
return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
int spreadArgPos = type.parameterCount() - arrayLength;
return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
}
private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
......@@ -790,7 +824,7 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray
}
}
if (sawProblem) {
ArrayList<Class<?>> ptypes = new ArrayList<Class<?>>(type().parameterList());
ArrayList<Class<?>> ptypes = new ArrayList<>(type().parameterList());
for (int i = nargs - arrayLength; i < nargs; i++) {
ptypes.set(i, arrayElement);
}
......@@ -885,8 +919,12 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
*/
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
asCollectorChecks(arrayType, arrayLength);
int collectArgPos = type().parameterCount()-1;
MethodHandle target = this;
if (arrayType != type().parameterType(collectArgPos))
target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
return MethodHandleImpl.makeCollectArguments(target, collector, collectArgPos, false);
}
// private API: return true if last param exactly matches arrayType
......@@ -1056,7 +1094,7 @@ assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
boolean lastMatch = asCollectorChecks(arrayType, 0);
if (isVarargsCollector() && lastMatch)
return this;
return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
return MethodHandleImpl.makeVarargsCollector(this, arrayType);
}
/**
......@@ -1155,14 +1193,13 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
*/
public MethodHandle bindTo(Object x) {
Class<?> ptype;
if (type().parameterCount() == 0 ||
(ptype = type().parameterType(0)).isPrimitive())
@SuppressWarnings("LocalVariableHidesMemberVariable")
MethodType type = type();
if (type.parameterCount() == 0 ||
(ptype = type.parameterType(0)).isPrimitive())
throw newIllegalArgumentException("no leading reference parameter", x);
x = MethodHandles.checkValue(ptype, x);
// Cf. MethodHandles.insertArguments for the following logic:
MethodHandle bmh = MethodHandleImpl.bindReceiver(this, x);
if (bmh != null) return bmh;
return MethodHandleImpl.bindArgument(this, 0, x);
x = ptype.cast(x); // throw CCE if needed
return bindReceiver(x);
}
/**
......@@ -1183,11 +1220,178 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
@Override
public String toString() {
if (DEBUG_METHOD_HANDLE_NAMES) return debugString();
return standardString();
}
String standardString() {
return "MethodHandle"+type;
}
String debugString() {
return standardString()+"="+internalForm()+internalValues();
}
//// Implementation methods.
//// Sub-classes can override these default implementations.
//// All these methods assume arguments are already validated.
// Other transforms to do: convert, explicitCast, permute, drop, filter, fold, GWT, catch
/*non-public*/
String debugString() {
return getNameString(this);
MethodHandle setVarargs(MemberName member) throws IllegalAccessException {
if (!member.isVarargs()) return this;
int argc = type().parameterCount();
if (argc != 0) {
Class<?> arrayType = type().parameterType(argc-1);
if (arrayType.isArray()) {
return MethodHandleImpl.makeVarargsCollector(this, arrayType);
}
}
throw member.makeAccessException("cannot make variable arity", null);
}
/*non-public*/
MethodHandle viewAsType(MethodType newType) {
// No actual conversions, just a new view of the same method.
if (!type.isViewableAs(newType))
throw new InternalError();
return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
}
// Decoding
/*non-public*/
LambdaForm internalForm() {
return form;
}
/*non-public*/
MemberName internalMemberName() {
return null; // DMH returns DMH.member
}
/*non-public*/
Object internalValues() {
return "";
}
//// Method handle implementation methods.
//// Sub-classes can override these default implementations.
//// All these methods assume arguments are already validated.
/*non-public*/ MethodHandle convertArguments(MethodType newType) {
// Override this if it can be improved.
return MethodHandleImpl.makePairwiseConvert(this, newType, 1);
}
/*non-public*/
MethodHandle bindArgument(int pos, char basicType, Object value) {
// Override this if it can be improved.
return rebind().bindArgument(pos, basicType, value);
}
/*non-public*/
MethodHandle bindReceiver(Object receiver) {
// Override this if it can be improved.
return bindArgument(0, 'L', receiver);
}
/*non-public*/
MethodHandle bindImmediate(int pos, char basicType, Object value) {
// Bind an immediate value to a position in the arguments.
// This means, elide the respective argument,
// and replace all references to it in NamedFunction args with the specified value.
// CURRENT RESTRICTIONS
// * only for pos 0 and UNSAFE (position is adjusted in MHImpl to make API usable for others)
assert pos == 0 && basicType == 'L' && value instanceof Unsafe;
MethodType type2 = type.dropParameterTypes(pos, pos + 1); // adjustment: ignore receiver!
LambdaForm form2 = form.bindImmediate(pos + 1, basicType, value); // adjust pos to form-relative pos
return copyWith(type2, form2);
}
/*non-public*/
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
throw new InternalError("copyWith: " + this.getClass());
}
/*non-public*/
MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
// Override this if it can be improved.
return rebind().dropArguments(srcType, pos, drops);
}
/*non-public*/
MethodHandle permuteArguments(MethodType newType, int[] reorder) {
// Override this if it can be improved.
return rebind().permuteArguments(newType, reorder);
}
/*non-public*/
MethodHandle rebind() {
// Bind 'this' into a new invoker, of the known class BMH.
MethodType type2 = type();
LambdaForm form2 = reinvokerForm(type2.basicType());
// form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
return BoundMethodHandle.bindSingle(type2, form2, this);
}
/*non-public*/
MethodHandle reinvokerTarget() {
throw new InternalError("not a reinvoker MH: "+this.getClass().getName()+": "+this);
}
/** Create a LF which simply reinvokes a target of the given basic type.
* The target MH must override {@link #reinvokerTarget} to provide the target.
*/
static LambdaForm reinvokerForm(MethodType mtype) {
mtype = mtype.basicType();
LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
if (reinvoker != null) return reinvoker;
MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
final int THIS_BMH = 0;
final int ARG_BASE = 1;
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
int nameCursor = ARG_LIMIT;
final int NEXT_MH = nameCursor++;
final int REINVOKE = nameCursor++;
LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
names[REINVOKE] = new LambdaForm.Name(MH_invokeBasic, targetArgs);
return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
}
private static final LambdaForm.NamedFunction NF_reinvokerTarget;
static {
try {
NF_reinvokerTarget = new LambdaForm.NamedFunction(MethodHandle.class
.getDeclaredMethod("reinvokerTarget"));
} catch (ReflectiveOperationException ex) {
throw new InternalError(ex);
}
}
/**
* Replace the old lambda form of this method handle with a new one.
* The new one must be functionally equivalent to the old one.
* Threads may continue running the old form indefinitely,
* but it is likely that the new one will be preferred for new executions.
* Use with discretion.
* @param newForm
*/
/*non-public*/
void updateForm(LambdaForm newForm) {
if (form == newForm) return;
// ISSUE: Should we have a memory fence here?
UNSAFE.putObject(this, FORM_OFFSET, newForm);
this.form.prepare(); // as in MethodHandle.<init>
}
private static final long FORM_OFFSET;
static {
try {
FORM_OFFSET = UNSAFE.objectFieldOffset(MethodHandle.class.getDeclaredField("form"));
} catch (ReflectiveOperationException ex) {
throw new InternalError(ex);
}
}
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
import java.lang.invoke.MethodHandleNatives.Constants;
//Not yet public: public
class MethodHandleInfo {
public static final int
REF_NONE = Constants.REF_NONE,
REF_getField = Constants.REF_getField,
REF_getStatic = Constants.REF_getStatic,
REF_putField = Constants.REF_putField,
REF_putStatic = Constants.REF_putStatic,
REF_invokeVirtual = Constants.REF_invokeVirtual,
REF_invokeStatic = Constants.REF_invokeStatic,
REF_invokeSpecial = Constants.REF_invokeSpecial,
REF_newInvokeSpecial = Constants.REF_newInvokeSpecial,
REF_invokeInterface = Constants.REF_invokeInterface;
private final Class<?> declaringClass;
private final String name;
private final MethodType methodType;
private final int referenceKind;
public MethodHandleInfo(MethodHandle mh) throws ReflectiveOperationException {
MemberName mn = mh.internalMemberName();
this.declaringClass = mn.getDeclaringClass();
this.name = mn.getName();
this.methodType = mn.getMethodType();
this.referenceKind = mn.getReferenceKind();
}
public Class<?> getDeclaringClass() {
return declaringClass;
}
public String getName() {
return name;
}
public MethodType getMethodType() {
return methodType;
}
public int getReferenceKind() {
return referenceKind;
}
}
......@@ -27,6 +27,7 @@ package java.lang.invoke;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.misc.Unsafe;
/**
* This class consists exclusively of static names internal to the
......@@ -38,16 +39,30 @@ import java.security.PrivilegedAction;
private MethodHandleStatics() { } // do not instantiate
static final Unsafe UNSAFE = Unsafe.getUnsafe();
static final boolean DEBUG_METHOD_HANDLE_NAMES;
static final boolean DUMP_CLASS_FILES;
static final boolean TRACE_INTERPRETER;
static final boolean TRACE_METHOD_LINKAGE;
static final Integer COMPILE_THRESHOLD;
static {
final Object[] values = { false };
final Object[] values = { false, false, false, false, null };
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES");
values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
return null;
}
});
DEBUG_METHOD_HANDLE_NAMES = (Boolean) values[0];
DUMP_CLASS_FILES = (Boolean) values[1];
TRACE_INTERPRETER = (Boolean) values[2];
TRACE_METHOD_LINKAGE = (Boolean) values[3];
COMPILE_THRESHOLD = (Integer) values[4];
}
/*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
......@@ -55,7 +70,7 @@ import java.security.PrivilegedAction;
type = target.type();
MemberName name = null;
if (target != null)
name = MethodHandleNatives.getMethodName(target);
name = target.internalMemberName();
if (name == null)
return "invoke" + type;
return name.getName() + type;
......@@ -77,20 +92,6 @@ import java.security.PrivilegedAction;
return str + target.type();
}
static void checkSpreadArgument(Object av, int n) {
if (av == null) {
if (n == 0) return;
} else if (av instanceof Object[]) {
int len = ((Object[])av).length;
if (len == n) return;
} else {
int len = java.lang.reflect.Array.getLength(av);
if (len == n) return;
}
// fall through to error:
throw newIllegalArgumentException("Array is not of length "+n);
}
// handy shared exception makers (they simplify the common case code)
/*non-public*/ static RuntimeException newIllegalStateException(String message) {
return new IllegalStateException(message);
......@@ -110,6 +111,9 @@ import java.security.PrivilegedAction;
/*non-public*/ static Error uncaughtException(Exception ex) {
throw new InternalError("uncaught exception", ex);
}
static Error NYI() {
throw new AssertionError("NYI");
}
private static String message(String message, Object obj) {
if (obj != null) message = message + ": " + obj;
return message;
......
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A method handle whose behavior is determined only by its LambdaForm.
* @author jrose
*/
final class SimpleMethodHandle extends MethodHandle {
SimpleMethodHandle(MethodType type, LambdaForm form) {
super(type, form);
}
@Override
MethodHandle bindArgument(int pos, char basicType, Object value) {
MethodType type2 = type().dropParameterTypes(pos, pos+1);
LambdaForm form2 = internalForm().bind(1+pos, BoundMethodHandle.SpeciesData.EMPTY);
return BoundMethodHandle.bindSingle(type2, form2, basicType, value);
}
@Override
MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
LambdaForm newForm = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
return new SimpleMethodHandle(srcType, newForm);
}
@Override
MethodHandle permuteArguments(MethodType newType, int[] reorder) {
LambdaForm form2 = internalForm().permuteArguments(1, reorder, basicTypes(newType.parameterList()));
return new SimpleMethodHandle(newType, form2);
}
@Override
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
return new SimpleMethodHandle(mt, lf);
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册