feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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 sun.invoke.util.Wrapper;
|
||||
|
||||
import static sun.invoke.util.Wrapper.forPrimitiveType;
|
||||
import static sun.invoke.util.Wrapper.forWrapperType;
|
||||
import static sun.invoke.util.Wrapper.isWrapperType;
|
||||
|
||||
/**
|
||||
* Abstract implementation of a lambda metafactory which provides parameter
|
||||
* unrolling and input validation.
|
||||
*
|
||||
* @see LambdaMetafactory
|
||||
*/
|
||||
/* package */ abstract class AbstractValidatingLambdaMetafactory {
|
||||
|
||||
/*
|
||||
* For context, the comments for the following fields are marked in quotes
|
||||
* with their values, given this program:
|
||||
* interface II<T> { Object foo(T x); }
|
||||
* interface JJ<R extends Number> extends II<R> { }
|
||||
* class CC { String impl(int i) { return "impl:"+i; }}
|
||||
* class X {
|
||||
* public static void main(String[] args) {
|
||||
* JJ<Integer> iii = (new CC())::impl;
|
||||
* System.out.printf(">>> %s\n", iii.foo(44));
|
||||
* }}
|
||||
*/
|
||||
final Class<?> targetClass; // The class calling the meta-factory via invokedynamic "class X"
|
||||
final MethodType invokedType; // The type of the invoked method "(CC)II"
|
||||
final Class<?> samBase; // The type of the returned instance "interface JJ"
|
||||
final String samMethodName; // Name of the SAM method "foo"
|
||||
final MethodType samMethodType; // Type of the SAM method "(Object)Object"
|
||||
final MethodHandle implMethod; // Raw method handle for the implementation method
|
||||
final MethodHandleInfo implInfo; // Info about the implementation method handle "MethodHandleInfo[5 CC.impl(int)String]"
|
||||
final int implKind; // Invocation kind for implementation "5"=invokevirtual
|
||||
final boolean implIsInstanceMethod; // Is the implementation an instance method "true"
|
||||
final Class<?> implDefiningClass; // Type defining the implementation "class CC"
|
||||
final MethodType implMethodType; // Type of the implementation method "(int)String"
|
||||
final MethodType instantiatedMethodType; // Instantiated erased functional interface method type "(Integer)Object"
|
||||
final boolean isSerializable; // Should the returned instance be serializable
|
||||
final Class<?>[] markerInterfaces; // Additional marker interfaces to be implemented
|
||||
final MethodType[] additionalBridges; // Signatures of additional methods to bridge
|
||||
|
||||
|
||||
/**
|
||||
* Meta-factory constructor.
|
||||
*
|
||||
* @param caller Stacked automatically by VM; represents a lookup context
|
||||
* with the accessibility privileges of the caller.
|
||||
* @param invokedType Stacked automatically by VM; the signature of the
|
||||
* invoked method, which includes the expected static
|
||||
* type of the returned lambda object, and the static
|
||||
* types of the captured arguments for the lambda. In
|
||||
* the event that the implementation method is an
|
||||
* instance method, the first argument in the invocation
|
||||
* signature will correspond to the receiver.
|
||||
* @param samMethodName Name of the method in the functional interface to
|
||||
* which the lambda or method reference is being
|
||||
* converted, represented as a String.
|
||||
* @param samMethodType Type of the method in the functional interface to
|
||||
* which the lambda or method reference is being
|
||||
* converted, represented as a MethodType.
|
||||
* @param implMethod The implementation method which should be called
|
||||
* (with suitable adaptation of argument types, return
|
||||
* types, and adjustment for captured arguments) when
|
||||
* methods of the resulting functional interface instance
|
||||
* are invoked.
|
||||
* @param instantiatedMethodType The signature of the primary functional
|
||||
* interface method after type variables are
|
||||
* substituted with their instantiation from
|
||||
* the capture site
|
||||
* @param isSerializable Should the lambda be made serializable? If set,
|
||||
* either the target type or one of the additional SAM
|
||||
* types must extend {@code Serializable}.
|
||||
* @param markerInterfaces Additional interfaces which the lambda object
|
||||
* should implement.
|
||||
* @param additionalBridges Method types for additional signatures to be
|
||||
* bridged to the implementation method
|
||||
* @throws LambdaConversionException If any of the meta-factory protocol
|
||||
* invariants are violated
|
||||
*/
|
||||
AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller,
|
||||
MethodType invokedType,
|
||||
String samMethodName,
|
||||
MethodType samMethodType,
|
||||
MethodHandle implMethod,
|
||||
MethodType instantiatedMethodType,
|
||||
boolean isSerializable,
|
||||
Class<?>[] markerInterfaces,
|
||||
MethodType[] additionalBridges)
|
||||
throws LambdaConversionException {
|
||||
if ((caller.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) {
|
||||
throw new LambdaConversionException(String.format(
|
||||
"Invalid caller: %s",
|
||||
caller.lookupClass().getName()));
|
||||
}
|
||||
this.targetClass = caller.lookupClass();
|
||||
this.invokedType = invokedType;
|
||||
|
||||
this.samBase = invokedType.returnType();
|
||||
|
||||
this.samMethodName = samMethodName;
|
||||
this.samMethodType = samMethodType;
|
||||
|
||||
this.implMethod = implMethod;
|
||||
this.implInfo = caller.revealDirect(implMethod);
|
||||
this.implKind = implInfo.getReferenceKind();
|
||||
this.implIsInstanceMethod =
|
||||
implKind == MethodHandleInfo.REF_invokeVirtual ||
|
||||
implKind == MethodHandleInfo.REF_invokeSpecial ||
|
||||
implKind == MethodHandleInfo.REF_invokeInterface;
|
||||
this.implDefiningClass = implInfo.getDeclaringClass();
|
||||
this.implMethodType = implInfo.getMethodType();
|
||||
this.instantiatedMethodType = instantiatedMethodType;
|
||||
this.isSerializable = isSerializable;
|
||||
this.markerInterfaces = markerInterfaces;
|
||||
this.additionalBridges = additionalBridges;
|
||||
|
||||
if (!samBase.isInterface()) {
|
||||
throw new LambdaConversionException(String.format(
|
||||
"Functional interface %s is not an interface",
|
||||
samBase.getName()));
|
||||
}
|
||||
|
||||
for (Class<?> c : markerInterfaces) {
|
||||
if (!c.isInterface()) {
|
||||
throw new LambdaConversionException(String.format(
|
||||
"Marker interface %s is not an interface",
|
||||
c.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the CallSite.
|
||||
*
|
||||
* @return a CallSite, which, when invoked, will return an instance of the
|
||||
* functional interface
|
||||
* @throws ReflectiveOperationException
|
||||
*/
|
||||
abstract CallSite buildCallSite()
|
||||
throws LambdaConversionException;
|
||||
|
||||
/**
|
||||
* Check the meta-factory arguments for errors
|
||||
* @throws LambdaConversionException if there are improper conversions
|
||||
*/
|
||||
void validateMetafactoryArgs() throws LambdaConversionException {
|
||||
switch (implKind) {
|
||||
case MethodHandleInfo.REF_invokeInterface:
|
||||
case MethodHandleInfo.REF_invokeVirtual:
|
||||
case MethodHandleInfo.REF_invokeStatic:
|
||||
case MethodHandleInfo.REF_newInvokeSpecial:
|
||||
case MethodHandleInfo.REF_invokeSpecial:
|
||||
break;
|
||||
default:
|
||||
throw new LambdaConversionException(String.format("Unsupported MethodHandle kind: %s", implInfo));
|
||||
}
|
||||
|
||||
// Check arity: optional-receiver + captured + SAM == impl
|
||||
final int implArity = implMethodType.parameterCount();
|
||||
final int receiverArity = implIsInstanceMethod ? 1 : 0;
|
||||
final int capturedArity = invokedType.parameterCount();
|
||||
final int samArity = samMethodType.parameterCount();
|
||||
final int instantiatedArity = instantiatedMethodType.parameterCount();
|
||||
if (implArity + receiverArity != capturedArity + samArity) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Incorrect number of parameters for %s method %s; %d captured parameters, %d functional interface method parameters, %d implementation parameters",
|
||||
implIsInstanceMethod ? "instance" : "static", implInfo,
|
||||
capturedArity, samArity, implArity));
|
||||
}
|
||||
if (instantiatedArity != samArity) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Incorrect number of parameters for %s method %s; %d instantiated parameters, %d functional interface method parameters",
|
||||
implIsInstanceMethod ? "instance" : "static", implInfo,
|
||||
instantiatedArity, samArity));
|
||||
}
|
||||
for (MethodType bridgeMT : additionalBridges) {
|
||||
if (bridgeMT.parameterCount() != samArity) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Incorrect number of parameters for bridge signature %s; incompatible with %s",
|
||||
bridgeMT, samMethodType));
|
||||
}
|
||||
}
|
||||
|
||||
// If instance: first captured arg (receiver) must be subtype of class where impl method is defined
|
||||
final int capturedStart;
|
||||
final int samStart;
|
||||
if (implIsInstanceMethod) {
|
||||
final Class<?> receiverClass;
|
||||
|
||||
// implementation is an instance method, adjust for receiver in captured variables / SAM arguments
|
||||
if (capturedArity == 0) {
|
||||
// receiver is function parameter
|
||||
capturedStart = 0;
|
||||
samStart = 1;
|
||||
receiverClass = instantiatedMethodType.parameterType(0);
|
||||
} else {
|
||||
// receiver is a captured variable
|
||||
capturedStart = 1;
|
||||
samStart = 0;
|
||||
receiverClass = invokedType.parameterType(0);
|
||||
}
|
||||
|
||||
// check receiver type
|
||||
if (!implDefiningClass.isAssignableFrom(receiverClass)) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Invalid receiver type %s; not a subtype of implementation type %s",
|
||||
receiverClass, implDefiningClass));
|
||||
}
|
||||
|
||||
Class<?> implReceiverClass = implMethod.type().parameterType(0);
|
||||
if (implReceiverClass != implDefiningClass && !implReceiverClass.isAssignableFrom(receiverClass)) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Invalid receiver type %s; not a subtype of implementation receiver type %s",
|
||||
receiverClass, implReceiverClass));
|
||||
}
|
||||
} else {
|
||||
// no receiver
|
||||
capturedStart = 0;
|
||||
samStart = 0;
|
||||
}
|
||||
|
||||
// Check for exact match on non-receiver captured arguments
|
||||
final int implFromCaptured = capturedArity - capturedStart;
|
||||
for (int i=0; i<implFromCaptured; i++) {
|
||||
Class<?> implParamType = implMethodType.parameterType(i);
|
||||
Class<?> capturedParamType = invokedType.parameterType(i + capturedStart);
|
||||
if (!capturedParamType.equals(implParamType)) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Type mismatch in captured lambda parameter %d: expecting %s, found %s",
|
||||
i, capturedParamType, implParamType));
|
||||
}
|
||||
}
|
||||
// Check for adaptation match on SAM arguments
|
||||
final int samOffset = samStart - implFromCaptured;
|
||||
for (int i=implFromCaptured; i<implArity; i++) {
|
||||
Class<?> implParamType = implMethodType.parameterType(i);
|
||||
Class<?> instantiatedParamType = instantiatedMethodType.parameterType(i + samOffset);
|
||||
if (!isAdaptableTo(instantiatedParamType, implParamType, true)) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Type mismatch for lambda argument %d: %s is not convertible to %s",
|
||||
i, instantiatedParamType, implParamType));
|
||||
}
|
||||
}
|
||||
|
||||
// Adaptation match: return type
|
||||
Class<?> expectedType = instantiatedMethodType.returnType();
|
||||
Class<?> actualReturnType =
|
||||
(implKind == MethodHandleInfo.REF_newInvokeSpecial)
|
||||
? implDefiningClass
|
||||
: implMethodType.returnType();
|
||||
Class<?> samReturnType = samMethodType.returnType();
|
||||
if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Type mismatch for lambda return: %s is not convertible to %s",
|
||||
actualReturnType, expectedType));
|
||||
}
|
||||
if (!isAdaptableToAsReturnStrict(expectedType, samReturnType)) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Type mismatch for lambda expected return: %s is not convertible to %s",
|
||||
expectedType, samReturnType));
|
||||
}
|
||||
for (MethodType bridgeMT : additionalBridges) {
|
||||
if (!isAdaptableToAsReturnStrict(expectedType, bridgeMT.returnType())) {
|
||||
throw new LambdaConversionException(
|
||||
String.format("Type mismatch for lambda expected return: %s is not convertible to %s",
|
||||
expectedType, bridgeMT.returnType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check type adaptability for parameter types.
|
||||
* @param fromType Type to convert from
|
||||
* @param toType Type to convert to
|
||||
* @param strict If true, do strict checks, else allow that fromType may be parameterized
|
||||
* @return True if 'fromType' can be passed to an argument of 'toType'
|
||||
*/
|
||||
private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict) {
|
||||
if (fromType.equals(toType)) {
|
||||
return true;
|
||||
}
|
||||
if (fromType.isPrimitive()) {
|
||||
Wrapper wfrom = forPrimitiveType(fromType);
|
||||
if (toType.isPrimitive()) {
|
||||
// both are primitive: widening
|
||||
Wrapper wto = forPrimitiveType(toType);
|
||||
return wto.isConvertibleFrom(wfrom);
|
||||
} else {
|
||||
// from primitive to reference: boxing
|
||||
return toType.isAssignableFrom(wfrom.wrapperType());
|
||||
}
|
||||
} else {
|
||||
if (toType.isPrimitive()) {
|
||||
// from reference to primitive: unboxing
|
||||
Wrapper wfrom;
|
||||
if (isWrapperType(fromType) && (wfrom = forWrapperType(fromType)).primitiveType().isPrimitive()) {
|
||||
// fromType is a primitive wrapper; unbox+widen
|
||||
Wrapper wto = forPrimitiveType(toType);
|
||||
return wto.isConvertibleFrom(wfrom);
|
||||
} else {
|
||||
// must be convertible to primitive
|
||||
return !strict;
|
||||
}
|
||||
} else {
|
||||
// both are reference types: fromType should be a superclass of toType.
|
||||
return !strict || toType.isAssignableFrom(fromType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check type adaptability for return types --
|
||||
* special handling of void type) and parameterized fromType
|
||||
* @return True if 'fromType' can be converted to 'toType'
|
||||
*/
|
||||
private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) {
|
||||
return toType.equals(void.class)
|
||||
|| !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false);
|
||||
}
|
||||
private boolean isAdaptableToAsReturnStrict(Class<?> fromType, Class<?> toType) {
|
||||
if (fromType.equals(void.class)) return toType.equals(void.class);
|
||||
return isAdaptableTo(fromType, toType, true);
|
||||
}
|
||||
|
||||
|
||||
/*********** Logging support -- for debugging only, uncomment as needed
|
||||
static final Executor logPool = Executors.newSingleThreadExecutor();
|
||||
protected static void log(final String s) {
|
||||
MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected static void log(final String s, final Throwable e) {
|
||||
MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println(s);
|
||||
e.printStackTrace(System.out);
|
||||
}
|
||||
});
|
||||
}
|
||||
***********************/
|
||||
|
||||
}
|
||||
845
jdkSrc/jdk8/java/lang/invoke/BoundMethodHandle.java
Normal file
845
jdkSrc/jdk8/java/lang/invoke/BoundMethodHandle.java
Normal file
@@ -0,0 +1,845 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, 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 jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
import java.lang.invoke.LambdaForm.NamedFunction;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.FieldVisitor;
|
||||
import sun.invoke.util.ValueConversions;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
|
||||
/**
|
||||
* The flavor of method handle which emulates an invoke instruction
|
||||
* on a predetermined argument. The JVM dispatches to the correct method
|
||||
* when the handle is created, not when it is invoked.
|
||||
*
|
||||
* All bound arguments are encapsulated in dedicated species.
|
||||
*/
|
||||
/*non-public*/ abstract class BoundMethodHandle extends MethodHandle {
|
||||
|
||||
/*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) {
|
||||
super(type, form);
|
||||
assert(speciesData() == speciesData(form));
|
||||
}
|
||||
|
||||
//
|
||||
// BMH API and internals
|
||||
//
|
||||
|
||||
static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) {
|
||||
// for some type signatures, there exist pre-defined concrete BMH classes
|
||||
try {
|
||||
switch (xtype) {
|
||||
case L_TYPE:
|
||||
return bindSingle(type, form, x); // Use known fast path.
|
||||
case I_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor().invokeBasic(type, form, ValueConversions.widenSubword(x));
|
||||
case J_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor().invokeBasic(type, form, (long) x);
|
||||
case F_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor().invokeBasic(type, form, (float) x);
|
||||
case D_TYPE:
|
||||
return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor().invokeBasic(type, form, (double) x);
|
||||
default : throw newInternalError("unexpected xtype: " + xtype);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw newInternalError(t);
|
||||
}
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
LambdaFormEditor editor() {
|
||||
return form.editor();
|
||||
}
|
||||
|
||||
static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
|
||||
return Species_L.make(type, form, x);
|
||||
}
|
||||
|
||||
@Override // there is a default binder in the super class, for 'L' types only
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentL(int pos, Object value) {
|
||||
return editor().bindArgumentL(this, pos, value);
|
||||
}
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentI(int pos, int value) {
|
||||
return editor().bindArgumentI(this, pos, value);
|
||||
}
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentJ(int pos, long value) {
|
||||
return editor().bindArgumentJ(this, pos, value);
|
||||
}
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentF(int pos, float value) {
|
||||
return editor().bindArgumentF(this, pos, value);
|
||||
}
|
||||
/*non-public*/
|
||||
BoundMethodHandle bindArgumentD(int pos, double value) {
|
||||
return editor().bindArgumentD(this, pos, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
BoundMethodHandle rebind() {
|
||||
if (!tooComplex()) {
|
||||
return this;
|
||||
}
|
||||
return makeReinvoker(this);
|
||||
}
|
||||
|
||||
private boolean tooComplex() {
|
||||
return (fieldCount() > FIELD_COUNT_THRESHOLD ||
|
||||
form.expressionCount() > FORM_EXPRESSION_THRESHOLD);
|
||||
}
|
||||
private static final int FIELD_COUNT_THRESHOLD = 12; // largest convenient BMH field count
|
||||
private static final int FORM_EXPRESSION_THRESHOLD = 24; // largest convenient BMH expression count
|
||||
|
||||
/**
|
||||
* A reinvoker MH has this form:
|
||||
* {@code lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }}
|
||||
*/
|
||||
static BoundMethodHandle makeReinvoker(MethodHandle target) {
|
||||
LambdaForm form = DelegatingMethodHandle.makeReinvokerForm(
|
||||
target, MethodTypeForm.LF_REBIND,
|
||||
Species_L.SPECIES_DATA, Species_L.SPECIES_DATA.getterFunction(0));
|
||||
return Species_L.make(target.type(), form, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a
|
||||
* static field containing this value, and they must accordingly implement this method.
|
||||
*/
|
||||
/*non-public*/ abstract SpeciesData speciesData();
|
||||
|
||||
/*non-public*/ static SpeciesData speciesData(LambdaForm form) {
|
||||
Object c = form.names[0].constraint;
|
||||
if (c instanceof SpeciesData)
|
||||
return (SpeciesData) c;
|
||||
// if there is no BMH constraint, then use the null constraint
|
||||
return SpeciesData.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of fields in this BMH. Equivalent to speciesData().fieldCount().
|
||||
*/
|
||||
/*non-public*/ abstract int fieldCount();
|
||||
|
||||
@Override
|
||||
Object internalProperties() {
|
||||
return "\n& BMH="+internalValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
final Object internalValues() {
|
||||
Object[] boundValues = new Object[speciesData().fieldCount()];
|
||||
for (int i = 0; i < boundValues.length; ++i) {
|
||||
boundValues[i] = arg(i);
|
||||
}
|
||||
return Arrays.asList(boundValues);
|
||||
}
|
||||
|
||||
/*non-public*/ final Object arg(int i) {
|
||||
try {
|
||||
switch (speciesData().fieldType(i)) {
|
||||
case L_TYPE: return speciesData().getters[i].invokeBasic(this);
|
||||
case I_TYPE: return (int) speciesData().getters[i].invokeBasic(this);
|
||||
case J_TYPE: return (long) speciesData().getters[i].invokeBasic(this);
|
||||
case F_TYPE: return (float) speciesData().getters[i].invokeBasic(this);
|
||||
case D_TYPE: return (double) speciesData().getters[i].invokeBasic(this);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
throw new InternalError("unexpected type: " + speciesData().typeChars+"."+i);
|
||||
}
|
||||
|
||||
//
|
||||
// cloning API
|
||||
//
|
||||
|
||||
/*non-public*/ abstract BoundMethodHandle copyWith(MethodType mt, LambdaForm lf);
|
||||
/*non-public*/ abstract BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg);
|
||||
/*non-public*/ abstract BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg);
|
||||
/*non-public*/ abstract BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg);
|
||||
/*non-public*/ abstract BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg);
|
||||
/*non-public*/ abstract BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg);
|
||||
|
||||
//
|
||||
// concrete BMH classes required to close bootstrap loops
|
||||
//
|
||||
|
||||
private // make it private to force users to access the enclosing class first
|
||||
static final class Species_L extends BoundMethodHandle {
|
||||
final Object argL0;
|
||||
private Species_L(MethodType mt, LambdaForm lf, Object argL0) {
|
||||
super(mt, lf);
|
||||
this.argL0 = argL0;
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ SpeciesData speciesData() {
|
||||
return SPECIES_DATA;
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ int fieldCount() {
|
||||
return 1;
|
||||
}
|
||||
/*non-public*/ static final SpeciesData SPECIES_DATA = new SpeciesData("L", Species_L.class);
|
||||
/*non-public*/ static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) {
|
||||
return new Species_L(mt, lf, argL0);
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new Species_L(mt, lf, argL0);
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// BMH species meta-data
|
||||
//
|
||||
|
||||
/**
|
||||
* Meta-data wrapper for concrete BMH types.
|
||||
* Each BMH type corresponds to a given sequence of basic field types (LIJFD).
|
||||
* The fields are immutable; their values are fully specified at object construction.
|
||||
* Each BMH type supplies an array of getter functions which may be used in lambda forms.
|
||||
* A BMH is constructed by cloning a shorter BMH and adding one or more new field values.
|
||||
* The shortest possible BMH has zero fields; its class is SimpleMethodHandle.
|
||||
* BMH species are not interrelated by subtyping, even though it would appear that
|
||||
* a shorter BMH could serve as a supertype of a longer one which extends it.
|
||||
*/
|
||||
static class SpeciesData {
|
||||
private final String typeChars;
|
||||
private final BasicType[] typeCodes;
|
||||
private final Class<? extends BoundMethodHandle> clazz;
|
||||
// Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
|
||||
// Therefore, we need a non-final link in the chain. Use array elements.
|
||||
@Stable private final MethodHandle[] constructor;
|
||||
@Stable private final MethodHandle[] getters;
|
||||
@Stable private final NamedFunction[] nominalGetters;
|
||||
@Stable private final SpeciesData[] extensions;
|
||||
|
||||
/*non-public*/ int fieldCount() {
|
||||
return typeCodes.length;
|
||||
}
|
||||
/*non-public*/ BasicType fieldType(int i) {
|
||||
return typeCodes[i];
|
||||
}
|
||||
/*non-public*/ char fieldTypeChar(int i) {
|
||||
return typeChars.charAt(i);
|
||||
}
|
||||
Object fieldSignature() {
|
||||
return typeChars;
|
||||
}
|
||||
public Class<? extends BoundMethodHandle> fieldHolder() {
|
||||
return clazz;
|
||||
}
|
||||
public String toString() {
|
||||
return "SpeciesData<"+fieldSignature()+">";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
|
||||
* represents a MH bound to a generic invoker, which in turn forwards to the corresponding
|
||||
* getter.
|
||||
*/
|
||||
NamedFunction getterFunction(int i) {
|
||||
NamedFunction nf = nominalGetters[i];
|
||||
assert(nf.memberDeclaringClassOrNull() == fieldHolder());
|
||||
assert(nf.returnType() == fieldType(i));
|
||||
return nf;
|
||||
}
|
||||
|
||||
NamedFunction[] getterFunctions() {
|
||||
return nominalGetters;
|
||||
}
|
||||
|
||||
MethodHandle[] getterHandles() { return getters; }
|
||||
|
||||
MethodHandle constructor() {
|
||||
return constructor[0];
|
||||
}
|
||||
|
||||
static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
|
||||
|
||||
SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
|
||||
this.typeChars = types;
|
||||
this.typeCodes = basicTypes(types);
|
||||
this.clazz = clazz;
|
||||
if (!INIT_DONE) {
|
||||
this.constructor = new MethodHandle[1]; // only one ctor
|
||||
this.getters = new MethodHandle[types.length()];
|
||||
this.nominalGetters = new NamedFunction[types.length()];
|
||||
} else {
|
||||
this.constructor = Factory.makeCtors(clazz, types, null);
|
||||
this.getters = Factory.makeGetters(clazz, types, null);
|
||||
this.nominalGetters = Factory.makeNominalGetters(types, null, this.getters);
|
||||
}
|
||||
this.extensions = new SpeciesData[ARG_TYPE_LIMIT];
|
||||
}
|
||||
|
||||
private void initForBootstrap() {
|
||||
assert(!INIT_DONE);
|
||||
if (constructor() == null) {
|
||||
String types = typeChars;
|
||||
CACHE.put(types, this);
|
||||
Factory.makeCtors(clazz, types, this.constructor);
|
||||
Factory.makeGetters(clazz, types, this.getters);
|
||||
Factory.makeNominalGetters(types, this.nominalGetters, this.getters);
|
||||
}
|
||||
}
|
||||
|
||||
private static final ConcurrentMap<String, SpeciesData> CACHE = new ConcurrentHashMap<>();
|
||||
private static final boolean INIT_DONE; // set after <clinit> finishes...
|
||||
|
||||
SpeciesData extendWith(byte type) {
|
||||
return extendWith(BasicType.basicType(type));
|
||||
}
|
||||
|
||||
SpeciesData extendWith(BasicType type) {
|
||||
int ord = type.ordinal();
|
||||
SpeciesData d = extensions[ord];
|
||||
if (d != null) return d;
|
||||
extensions[ord] = d = get(typeChars+type.basicTypeChar());
|
||||
return d;
|
||||
}
|
||||
|
||||
private static SpeciesData get(String types) {
|
||||
return CACHE.computeIfAbsent(types, new Function<String, SpeciesData>() {
|
||||
@Override
|
||||
public SpeciesData apply(String types) {
|
||||
Class<? extends BoundMethodHandle> bmhcl = Factory.getConcreteBMHClass(types);
|
||||
// SpeciesData instantiation may throw VirtualMachineError because of
|
||||
// code cache overflow...
|
||||
SpeciesData speciesData = new SpeciesData(types, bmhcl);
|
||||
// CHM.computeIfAbsent ensures only one SpeciesData will be set
|
||||
// successfully on the concrete BMH class if ever
|
||||
Factory.setSpeciesDataToConcreteBMHClass(bmhcl, speciesData);
|
||||
// the concrete BMH class is published via SpeciesData instance
|
||||
// returned here only after it's SPECIES_DATA field is set
|
||||
return speciesData;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This is to be called when assertions are enabled. It checks whether SpeciesData for all of the statically
|
||||
* defined species subclasses of BoundMethodHandle has been added to the SpeciesData cache. See below in the
|
||||
* static initializer for
|
||||
*/
|
||||
static boolean speciesDataCachePopulated() {
|
||||
Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
|
||||
try {
|
||||
for (Class<?> c : rootCls.getDeclaredClasses()) {
|
||||
if (rootCls.isAssignableFrom(c)) {
|
||||
final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
|
||||
SpeciesData d = Factory.getSpeciesDataFromConcreteBMHClass(cbmh);
|
||||
assert(d != null) : cbmh.getName();
|
||||
assert(d.clazz == cbmh);
|
||||
assert(CACHE.get(d.typeChars) == d);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw newInternalError(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static {
|
||||
// Pre-fill the BMH species-data cache with EMPTY and all BMH's inner subclasses.
|
||||
EMPTY.initForBootstrap();
|
||||
Species_L.SPECIES_DATA.initForBootstrap();
|
||||
// check that all static SpeciesData instances have been initialized
|
||||
assert speciesDataCachePopulated();
|
||||
// Note: Do not simplify this, because INIT_DONE must not be
|
||||
// a compile-time constant during bootstrapping.
|
||||
INIT_DONE = Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static SpeciesData getSpeciesData(String types) {
|
||||
return SpeciesData.get(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generation of concrete BMH classes.
|
||||
*
|
||||
* A concrete BMH species is fit for binding a number of values adhering to a
|
||||
* given type pattern. Reference types are erased.
|
||||
*
|
||||
* BMH species are cached by type pattern.
|
||||
*
|
||||
* A BMH species has a number of fields with the concrete (possibly erased) types of
|
||||
* bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
|
||||
* which can be included as names in lambda forms.
|
||||
*/
|
||||
static class Factory {
|
||||
|
||||
static final String JLO_SIG = "Ljava/lang/Object;";
|
||||
static final String JLS_SIG = "Ljava/lang/String;";
|
||||
static final String JLC_SIG = "Ljava/lang/Class;";
|
||||
static final String MH = "java/lang/invoke/MethodHandle";
|
||||
static final String MH_SIG = "L"+MH+";";
|
||||
static final String BMH = "java/lang/invoke/BoundMethodHandle";
|
||||
static final String BMH_SIG = "L"+BMH+";";
|
||||
static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData";
|
||||
static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
|
||||
static final String STABLE_SIG = "Ljava/lang/invoke/Stable;";
|
||||
|
||||
static final String SPECIES_PREFIX_NAME = "Species_";
|
||||
static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
|
||||
|
||||
static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
|
||||
static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
|
||||
static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
|
||||
static final String VOID_SIG = "()V";
|
||||
static final String INT_SIG = "()I";
|
||||
|
||||
static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
|
||||
|
||||
static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
|
||||
|
||||
static final ConcurrentMap<String, Class<? extends BoundMethodHandle>> CLASS_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Get a concrete subclass of BMH for a given combination of bound types.
|
||||
*
|
||||
* @param types the type signature, wherein reference types are erased to 'L'
|
||||
* @return the concrete BMH class
|
||||
*/
|
||||
static Class<? extends BoundMethodHandle> getConcreteBMHClass(String types) {
|
||||
// CHM.computeIfAbsent ensures generateConcreteBMHClass is called
|
||||
// only once per key.
|
||||
return CLASS_CACHE.computeIfAbsent(
|
||||
types, new Function<String, Class<? extends BoundMethodHandle>>() {
|
||||
@Override
|
||||
public Class<? extends BoundMethodHandle> apply(String types) {
|
||||
return generateConcreteBMHClass(types);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a concrete subclass of BMH for a given combination of bound types.
|
||||
*
|
||||
* A concrete BMH species adheres to the following schema:
|
||||
*
|
||||
* <pre>
|
||||
* class Species_[[types]] extends BoundMethodHandle {
|
||||
* [[fields]]
|
||||
* final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* The {@code [[types]]} signature is precisely the string that is passed to this
|
||||
* method.
|
||||
*
|
||||
* The {@code [[fields]]} section consists of one field definition per character in
|
||||
* the type signature, adhering to the naming schema described in the definition of
|
||||
* {@link #makeFieldName}.
|
||||
*
|
||||
* For example, a concrete BMH species for two reference and one integral bound values
|
||||
* would have the following shape:
|
||||
*
|
||||
* <pre>
|
||||
* class BoundMethodHandle { ... private static
|
||||
* final class Species_LLI extends BoundMethodHandle {
|
||||
* final Object argL0;
|
||||
* final Object argL1;
|
||||
* final int argI2;
|
||||
* private Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
|
||||
* super(mt, lf);
|
||||
* this.argL0 = argL0;
|
||||
* this.argL1 = argL1;
|
||||
* this.argI2 = argI2;
|
||||
* }
|
||||
* final SpeciesData speciesData() { return SPECIES_DATA; }
|
||||
* final int fieldCount() { return 3; }
|
||||
* @Stable static SpeciesData SPECIES_DATA; // injected afterwards
|
||||
* static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
|
||||
* return new Species_LLI(mt, lf, argL0, argL1, argI2);
|
||||
* }
|
||||
* final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
* return new Species_LLI(mt, lf, argL0, argL1, argI2);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
|
||||
* return SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
|
||||
* return SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
|
||||
* return SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
|
||||
* return SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
|
||||
* return SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param types the type signature, wherein reference types are erased to 'L'
|
||||
* @return the generated concrete BMH class
|
||||
*/
|
||||
static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
|
||||
String shortTypes = LambdaForm.shortenSignature(types);
|
||||
final String className = SPECIES_PREFIX_PATH + shortTypes;
|
||||
final String sourceFile = SPECIES_PREFIX_NAME + shortTypes;
|
||||
final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
|
||||
cw.visit(V1_6, NOT_ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
|
||||
cw.visitSource(sourceFile, null);
|
||||
|
||||
// emit static types and SPECIES_DATA fields
|
||||
FieldVisitor fw = cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null);
|
||||
fw.visitAnnotation(STABLE_SIG, true);
|
||||
fw.visitEnd();
|
||||
|
||||
// emit bound argument fields
|
||||
for (int i = 0; i < types.length(); ++i) {
|
||||
final char t = types.charAt(i);
|
||||
final String fieldName = makeFieldName(types, i);
|
||||
final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t);
|
||||
cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd();
|
||||
}
|
||||
|
||||
MethodVisitor mv;
|
||||
|
||||
// emit constructor
|
||||
mv = cw.visitMethod(ACC_PRIVATE, "<init>", makeSignature(types, true), null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0); // this
|
||||
mv.visitVarInsn(ALOAD, 1); // type
|
||||
mv.visitVarInsn(ALOAD, 2); // form
|
||||
|
||||
mv.visitMethodInsn(INVOKESPECIAL, BMH, "<init>", makeSignature("", true), false);
|
||||
|
||||
for (int i = 0, j = 0; i < types.length(); ++i, ++j) {
|
||||
// i counts the arguments, j counts corresponding argument slots
|
||||
char t = types.charAt(i);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(typeLoadOp(t), j + 3); // parameters start at 3
|
||||
mv.visitFieldInsn(PUTFIELD, className, makeFieldName(types, i), typeSig(t));
|
||||
if (t == 'J' || t == 'D') {
|
||||
++j; // adjust argument register access
|
||||
}
|
||||
}
|
||||
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// emit implementation of speciesData()
|
||||
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// emit implementation of fieldCount()
|
||||
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "fieldCount", INT_SIG, null, null);
|
||||
mv.visitCode();
|
||||
int fc = types.length();
|
||||
if (fc <= (ICONST_5 - ICONST_0)) {
|
||||
mv.visitInsn(ICONST_0 + fc);
|
||||
} else {
|
||||
mv.visitIntInsn(SIPUSH, fc);
|
||||
}
|
||||
mv.visitInsn(IRETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
// emit make() ...factory method wrapping constructor
|
||||
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_STATIC, "make", makeSignature(types, false), null, null);
|
||||
mv.visitCode();
|
||||
// make instance
|
||||
mv.visitTypeInsn(NEW, className);
|
||||
mv.visitInsn(DUP);
|
||||
// load mt, lf
|
||||
mv.visitVarInsn(ALOAD, 0); // type
|
||||
mv.visitVarInsn(ALOAD, 1); // form
|
||||
// load factory method arguments
|
||||
for (int i = 0, j = 0; i < types.length(); ++i, ++j) {
|
||||
// i counts the arguments, j counts corresponding argument slots
|
||||
char t = types.charAt(i);
|
||||
mv.visitVarInsn(typeLoadOp(t), j + 2); // parameters start at 3
|
||||
if (t == 'J' || t == 'D') {
|
||||
++j; // adjust argument register access
|
||||
}
|
||||
}
|
||||
|
||||
// finally, invoke the constructor and return
|
||||
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", makeSignature(types, true), false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// emit copyWith()
|
||||
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "copyWith", makeSignature("", false), null, null);
|
||||
mv.visitCode();
|
||||
// make instance
|
||||
mv.visitTypeInsn(NEW, className);
|
||||
mv.visitInsn(DUP);
|
||||
// load mt, lf
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
// put fields on the stack
|
||||
emitPushFields(types, className, mv);
|
||||
// finally, invoke the constructor and return
|
||||
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", makeSignature(types, true), false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// for each type, emit copyWithExtendT()
|
||||
for (BasicType type : BasicType.ARG_TYPES) {
|
||||
int ord = type.ordinal();
|
||||
char btChar = type.basicTypeChar();
|
||||
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "copyWithExtend" + btChar, makeSignature(String.valueOf(btChar), false), null, E_THROWABLE);
|
||||
mv.visitCode();
|
||||
// return SPECIES_DATA.extendWith(t).constructor().invokeBasic(mt, lf, argL0, ..., narg)
|
||||
// obtain constructor
|
||||
mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
|
||||
int iconstInsn = ICONST_0 + ord;
|
||||
assert(iconstInsn <= ICONST_5);
|
||||
mv.visitInsn(iconstInsn);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG, false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "constructor", "()" + MH_SIG, false);
|
||||
// load mt, lf
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
// put fields on the stack
|
||||
emitPushFields(types, className, mv);
|
||||
// put narg on stack
|
||||
mv.visitVarInsn(typeLoadOp(btChar), 3);
|
||||
// finally, invoke the constructor and return
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + btChar, false), false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
cw.visitEnd();
|
||||
|
||||
// load class
|
||||
final byte[] classFile = cw.toByteArray();
|
||||
InvokerBytecodeGenerator.maybeDump(className, classFile);
|
||||
Class<? extends BoundMethodHandle> bmhClass =
|
||||
//UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
|
||||
UNSAFE.defineClass(className, classFile, 0, classFile.length,
|
||||
BoundMethodHandle.class.getClassLoader(), null)
|
||||
.asSubclass(BoundMethodHandle.class);
|
||||
|
||||
return bmhClass;
|
||||
}
|
||||
|
||||
private static int typeLoadOp(char t) {
|
||||
switch (t) {
|
||||
case 'L': return ALOAD;
|
||||
case 'I': return ILOAD;
|
||||
case 'J': return LLOAD;
|
||||
case 'F': return FLOAD;
|
||||
case 'D': return DLOAD;
|
||||
default : throw newInternalError("unrecognized type " + t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitPushFields(String types, String className, MethodVisitor mv) {
|
||||
for (int i = 0; i < types.length(); ++i) {
|
||||
char tc = types.charAt(i);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc));
|
||||
}
|
||||
}
|
||||
|
||||
static String typeSig(char t) {
|
||||
return t == 'L' ? JLO_SIG : String.valueOf(t);
|
||||
}
|
||||
|
||||
//
|
||||
// Getter MH generation.
|
||||
//
|
||||
|
||||
private static MethodHandle makeGetter(Class<?> cbmhClass, String types, int index) {
|
||||
String fieldName = makeFieldName(types, index);
|
||||
Class<?> fieldType = Wrapper.forBasicType(types.charAt(index)).primitiveType();
|
||||
try {
|
||||
return LOOKUP.findGetter(cbmhClass, fieldName, fieldType);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw newInternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) {
|
||||
if (mhs == null) mhs = new MethodHandle[types.length()];
|
||||
for (int i = 0; i < mhs.length; ++i) {
|
||||
mhs[i] = makeGetter(cbmhClass, types, i);
|
||||
assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass);
|
||||
}
|
||||
return mhs;
|
||||
}
|
||||
|
||||
static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) {
|
||||
if (mhs == null) mhs = new MethodHandle[1];
|
||||
if (types.equals("")) return mhs; // hack for empty BMH species
|
||||
mhs[0] = makeCbmhCtor(cbmh, types);
|
||||
return mhs;
|
||||
}
|
||||
|
||||
static NamedFunction[] makeNominalGetters(String types, NamedFunction[] nfs, MethodHandle[] getters) {
|
||||
if (nfs == null) nfs = new NamedFunction[types.length()];
|
||||
for (int i = 0; i < nfs.length; ++i) {
|
||||
nfs[i] = new NamedFunction(getters[i]);
|
||||
}
|
||||
return nfs;
|
||||
}
|
||||
|
||||
//
|
||||
// Auxiliary methods.
|
||||
//
|
||||
|
||||
static SpeciesData getSpeciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
|
||||
try {
|
||||
Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
|
||||
return (SpeciesData) F_SPECIES_DATA.get(null);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void setSpeciesDataToConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh, SpeciesData speciesData) {
|
||||
try {
|
||||
Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
|
||||
assert F_SPECIES_DATA.getDeclaredAnnotation(Stable.class) != null;
|
||||
F_SPECIES_DATA.set(null, speciesData);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Field names in concrete BMHs adhere to this pattern:
|
||||
* arg + type + index
|
||||
* where type is a single character (L, I, J, F, D).
|
||||
*/
|
||||
private static String makeFieldName(String types, int index) {
|
||||
assert index >= 0 && index < types.length();
|
||||
return "arg" + types.charAt(index) + index;
|
||||
}
|
||||
|
||||
private static String makeSignature(String types, boolean ctor) {
|
||||
StringBuilder buf = new StringBuilder(SIG_INCIPIT);
|
||||
for (char c : types.toCharArray()) {
|
||||
buf.append(typeSig(c));
|
||||
}
|
||||
return buf.append(')').append(ctor ? "V" : BMH_SIG).toString();
|
||||
}
|
||||
|
||||
static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
|
||||
try {
|
||||
return LOOKUP.findStatic(cbmh, "make", MethodType.fromMethodDescriptorString(makeSignature(types, false), null));
|
||||
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
|
||||
throw newInternalError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
|
||||
|
||||
/**
|
||||
* All subclasses must provide such a value describing their type signature.
|
||||
*/
|
||||
static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
|
||||
|
||||
private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[5];
|
||||
private static SpeciesData checkCache(int size, String types) {
|
||||
int idx = size - 1;
|
||||
SpeciesData data = SPECIES_DATA_CACHE[idx];
|
||||
if (data != null) return data;
|
||||
SPECIES_DATA_CACHE[idx] = data = getSpeciesData(types);
|
||||
return data;
|
||||
}
|
||||
static SpeciesData speciesData_L() { return checkCache(1, "L"); }
|
||||
static SpeciesData speciesData_LL() { return checkCache(2, "LL"); }
|
||||
static SpeciesData speciesData_LLL() { return checkCache(3, "LLL"); }
|
||||
static SpeciesData speciesData_LLLL() { return checkCache(4, "LLLL"); }
|
||||
static SpeciesData speciesData_LLLLL() { return checkCache(5, "LLLLL"); }
|
||||
}
|
||||
360
jdkSrc/jdk8/java/lang/invoke/CallSite.java
Normal file
360
jdkSrc/jdk8/java/lang/invoke/CallSite.java
Normal file
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, 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 sun.invoke.empty.Empty;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
/**
|
||||
* A {@code CallSite} is a holder for a variable {@link MethodHandle},
|
||||
* which is called its {@code target}.
|
||||
* An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
|
||||
* all calls to the site's current target.
|
||||
* 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}.
|
||||
* <p>
|
||||
* {@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>
|
||||
* <p>
|
||||
* A non-constant call site may be <em>relinked</em> by changing its target.
|
||||
* The new target must have the same {@linkplain MethodHandle#type() type}
|
||||
* as the previous target.
|
||||
* Thus, though a call site can be relinked to a series of
|
||||
* successive targets, it cannot change its type.
|
||||
* <p>
|
||||
* Here is a sample use of call sites and bootstrap methods which links every
|
||||
* dynamic call site to print its arguments:
|
||||
<blockquote><pre>{@code
|
||||
static void test() throws Throwable {
|
||||
// THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
|
||||
InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
|
||||
}
|
||||
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));
|
||||
}
|
||||
private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
|
||||
// ignore caller and name, but match the type:
|
||||
return new ConstantCallSite(printArgs.asType(type));
|
||||
}
|
||||
}</pre></blockquote>
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
abstract
|
||||
public class CallSite {
|
||||
static { MethodHandleImpl.initStatics(); }
|
||||
|
||||
// The actual payload of this call site:
|
||||
/*package-private*/
|
||||
MethodHandle target; // Note: This field is known to the JVM. Do not change.
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* @throws NullPointerException if the proposed type is null
|
||||
*/
|
||||
/*package-private*/
|
||||
CallSite(MethodType type) {
|
||||
target = makeUninitializedCallSite(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a call site object equipped with an initial target method handle.
|
||||
* @param target the method handle which will be the initial target of the call site
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
/*package-private*/
|
||||
CallSite(MethodHandle target) {
|
||||
target.type(); // null check
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* @throws Throwable anything else thrown by the hook function
|
||||
*/
|
||||
/*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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
*/
|
||||
public MethodType type() {
|
||||
// warning: do not call getTarget here, because CCS.getTarget can throw IllegalStateException
|
||||
return target.type();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return the current linkage state of the call site, its target method handle
|
||||
* @see ConstantCallSite
|
||||
* @see VolatileCallSite
|
||||
* @see #setTarget
|
||||
* @see ConstantCallSite#getTarget
|
||||
* @see MutableCallSite#getTarget
|
||||
* @see VolatileCallSite#getTarget
|
||||
*/
|
||||
public abstract MethodHandle getTarget();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* The type of the new target must be {@linkplain MethodType#equals equal to}
|
||||
* the type of the old target.
|
||||
*
|
||||
* @param newTarget the new target
|
||||
* @throws NullPointerException if the proposed new target is null
|
||||
* @throws WrongMethodTypeException if the proposed new target
|
||||
* has a method type that differs from the previous target
|
||||
* @see CallSite#getTarget
|
||||
* @see ConstantCallSite#setTarget
|
||||
* @see MutableCallSite#setTarget
|
||||
* @see VolatileCallSite#setTarget
|
||||
*/
|
||||
public abstract void setTarget(MethodHandle newTarget);
|
||||
|
||||
void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
|
||||
MethodType oldType = oldTarget.type();
|
||||
MethodType newType = newTarget.type(); // null check!
|
||||
if (!newType.equals(oldType))
|
||||
throw wrongTargetType(newTarget, oldType);
|
||||
}
|
||||
|
||||
private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
|
||||
return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a method handle equivalent to an invokedynamic instruction
|
||||
* which has been linked to this call site.
|
||||
* <p>
|
||||
* This method is equivalent to the following code:
|
||||
* <blockquote><pre>{@code
|
||||
* MethodHandle getTarget, invoker, result;
|
||||
* getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
|
||||
* invoker = MethodHandles.exactInvoker(this.type());
|
||||
* result = MethodHandles.foldArguments(invoker, getTarget)
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* @return a method handle which always invokes this call site's current target
|
||||
*/
|
||||
public abstract MethodHandle dynamicInvoker();
|
||||
|
||||
/*non-public*/ MethodHandle makeDynamicInvoker() {
|
||||
MethodHandle getTarget = GET_TARGET.bindArgumentL(0, this);
|
||||
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
|
||||
return MethodHandles.foldArguments(invoker, getTarget);
|
||||
}
|
||||
|
||||
private static final MethodHandle GET_TARGET;
|
||||
private static final MethodHandle THROW_UCS;
|
||||
static {
|
||||
try {
|
||||
GET_TARGET = IMPL_LOOKUP.
|
||||
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
|
||||
THROW_UCS = IMPL_LOOKUP.
|
||||
findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Object.class, Object[].class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw newInternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
|
||||
private static Object uninitializedCallSite(Object... ignore) {
|
||||
throw new IllegalStateException("uninitialized call site");
|
||||
}
|
||||
|
||||
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
|
||||
return invoker.viewAsType(targetType, false);
|
||||
}
|
||||
|
||||
// unsafe stuff:
|
||||
private static final long TARGET_OFFSET;
|
||||
static {
|
||||
try {
|
||||
TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
|
||||
} catch (Exception ex) { throw new Error(ex); }
|
||||
}
|
||||
|
||||
/*package-private*/
|
||||
void setTargetNormal(MethodHandle newTarget) {
|
||||
MethodHandleNatives.setCallSiteTargetNormal(this, newTarget);
|
||||
}
|
||||
/*package-private*/
|
||||
MethodHandle getTargetVolatile() {
|
||||
return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
|
||||
}
|
||||
/*package-private*/
|
||||
void setTargetVolatile(MethodHandle newTarget) {
|
||||
MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
|
||||
}
|
||||
|
||||
// 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:
|
||||
Class<?> callerClass) {
|
||||
MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
|
||||
CallSite site;
|
||||
try {
|
||||
Object binding;
|
||||
info = maybeReBox(info);
|
||||
if (info == null) {
|
||||
binding = bootstrapMethod.invoke(caller, name, type);
|
||||
} else if (!info.getClass().isArray()) {
|
||||
binding = bootstrapMethod.invoke(caller, name, type, info);
|
||||
} else {
|
||||
Object[] argv = (Object[]) info;
|
||||
maybeReBoxElements(argv);
|
||||
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);
|
||||
}
|
||||
}
|
||||
//System.out.println("BSM for "+name+type+" => "+binding);
|
||||
if (binding instanceof CallSite) {
|
||||
site = (CallSite) binding;
|
||||
} else {
|
||||
throw new ClassCastException("bootstrap method failed to produce a CallSite");
|
||||
}
|
||||
if (!site.getTarget().type().equals(type))
|
||||
throw wrongTargetType(site.getTarget(), type);
|
||||
} catch (Throwable ex) {
|
||||
BootstrapMethodError bex;
|
||||
if (ex instanceof BootstrapMethodError)
|
||||
bex = (BootstrapMethodError) ex;
|
||||
else
|
||||
bex = new BootstrapMethodError("call site initialization exception", ex);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
120
jdkSrc/jdk8/java/lang/invoke/ConstantCallSite.java
Normal file
120
jdkSrc/jdk8/java/lang/invoke/ConstantCallSite.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, 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;
|
||||
|
||||
/**
|
||||
* A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed.
|
||||
* An {@code invokedynamic} instruction linked to a {@code ConstantCallSite} is permanently
|
||||
* bound to the call site's target.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class ConstantCallSite extends CallSite {
|
||||
private final boolean isFrozen;
|
||||
|
||||
/**
|
||||
* Creates a call site with a permanent target.
|
||||
* @param target the target to be permanently associated with this call site
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public ConstantCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
isFrozen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a call site with a permanent target, possibly bound to the call site itself.
|
||||
* <p>
|
||||
* During construction of the call site, the {@code createTargetHook} is invoked to
|
||||
* produce the actual target, as if by a call of the form
|
||||
* {@code (MethodHandle) createTargetHook.invoke(this)}.
|
||||
* <p>
|
||||
* Note that user code cannot perform such an action directly in a subclass constructor,
|
||||
* since the target must be fixed before the {@code ConstantCallSite} constructor returns.
|
||||
* <p>
|
||||
* The hook is said to bind the call site to a target method handle,
|
||||
* and a typical action would be {@code someTarget.bindTo(this)}.
|
||||
* However, the hook is free to take any action whatever,
|
||||
* including ignoring the call site and returning a constant target.
|
||||
* <p>
|
||||
* The result returned by the hook must be a method handle of exactly
|
||||
* the same type as the call site.
|
||||
* <p>
|
||||
* While the hook is being called, the new {@code ConstantCallSite}
|
||||
* object is in a partially constructed state.
|
||||
* In this state,
|
||||
* a call to {@code getTarget}, or any other attempt to use the target,
|
||||
* will result in an {@code IllegalStateException}.
|
||||
* It is legal at all times to obtain the call site's type using the {@code type} method.
|
||||
*
|
||||
* @param targetType the type of the method handle to be permanently associated with this call site
|
||||
* @param createTargetHook a method handle to invoke (on the call site) to produce the call site's target
|
||||
* @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}
|
||||
* @throws Throwable anything else thrown by the hook function
|
||||
*/
|
||||
protected ConstantCallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
|
||||
super(targetType, createTargetHook);
|
||||
isFrozen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target method of the call site, which behaves
|
||||
* like a {@code final} field of the {@code ConstantCallSite}.
|
||||
* That is, the target is always the original value passed
|
||||
* to the constructor call which created this instance.
|
||||
*
|
||||
* @return the immutable linkage state of this call site, a constant method handle
|
||||
* @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
|
||||
*/
|
||||
@Override public final MethodHandle getTarget() {
|
||||
if (!isFrozen) throw new IllegalStateException();
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always throws an {@link UnsupportedOperationException}.
|
||||
* This kind of call site cannot change its target.
|
||||
* @param ignore a new target proposed for the call site, which is ignored
|
||||
* @throws UnsupportedOperationException because this kind of call site cannot change its target
|
||||
*/
|
||||
@Override public final void setTarget(MethodHandle ignore) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this call site's permanent target.
|
||||
* Since that target will never change, this is a correct implementation
|
||||
* of {@link CallSite#dynamicInvoker CallSite.dynamicInvoker}.
|
||||
* @return the immutable linkage state of this call site, a constant method handle
|
||||
* @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
|
||||
*/
|
||||
@Override
|
||||
public final MethodHandle dynamicInvoker() {
|
||||
return getTarget();
|
||||
}
|
||||
}
|
||||
164
jdkSrc/jdk8/java/lang/invoke/DelegatingMethodHandle.java
Normal file
164
jdkSrc/jdk8/java/lang/invoke/DelegatingMethodHandle.java
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.util.Arrays;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* A method handle whose invocation behavior is determined by a target.
|
||||
* The delegating MH itself can hold extra "intentions" beyond the simple behavior.
|
||||
* @author jrose
|
||||
*/
|
||||
/*non-public*/
|
||||
abstract class DelegatingMethodHandle extends MethodHandle {
|
||||
protected DelegatingMethodHandle(MethodHandle target) {
|
||||
this(target.type(), target);
|
||||
}
|
||||
|
||||
protected DelegatingMethodHandle(MethodType type, MethodHandle target) {
|
||||
super(type, chooseDelegatingForm(target));
|
||||
}
|
||||
|
||||
protected DelegatingMethodHandle(MethodType type, LambdaForm form) {
|
||||
super(type, form);
|
||||
}
|
||||
|
||||
/** Define this to extract the delegated target which supplies the invocation behavior. */
|
||||
abstract protected MethodHandle getTarget();
|
||||
|
||||
@Override
|
||||
abstract MethodHandle asTypeUncached(MethodType newType);
|
||||
|
||||
@Override
|
||||
MemberName internalMemberName() {
|
||||
return getTarget().internalMemberName();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isInvokeSpecial() {
|
||||
return getTarget().isInvokeSpecial();
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> internalCallerClass() {
|
||||
return getTarget().internalCallerClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
// FIXME: rethink 'copyWith' protocol; it is too low-level for use on all MHs
|
||||
throw newIllegalArgumentException("do not use this");
|
||||
}
|
||||
|
||||
@Override
|
||||
String internalProperties() {
|
||||
return "\n& Class="+getClass().getSimpleName()+
|
||||
"\n& Target="+getTarget().debugString();
|
||||
}
|
||||
|
||||
@Override
|
||||
BoundMethodHandle rebind() {
|
||||
return getTarget().rebind();
|
||||
}
|
||||
|
||||
private static LambdaForm chooseDelegatingForm(MethodHandle target) {
|
||||
if (target instanceof SimpleMethodHandle)
|
||||
return target.internalForm(); // no need for an indirection
|
||||
return makeReinvokerForm(target, MethodTypeForm.LF_DELEGATE, DelegatingMethodHandle.class, NF_getTarget);
|
||||
}
|
||||
|
||||
static LambdaForm makeReinvokerForm(MethodHandle target,
|
||||
int whichCache,
|
||||
Object constraint,
|
||||
NamedFunction getTargetFn) {
|
||||
String debugString;
|
||||
switch(whichCache) {
|
||||
case MethodTypeForm.LF_REBIND: debugString = "BMH.reinvoke"; break;
|
||||
case MethodTypeForm.LF_DELEGATE: debugString = "MH.delegate"; break;
|
||||
default: debugString = "MH.reinvoke"; break;
|
||||
}
|
||||
// No pre-action needed.
|
||||
return makeReinvokerForm(target, whichCache, constraint, debugString, true, getTargetFn, null);
|
||||
}
|
||||
/** Create a LF which simply reinvokes a target of the given basic type. */
|
||||
static LambdaForm makeReinvokerForm(MethodHandle target,
|
||||
int whichCache,
|
||||
Object constraint,
|
||||
String debugString,
|
||||
boolean forceInline,
|
||||
NamedFunction getTargetFn,
|
||||
NamedFunction preActionFn) {
|
||||
MethodType mtype = target.type().basicType();
|
||||
boolean customized = (whichCache < 0 ||
|
||||
mtype.parameterSlotCount() > MethodType.MAX_MH_INVOKER_ARITY);
|
||||
boolean hasPreAction = (preActionFn != null);
|
||||
LambdaForm form;
|
||||
if (!customized) {
|
||||
form = mtype.form().cachedLambdaForm(whichCache);
|
||||
if (form != null) return form;
|
||||
}
|
||||
final int THIS_DMH = 0;
|
||||
final int ARG_BASE = 1;
|
||||
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
|
||||
int nameCursor = ARG_LIMIT;
|
||||
final int PRE_ACTION = hasPreAction ? nameCursor++ : -1;
|
||||
final int NEXT_MH = customized ? -1 : nameCursor++;
|
||||
final int REINVOKE = nameCursor++;
|
||||
LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
|
||||
assert(names.length == nameCursor);
|
||||
names[THIS_DMH] = names[THIS_DMH].withConstraint(constraint);
|
||||
Object[] targetArgs;
|
||||
if (hasPreAction) {
|
||||
names[PRE_ACTION] = new LambdaForm.Name(preActionFn, names[THIS_DMH]);
|
||||
}
|
||||
if (customized) {
|
||||
targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class);
|
||||
names[REINVOKE] = new LambdaForm.Name(target, targetArgs); // the invoker is the target itself
|
||||
} else {
|
||||
names[NEXT_MH] = new LambdaForm.Name(getTargetFn, names[THIS_DMH]);
|
||||
targetArgs = Arrays.copyOfRange(names, THIS_DMH, ARG_LIMIT, Object[].class);
|
||||
targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
|
||||
names[REINVOKE] = new LambdaForm.Name(mtype, targetArgs);
|
||||
}
|
||||
form = new LambdaForm(debugString, ARG_LIMIT, names, forceInline);
|
||||
if (!customized) {
|
||||
form = mtype.form().setCachedLambdaForm(whichCache, form);
|
||||
}
|
||||
return form;
|
||||
}
|
||||
|
||||
static final NamedFunction NF_getTarget;
|
||||
static {
|
||||
try {
|
||||
NF_getTarget = new NamedFunction(DelegatingMethodHandle.class
|
||||
.getDeclaredMethod("getTarget"));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
734
jdkSrc/jdk8/java/lang/invoke/DirectMethodHandle.java
Normal file
734
jdkSrc/jdk8/java/lang/invoke/DirectMethodHandle.java
Normal file
@@ -0,0 +1,734 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2017, 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 sun.misc.Unsafe;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import sun.invoke.util.VerifyAccess;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.MethodTypeForm.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
import sun.invoke.util.ValueConversions;
|
||||
import sun.invoke.util.VerifyType;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
/**
|
||||
* The flavor of method handle which implements a constant reference
|
||||
* to a class member.
|
||||
* @author jrose
|
||||
*/
|
||||
class DirectMethodHandle extends MethodHandle {
|
||||
final MemberName member;
|
||||
|
||||
// Constructors and factory methods in this class *must* be package scoped or private.
|
||||
private DirectMethodHandle(MethodType mtype, LambdaForm form, MemberName member) {
|
||||
super(mtype, form);
|
||||
if (!member.isResolved()) throw new InternalError();
|
||||
|
||||
if (member.getDeclaringClass().isInterface() &&
|
||||
member.isMethod() && !member.isAbstract()) {
|
||||
// Check for corner case: invokeinterface of Object method
|
||||
MemberName m = new MemberName(Object.class, member.getName(), member.getMethodType(), member.getReferenceKind());
|
||||
m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null);
|
||||
if (m != null && m.isPublic()) {
|
||||
assert(member.getReferenceKind() == m.getReferenceKind()); // else this.form is wrong
|
||||
member = m;
|
||||
}
|
||||
}
|
||||
|
||||
this.member = member;
|
||||
}
|
||||
|
||||
// Factory methods:
|
||||
static DirectMethodHandle make(byte refKind, Class<?> receiver, MemberName member) {
|
||||
MethodType mtype = member.getMethodOrFieldType();
|
||||
if (!member.isStatic()) {
|
||||
if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
|
||||
throw new InternalError(member.toString());
|
||||
mtype = mtype.insertParameterTypes(0, receiver);
|
||||
}
|
||||
if (!member.isField()) {
|
||||
switch (refKind) {
|
||||
case REF_invokeSpecial: {
|
||||
member = member.asSpecial();
|
||||
LambdaForm lform = preparedLambdaForm(member);
|
||||
return new Special(mtype, lform, member);
|
||||
}
|
||||
case REF_invokeInterface: {
|
||||
LambdaForm lform = preparedLambdaForm(member);
|
||||
return new Interface(mtype, lform, member, receiver);
|
||||
}
|
||||
default: {
|
||||
LambdaForm lform = preparedLambdaForm(member);
|
||||
return new DirectMethodHandle(mtype, lform, member);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LambdaForm lform = preparedFieldLambdaForm(member);
|
||||
if (member.isStatic()) {
|
||||
long offset = MethodHandleNatives.staticFieldOffset(member);
|
||||
Object base = MethodHandleNatives.staticFieldBase(member);
|
||||
return new StaticAccessor(mtype, lform, member, base, offset);
|
||||
} else {
|
||||
long offset = MethodHandleNatives.objectFieldOffset(member);
|
||||
assert(offset == (int)offset);
|
||||
return new Accessor(mtype, lform, member, (int)offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
static DirectMethodHandle make(Class<?> receiver, MemberName member) {
|
||||
byte refKind = member.getReferenceKind();
|
||||
if (refKind == REF_invokeSpecial)
|
||||
refKind = REF_invokeVirtual;
|
||||
return make(refKind, receiver, member);
|
||||
}
|
||||
static DirectMethodHandle make(MemberName member) {
|
||||
if (member.isConstructor())
|
||||
return makeAllocator(member);
|
||||
return make(member.getDeclaringClass(), member);
|
||||
}
|
||||
static DirectMethodHandle make(Method method) {
|
||||
return make(method.getDeclaringClass(), new MemberName(method));
|
||||
}
|
||||
static DirectMethodHandle make(Field field) {
|
||||
return make(field.getDeclaringClass(), new MemberName(field));
|
||||
}
|
||||
private static DirectMethodHandle makeAllocator(MemberName ctor) {
|
||||
assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
|
||||
Class<?> instanceClass = ctor.getDeclaringClass();
|
||||
ctor = ctor.asConstructor();
|
||||
assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
|
||||
MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
|
||||
LambdaForm lform = preparedLambdaForm(ctor);
|
||||
MemberName init = ctor.asSpecial();
|
||||
assert(init.getMethodType().returnType() == void.class);
|
||||
return new Constructor(mtype, lform, ctor, init, instanceClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
BoundMethodHandle rebind() {
|
||||
return BoundMethodHandle.makeReinvoker(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
assert(this.getClass() == DirectMethodHandle.class); // must override in subclasses
|
||||
return new DirectMethodHandle(mt, lf, member);
|
||||
}
|
||||
|
||||
@Override
|
||||
String internalProperties() {
|
||||
return "\n& DMH.MN="+internalMemberName();
|
||||
}
|
||||
|
||||
//// Implementation methods.
|
||||
@Override
|
||||
@ForceInline
|
||||
MemberName internalMemberName() {
|
||||
return member;
|
||||
}
|
||||
|
||||
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
|
||||
|
||||
/**
|
||||
* Create a LF which can invoke the given method.
|
||||
* Cache and share this structure among all methods with
|
||||
* the same basicType and refKind.
|
||||
*/
|
||||
private static LambdaForm preparedLambdaForm(MemberName m) {
|
||||
assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
|
||||
MethodType mtype = m.getInvocationType().basicType();
|
||||
assert(!m.isMethodHandleInvoke()) : m;
|
||||
int which;
|
||||
switch (m.getReferenceKind()) {
|
||||
case REF_invokeVirtual: which = LF_INVVIRTUAL; break;
|
||||
case REF_invokeStatic: which = LF_INVSTATIC; break;
|
||||
case REF_invokeSpecial: which = LF_INVSPECIAL; break;
|
||||
case REF_invokeInterface: which = LF_INVINTERFACE; break;
|
||||
case REF_newInvokeSpecial: which = LF_NEWINVSPECIAL; break;
|
||||
default: throw new InternalError(m.toString());
|
||||
}
|
||||
if (which == LF_INVSTATIC && shouldBeInitialized(m)) {
|
||||
// precompute the barrier-free version:
|
||||
preparedLambdaForm(mtype, which);
|
||||
which = LF_INVSTATIC_INIT;
|
||||
}
|
||||
LambdaForm lform = preparedLambdaForm(mtype, which);
|
||||
maybeCompile(lform, m);
|
||||
assert(lform.methodType().dropParameterTypes(0, 1)
|
||||
.equals(m.getInvocationType().basicType()))
|
||||
: Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
|
||||
return lform;
|
||||
}
|
||||
|
||||
private static LambdaForm preparedLambdaForm(MethodType mtype, int which) {
|
||||
LambdaForm lform = mtype.form().cachedLambdaForm(which);
|
||||
if (lform != null) return lform;
|
||||
lform = makePreparedLambdaForm(mtype, which);
|
||||
return mtype.form().setCachedLambdaForm(which, lform);
|
||||
}
|
||||
|
||||
private static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
|
||||
boolean needsInit = (which == LF_INVSTATIC_INIT);
|
||||
boolean doesAlloc = (which == LF_NEWINVSPECIAL);
|
||||
boolean needsReceiverCheck = (which == LF_INVINTERFACE);
|
||||
String linkerName, lambdaName;
|
||||
switch (which) {
|
||||
case LF_INVVIRTUAL: linkerName = "linkToVirtual"; lambdaName = "DMH.invokeVirtual"; break;
|
||||
case LF_INVSTATIC: linkerName = "linkToStatic"; lambdaName = "DMH.invokeStatic"; break;
|
||||
case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; lambdaName = "DMH.invokeStaticInit"; break;
|
||||
case LF_INVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.invokeSpecial"; break;
|
||||
case LF_INVINTERFACE: linkerName = "linkToInterface"; lambdaName = "DMH.invokeInterface"; break;
|
||||
case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.newInvokeSpecial"; break;
|
||||
default: throw new InternalError("which="+which);
|
||||
}
|
||||
MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class);
|
||||
if (doesAlloc)
|
||||
mtypeWithArg = mtypeWithArg
|
||||
.insertParameterTypes(0, Object.class) // insert newly allocated obj
|
||||
.changeReturnType(void.class); // <init> returns void
|
||||
MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic);
|
||||
try {
|
||||
linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
final int DMH_THIS = 0;
|
||||
final int ARG_BASE = 1;
|
||||
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
|
||||
int nameCursor = ARG_LIMIT;
|
||||
final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1);
|
||||
final int GET_MEMBER = nameCursor++;
|
||||
final int CHECK_RECEIVER = (needsReceiverCheck ? nameCursor++ : -1);
|
||||
final int LINKER_CALL = nameCursor++;
|
||||
Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
|
||||
assert(names.length == nameCursor);
|
||||
if (doesAlloc) {
|
||||
// names = { argx,y,z,... new C, init method }
|
||||
names[NEW_OBJ] = new Name(Lazy.NF_allocateInstance, names[DMH_THIS]);
|
||||
names[GET_MEMBER] = new Name(Lazy.NF_constructorMethod, names[DMH_THIS]);
|
||||
} else if (needsInit) {
|
||||
names[GET_MEMBER] = new Name(Lazy.NF_internalMemberNameEnsureInit, names[DMH_THIS]);
|
||||
} else {
|
||||
names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]);
|
||||
}
|
||||
assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
|
||||
Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
|
||||
if (needsReceiverCheck) {
|
||||
names[CHECK_RECEIVER] = new Name(Lazy.NF_checkReceiver, names[DMH_THIS], names[ARG_BASE]);
|
||||
outArgs[0] = names[CHECK_RECEIVER];
|
||||
}
|
||||
assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args!
|
||||
int result = LAST_RESULT;
|
||||
if (doesAlloc) {
|
||||
assert(outArgs[outArgs.length-2] == names[NEW_OBJ]); // got to move this one
|
||||
System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2);
|
||||
outArgs[0] = names[NEW_OBJ];
|
||||
result = NEW_OBJ;
|
||||
}
|
||||
names[LINKER_CALL] = new Name(linker, outArgs);
|
||||
lambdaName += "_" + shortenSignature(basicTypeSignature(mtype));
|
||||
LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
|
||||
// This is a tricky bit of code. Don't send it through the LF interpreter.
|
||||
lform.compileToBytecode();
|
||||
return lform;
|
||||
}
|
||||
|
||||
static Object findDirectMethodHandle(Name name) {
|
||||
if (name.function == Lazy.NF_internalMemberName ||
|
||||
name.function == Lazy.NF_internalMemberNameEnsureInit ||
|
||||
name.function == Lazy.NF_constructorMethod) {
|
||||
assert(name.arguments.length == 1);
|
||||
return name.arguments[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void maybeCompile(LambdaForm lform, MemberName m) {
|
||||
if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
|
||||
// Help along bootstrapping...
|
||||
lform.compileToBytecode();
|
||||
}
|
||||
|
||||
/** Static wrapper for DirectMethodHandle.internalMemberName. */
|
||||
@ForceInline
|
||||
/*non-public*/ static Object internalMemberName(Object mh) {
|
||||
return ((DirectMethodHandle)mh).member;
|
||||
}
|
||||
|
||||
/** Static wrapper for DirectMethodHandle.internalMemberName.
|
||||
* This one also forces initialization.
|
||||
*/
|
||||
/*non-public*/ static Object internalMemberNameEnsureInit(Object mh) {
|
||||
DirectMethodHandle dmh = (DirectMethodHandle)mh;
|
||||
dmh.ensureInitialized();
|
||||
return dmh.member;
|
||||
}
|
||||
|
||||
/*non-public*/ static
|
||||
boolean shouldBeInitialized(MemberName member) {
|
||||
switch (member.getReferenceKind()) {
|
||||
case REF_invokeStatic:
|
||||
case REF_getStatic:
|
||||
case REF_putStatic:
|
||||
case REF_newInvokeSpecial:
|
||||
break;
|
||||
default:
|
||||
// No need to initialize the class on this kind of member.
|
||||
return false;
|
||||
}
|
||||
Class<?> cls = member.getDeclaringClass();
|
||||
if (cls == ValueConversions.class ||
|
||||
cls == MethodHandleImpl.class ||
|
||||
cls == Invokers.class) {
|
||||
// These guys have lots of <clinit> DMH creation but we know
|
||||
// the MHs will not be used until the system is booted.
|
||||
return false;
|
||||
}
|
||||
if (VerifyAccess.isSamePackage(MethodHandle.class, cls) ||
|
||||
VerifyAccess.isSamePackage(ValueConversions.class, cls)) {
|
||||
// It is a system class. It is probably in the process of
|
||||
// being initialized, but we will help it along just to be safe.
|
||||
if (UNSAFE.shouldBeInitialized(cls)) {
|
||||
UNSAFE.ensureClassInitialized(cls);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return UNSAFE.shouldBeInitialized(cls);
|
||||
}
|
||||
|
||||
private static class EnsureInitialized extends ClassValue<WeakReference<Thread>> {
|
||||
@Override
|
||||
protected WeakReference<Thread> computeValue(Class<?> type) {
|
||||
UNSAFE.ensureClassInitialized(type);
|
||||
if (UNSAFE.shouldBeInitialized(type))
|
||||
// If the previous call didn't block, this can happen.
|
||||
// We are executing inside <clinit>.
|
||||
return new WeakReference<>(Thread.currentThread());
|
||||
return null;
|
||||
}
|
||||
static final EnsureInitialized INSTANCE = new EnsureInitialized();
|
||||
}
|
||||
|
||||
private void ensureInitialized() {
|
||||
if (checkInitialized(member)) {
|
||||
// The coast is clear. Delete the <clinit> barrier.
|
||||
if (member.isField())
|
||||
updateForm(preparedFieldLambdaForm(member));
|
||||
else
|
||||
updateForm(preparedLambdaForm(member));
|
||||
}
|
||||
}
|
||||
private static boolean checkInitialized(MemberName member) {
|
||||
Class<?> defc = member.getDeclaringClass();
|
||||
WeakReference<Thread> ref = EnsureInitialized.INSTANCE.get(defc);
|
||||
if (ref == null) {
|
||||
return true; // the final state
|
||||
}
|
||||
Thread clinitThread = ref.get();
|
||||
// Somebody may still be running defc.<clinit>.
|
||||
if (clinitThread == Thread.currentThread()) {
|
||||
// If anybody is running defc.<clinit>, it is this thread.
|
||||
if (UNSAFE.shouldBeInitialized(defc))
|
||||
// Yes, we are running it; keep the barrier for now.
|
||||
return false;
|
||||
} else {
|
||||
// We are in a random thread. Block.
|
||||
UNSAFE.ensureClassInitialized(defc);
|
||||
}
|
||||
assert(!UNSAFE.shouldBeInitialized(defc));
|
||||
// put it into the final state
|
||||
EnsureInitialized.INSTANCE.remove(defc);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*non-public*/ static void ensureInitialized(Object mh) {
|
||||
((DirectMethodHandle)mh).ensureInitialized();
|
||||
}
|
||||
|
||||
/** This subclass represents invokespecial instructions. */
|
||||
static class Special extends DirectMethodHandle {
|
||||
private Special(MethodType mtype, LambdaForm form, MemberName member) {
|
||||
super(mtype, form, member);
|
||||
}
|
||||
@Override
|
||||
boolean isInvokeSpecial() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new Special(mt, lf, member);
|
||||
}
|
||||
}
|
||||
|
||||
/** This subclass represents invokeinterface instructions. */
|
||||
static class Interface extends DirectMethodHandle {
|
||||
private final Class<?> refc;
|
||||
private Interface(MethodType mtype, LambdaForm form, MemberName member, Class<?> refc) {
|
||||
super(mtype, form, member);
|
||||
assert refc.isInterface() : refc;
|
||||
this.refc = refc;
|
||||
}
|
||||
@Override
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new Interface(mt, lf, member, refc);
|
||||
}
|
||||
|
||||
Object checkReceiver(Object recv) {
|
||||
if (!refc.isInstance(recv)) {
|
||||
String msg = String.format("Class %s does not implement the requested interface %s",
|
||||
recv.getClass().getName(), refc.getName());
|
||||
throw new IncompatibleClassChangeError(msg);
|
||||
}
|
||||
return recv;
|
||||
}
|
||||
}
|
||||
|
||||
/** This subclass handles constructor references. */
|
||||
static class Constructor extends DirectMethodHandle {
|
||||
final MemberName initMethod;
|
||||
final Class<?> instanceClass;
|
||||
|
||||
private Constructor(MethodType mtype, LambdaForm form, MemberName constructor,
|
||||
MemberName initMethod, Class<?> instanceClass) {
|
||||
super(mtype, form, constructor);
|
||||
this.initMethod = initMethod;
|
||||
this.instanceClass = instanceClass;
|
||||
assert(initMethod.isResolved());
|
||||
}
|
||||
@Override
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new Constructor(mt, lf, member, initMethod, instanceClass);
|
||||
}
|
||||
}
|
||||
|
||||
/*non-public*/ static Object constructorMethod(Object mh) {
|
||||
Constructor dmh = (Constructor)mh;
|
||||
return dmh.initMethod;
|
||||
}
|
||||
|
||||
/*non-public*/ static Object allocateInstance(Object mh) throws InstantiationException {
|
||||
Constructor dmh = (Constructor)mh;
|
||||
return UNSAFE.allocateInstance(dmh.instanceClass);
|
||||
}
|
||||
|
||||
/** This subclass handles non-static field references. */
|
||||
static class Accessor extends DirectMethodHandle {
|
||||
final Class<?> fieldType;
|
||||
final int fieldOffset;
|
||||
private Accessor(MethodType mtype, LambdaForm form, MemberName member,
|
||||
int fieldOffset) {
|
||||
super(mtype, form, member);
|
||||
this.fieldType = member.getFieldType();
|
||||
this.fieldOffset = fieldOffset;
|
||||
}
|
||||
|
||||
@Override Object checkCast(Object obj) {
|
||||
return fieldType.cast(obj);
|
||||
}
|
||||
@Override
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new Accessor(mt, lf, member, fieldOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
/*non-public*/ static long fieldOffset(Object accessorObj) {
|
||||
// Note: We return a long because that is what Unsafe.getObject likes.
|
||||
// We store a plain int because it is more compact.
|
||||
return ((Accessor)accessorObj).fieldOffset;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
/*non-public*/ static Object checkBase(Object obj) {
|
||||
// Note that the object's class has already been verified,
|
||||
// since the parameter type of the Accessor method handle
|
||||
// is either member.getDeclaringClass or a subclass.
|
||||
// This was verified in DirectMethodHandle.make.
|
||||
// Therefore, the only remaining check is for null.
|
||||
// Since this check is *not* guaranteed by Unsafe.getInt
|
||||
// and its siblings, we need to make an explicit one here.
|
||||
obj.getClass(); // maybe throw NPE
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** This subclass handles static field references. */
|
||||
static class StaticAccessor extends DirectMethodHandle {
|
||||
final private Class<?> fieldType;
|
||||
final private Object staticBase;
|
||||
final private long staticOffset;
|
||||
|
||||
private StaticAccessor(MethodType mtype, LambdaForm form, MemberName member,
|
||||
Object staticBase, long staticOffset) {
|
||||
super(mtype, form, member);
|
||||
this.fieldType = member.getFieldType();
|
||||
this.staticBase = staticBase;
|
||||
this.staticOffset = staticOffset;
|
||||
}
|
||||
|
||||
@Override Object checkCast(Object obj) {
|
||||
return fieldType.cast(obj);
|
||||
}
|
||||
@Override
|
||||
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return new StaticAccessor(mt, lf, member, staticBase, staticOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
/*non-public*/ static Object nullCheck(Object obj) {
|
||||
obj.getClass();
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
/*non-public*/ static Object staticBase(Object accessorObj) {
|
||||
return ((StaticAccessor)accessorObj).staticBase;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
/*non-public*/ static long staticOffset(Object accessorObj) {
|
||||
return ((StaticAccessor)accessorObj).staticOffset;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
/*non-public*/ static Object checkCast(Object mh, Object obj) {
|
||||
return ((DirectMethodHandle) mh).checkCast(obj);
|
||||
}
|
||||
|
||||
Object checkCast(Object obj) {
|
||||
return member.getReturnType().cast(obj);
|
||||
}
|
||||
|
||||
// Caching machinery for field accessors:
|
||||
private static byte
|
||||
AF_GETFIELD = 0,
|
||||
AF_PUTFIELD = 1,
|
||||
AF_GETSTATIC = 2,
|
||||
AF_PUTSTATIC = 3,
|
||||
AF_GETSTATIC_INIT = 4,
|
||||
AF_PUTSTATIC_INIT = 5,
|
||||
AF_LIMIT = 6;
|
||||
// Enumerate the different field kinds using Wrapper,
|
||||
// with an extra case added for checked references.
|
||||
private static int
|
||||
FT_LAST_WRAPPER = Wrapper.values().length-1,
|
||||
FT_UNCHECKED_REF = Wrapper.OBJECT.ordinal(),
|
||||
FT_CHECKED_REF = FT_LAST_WRAPPER+1,
|
||||
FT_LIMIT = FT_LAST_WRAPPER+2;
|
||||
private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) {
|
||||
return ((formOp * FT_LIMIT * 2)
|
||||
+ (isVolatile ? FT_LIMIT : 0)
|
||||
+ ftypeKind);
|
||||
}
|
||||
private static final LambdaForm[] ACCESSOR_FORMS
|
||||
= new LambdaForm[afIndex(AF_LIMIT, false, 0)];
|
||||
private static int ftypeKind(Class<?> ftype) {
|
||||
if (ftype.isPrimitive())
|
||||
return Wrapper.forPrimitiveType(ftype).ordinal();
|
||||
else if (VerifyType.isNullReferenceConversion(Object.class, ftype))
|
||||
return FT_UNCHECKED_REF;
|
||||
else
|
||||
return FT_CHECKED_REF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LF which can access the given field.
|
||||
* Cache and share this structure among all fields with
|
||||
* the same basicType and refKind.
|
||||
*/
|
||||
private static LambdaForm preparedFieldLambdaForm(MemberName m) {
|
||||
Class<?> ftype = m.getFieldType();
|
||||
boolean isVolatile = m.isVolatile();
|
||||
byte formOp;
|
||||
switch (m.getReferenceKind()) {
|
||||
case REF_getField: formOp = AF_GETFIELD; break;
|
||||
case REF_putField: formOp = AF_PUTFIELD; break;
|
||||
case REF_getStatic: formOp = AF_GETSTATIC; break;
|
||||
case REF_putStatic: formOp = AF_PUTSTATIC; break;
|
||||
default: throw new InternalError(m.toString());
|
||||
}
|
||||
if (shouldBeInitialized(m)) {
|
||||
// precompute the barrier-free version:
|
||||
preparedFieldLambdaForm(formOp, isVolatile, ftype);
|
||||
assert((AF_GETSTATIC_INIT - AF_GETSTATIC) ==
|
||||
(AF_PUTSTATIC_INIT - AF_PUTSTATIC));
|
||||
formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC);
|
||||
}
|
||||
LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype);
|
||||
maybeCompile(lform, m);
|
||||
assert(lform.methodType().dropParameterTypes(0, 1)
|
||||
.equals(m.getInvocationType().basicType()))
|
||||
: Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
|
||||
return lform;
|
||||
}
|
||||
private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, Class<?> ftype) {
|
||||
int afIndex = afIndex(formOp, isVolatile, ftypeKind(ftype));
|
||||
LambdaForm lform = ACCESSOR_FORMS[afIndex];
|
||||
if (lform != null) return lform;
|
||||
lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind(ftype));
|
||||
ACCESSOR_FORMS[afIndex] = lform; // don't bother with a CAS
|
||||
return lform;
|
||||
}
|
||||
|
||||
private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
|
||||
boolean isGetter = (formOp & 1) == (AF_GETFIELD & 1);
|
||||
boolean isStatic = (formOp >= AF_GETSTATIC);
|
||||
boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
|
||||
boolean needsCast = (ftypeKind == FT_CHECKED_REF);
|
||||
Wrapper fw = (needsCast ? Wrapper.OBJECT : Wrapper.values()[ftypeKind]);
|
||||
Class<?> ft = fw.primitiveType();
|
||||
assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
|
||||
String tname = fw.primitiveSimpleName();
|
||||
String ctname = Character.toUpperCase(tname.charAt(0)) + tname.substring(1);
|
||||
if (isVolatile) ctname += "Volatile";
|
||||
String getOrPut = (isGetter ? "get" : "put");
|
||||
String linkerName = (getOrPut + ctname); // getObject, putIntVolatile, etc.
|
||||
MethodType linkerType;
|
||||
if (isGetter)
|
||||
linkerType = MethodType.methodType(ft, Object.class, long.class);
|
||||
else
|
||||
linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
|
||||
MemberName linker = new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual);
|
||||
try {
|
||||
linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
|
||||
// What is the external type of the lambda form?
|
||||
MethodType mtype;
|
||||
if (isGetter)
|
||||
mtype = MethodType.methodType(ft);
|
||||
else
|
||||
mtype = MethodType.methodType(void.class, ft);
|
||||
mtype = mtype.basicType(); // erase short to int, etc.
|
||||
if (!isStatic)
|
||||
mtype = mtype.insertParameterTypes(0, Object.class);
|
||||
final int DMH_THIS = 0;
|
||||
final int ARG_BASE = 1;
|
||||
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
|
||||
// if this is for non-static access, the base pointer is stored at this index:
|
||||
final int OBJ_BASE = isStatic ? -1 : ARG_BASE;
|
||||
// if this is for write access, the value to be written is stored at this index:
|
||||
final int SET_VALUE = isGetter ? -1 : ARG_LIMIT - 1;
|
||||
int nameCursor = ARG_LIMIT;
|
||||
final int F_HOLDER = (isStatic ? nameCursor++ : -1); // static base if any
|
||||
final int F_OFFSET = nameCursor++; // Either static offset or field offset.
|
||||
final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
|
||||
final int INIT_BAR = (needsInit ? nameCursor++ : -1);
|
||||
final int PRE_CAST = (needsCast && !isGetter ? nameCursor++ : -1);
|
||||
final int LINKER_CALL = nameCursor++;
|
||||
final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1);
|
||||
final int RESULT = nameCursor-1; // either the call or the cast
|
||||
Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
|
||||
if (needsInit)
|
||||
names[INIT_BAR] = new Name(Lazy.NF_ensureInitialized, names[DMH_THIS]);
|
||||
if (needsCast && !isGetter)
|
||||
names[PRE_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
|
||||
Object[] outArgs = new Object[1 + linkerType.parameterCount()];
|
||||
assert(outArgs.length == (isGetter ? 3 : 4));
|
||||
outArgs[0] = UNSAFE;
|
||||
if (isStatic) {
|
||||
outArgs[1] = names[F_HOLDER] = new Name(Lazy.NF_staticBase, names[DMH_THIS]);
|
||||
outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_staticOffset, names[DMH_THIS]);
|
||||
} else {
|
||||
outArgs[1] = names[OBJ_CHECK] = new Name(Lazy.NF_checkBase, names[OBJ_BASE]);
|
||||
outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_fieldOffset, names[DMH_THIS]);
|
||||
}
|
||||
if (!isGetter) {
|
||||
outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
|
||||
}
|
||||
for (Object a : outArgs) assert(a != null);
|
||||
names[LINKER_CALL] = new Name(linker, outArgs);
|
||||
if (needsCast && isGetter)
|
||||
names[POST_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
|
||||
for (Name n : names) assert(n != null);
|
||||
String fieldOrStatic = (isStatic ? "Static" : "Field");
|
||||
String lambdaName = (linkerName + fieldOrStatic); // significant only for debugging
|
||||
if (needsCast) lambdaName += "Cast";
|
||||
if (needsInit) lambdaName += "Init";
|
||||
return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-initialized NamedFunctions for bootstrapping purposes.
|
||||
* Factored in an inner class to delay initialization until first usage.
|
||||
*/
|
||||
private static class Lazy {
|
||||
static final NamedFunction
|
||||
NF_internalMemberName,
|
||||
NF_internalMemberNameEnsureInit,
|
||||
NF_ensureInitialized,
|
||||
NF_fieldOffset,
|
||||
NF_checkBase,
|
||||
NF_staticBase,
|
||||
NF_staticOffset,
|
||||
NF_checkCast,
|
||||
NF_allocateInstance,
|
||||
NF_constructorMethod,
|
||||
NF_checkReceiver;
|
||||
static {
|
||||
try {
|
||||
NamedFunction nfs[] = {
|
||||
NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("internalMemberName", Object.class)),
|
||||
NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
|
||||
NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("ensureInitialized", Object.class)),
|
||||
NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("fieldOffset", Object.class)),
|
||||
NF_checkBase = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("checkBase", Object.class)),
|
||||
NF_staticBase = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("staticBase", Object.class)),
|
||||
NF_staticOffset = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("staticOffset", Object.class)),
|
||||
NF_checkCast = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("checkCast", Object.class, Object.class)),
|
||||
NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("allocateInstance", Object.class)),
|
||||
NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
|
||||
.getDeclaredMethod("constructorMethod", Object.class)),
|
||||
NF_checkReceiver = new NamedFunction(new MemberName(Interface.class
|
||||
.getDeclaredMethod("checkReceiver", Object.class)))
|
||||
};
|
||||
for (NamedFunction nf : nfs) {
|
||||
// Each nf must be statically invocable or we get tied up in our bootstraps.
|
||||
assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
|
||||
nf.resolve();
|
||||
}
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
jdkSrc/jdk8/java/lang/invoke/DontInline.java
Normal file
37
jdkSrc/jdk8/java/lang/invoke/DontInline.java
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 DontInline {
|
||||
}
|
||||
37
jdkSrc/jdk8/java/lang/invoke/ForceInline.java
Normal file
37
jdkSrc/jdk8/java/lang/invoke/ForceInline.java
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 {
|
||||
}
|
||||
145
jdkSrc/jdk8/java/lang/invoke/InfoFromMemberName.java
Normal file
145
jdkSrc/jdk8/java/lang/invoke/InfoFromMemberName.java
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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.security.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.invoke.MethodHandleNatives.Constants;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/*
|
||||
* Auxiliary to MethodHandleInfo, wants to nest in MethodHandleInfo but must be non-public.
|
||||
*/
|
||||
/*non-public*/
|
||||
final
|
||||
class InfoFromMemberName implements MethodHandleInfo {
|
||||
private final MemberName member;
|
||||
private final int referenceKind;
|
||||
|
||||
InfoFromMemberName(Lookup lookup, MemberName member, byte referenceKind) {
|
||||
assert(member.isResolved() || member.isMethodHandleInvoke());
|
||||
assert(member.referenceKindIsConsistentWith(referenceKind));
|
||||
this.member = member;
|
||||
this.referenceKind = referenceKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDeclaringClass() {
|
||||
return member.getDeclaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return member.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodType getMethodType() {
|
||||
return member.getMethodOrFieldType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModifiers() {
|
||||
return member.getModifiers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReferenceKind() {
|
||||
return referenceKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MethodHandleInfo.toString(getReferenceKind(), getDeclaringClass(), getName(), getMethodType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup) {
|
||||
if (member.isMethodHandleInvoke() && !member.isVarargs()) {
|
||||
// This member is an instance of a signature-polymorphic method, which cannot be reflected
|
||||
// A method handle invoker can come in either of two forms:
|
||||
// A generic placeholder (present in the source code, and varargs)
|
||||
// and a signature-polymorphic instance (synthetic and not varargs).
|
||||
// For more information see comments on {@link MethodHandleNatives#linkMethod}.
|
||||
throw new IllegalArgumentException("cannot reflect signature polymorphic method");
|
||||
}
|
||||
Member mem = AccessController.doPrivileged(new PrivilegedAction<Member>() {
|
||||
public Member run() {
|
||||
try {
|
||||
return reflectUnchecked();
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
Class<?> defc = getDeclaringClass();
|
||||
byte refKind = (byte) getReferenceKind();
|
||||
lookup.checkAccess(refKind, defc, convertToMemberName(refKind, mem));
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
return expected.cast(mem);
|
||||
}
|
||||
|
||||
private Member reflectUnchecked() throws ReflectiveOperationException {
|
||||
byte refKind = (byte) getReferenceKind();
|
||||
Class<?> defc = getDeclaringClass();
|
||||
boolean isPublic = Modifier.isPublic(getModifiers());
|
||||
if (MethodHandleNatives.refKindIsMethod(refKind)) {
|
||||
if (isPublic)
|
||||
return defc.getMethod(getName(), getMethodType().parameterArray());
|
||||
else
|
||||
return defc.getDeclaredMethod(getName(), getMethodType().parameterArray());
|
||||
} else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
|
||||
if (isPublic)
|
||||
return defc.getConstructor(getMethodType().parameterArray());
|
||||
else
|
||||
return defc.getDeclaredConstructor(getMethodType().parameterArray());
|
||||
} else if (MethodHandleNatives.refKindIsField(refKind)) {
|
||||
if (isPublic)
|
||||
return defc.getField(getName());
|
||||
else
|
||||
return defc.getDeclaredField(getName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("referenceKind="+refKind);
|
||||
}
|
||||
}
|
||||
|
||||
private static MemberName convertToMemberName(byte refKind, Member mem) throws IllegalAccessException {
|
||||
if (mem instanceof Method) {
|
||||
boolean wantSpecial = (refKind == REF_invokeSpecial);
|
||||
return new MemberName((Method) mem, wantSpecial);
|
||||
} else if (mem instanceof Constructor) {
|
||||
return new MemberName((Constructor) mem);
|
||||
} else if (mem instanceof Field) {
|
||||
boolean isSetter = (refKind == REF_putField || refKind == REF_putStatic);
|
||||
return new MemberName((Field) mem, isSetter);
|
||||
}
|
||||
throw new InternalError(mem.getClass().getName());
|
||||
}
|
||||
}
|
||||
37
jdkSrc/jdk8/java/lang/invoke/InjectedProfile.java
Normal file
37
jdkSrc/jdk8/java/lang/invoke/InjectedProfile.java
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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 InjectedProfile {
|
||||
}
|
||||
563
jdkSrc/jdk8/java/lang/invoke/InnerClassLambdaMetafactory.java
Normal file
563
jdkSrc/jdk8/java/lang/invoke/InnerClassLambdaMetafactory.java
Normal file
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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 jdk.internal.org.objectweb.asm.*;
|
||||
import sun.invoke.util.BytecodeDescriptor;
|
||||
import sun.misc.Unsafe;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import java.io.FilePermission;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.PropertyPermission;
|
||||
import java.util.Set;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
* Lambda metafactory implementation which dynamically creates an
|
||||
* inner-class-like class per lambda callsite.
|
||||
*
|
||||
* @see LambdaMetafactory
|
||||
*/
|
||||
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
private static final int CLASSFILE_VERSION = 52;
|
||||
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
||||
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
|
||||
private static final String NAME_CTOR = "<init>";
|
||||
private static final String NAME_FACTORY = "get$Lambda";
|
||||
|
||||
//Serialization support
|
||||
private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
|
||||
private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
|
||||
private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
|
||||
private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
|
||||
private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
|
||||
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
|
||||
private static final String NAME_METHOD_READ_OBJECT = "readObject";
|
||||
private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
|
||||
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
|
||||
= MethodType.methodType(void.class,
|
||||
Class.class,
|
||||
String.class, String.class, String.class,
|
||||
int.class, String.class, String.class, String.class,
|
||||
String.class,
|
||||
Object[].class).toMethodDescriptorString();
|
||||
private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION
|
||||
= MethodType.methodType(void.class, String.class).toMethodDescriptorString();
|
||||
private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
|
||||
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
// Used to ensure that each spun class name is unique
|
||||
private static final AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
// For dumping generated classes to disk, for debugging purposes
|
||||
private static final ProxyClassesDumper dumper;
|
||||
|
||||
static {
|
||||
final String key = "jdk.internal.lambda.dumpProxyClasses";
|
||||
String path = AccessController.doPrivileged(
|
||||
new GetPropertyAction(key), null,
|
||||
new PropertyPermission(key , "read"));
|
||||
dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
|
||||
}
|
||||
|
||||
// See context values in AbstractValidatingLambdaMetafactory
|
||||
private final String implMethodClassName; // Name of type containing implementation "CC"
|
||||
private final String implMethodName; // Name of implementation method "impl"
|
||||
private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
|
||||
private final Class<?> implMethodReturnClass; // class for implementaion method return type "Ljava/lang/String;"
|
||||
private final MethodType constructorType; // Generated class constructor type "(CC)void"
|
||||
private final ClassWriter cw; // ASM class writer
|
||||
private final String[] argNames; // Generated names for the constructor arguments
|
||||
private final String[] argDescs; // Type descriptors for the constructor arguments
|
||||
private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
|
||||
|
||||
/**
|
||||
* General meta-factory constructor, supporting both standard cases and
|
||||
* allowing for uncommon options such as serialization or bridging.
|
||||
*
|
||||
* @param caller Stacked automatically by VM; represents a lookup context
|
||||
* with the accessibility privileges of the caller.
|
||||
* @param invokedType Stacked automatically by VM; the signature of the
|
||||
* invoked method, which includes the expected static
|
||||
* type of the returned lambda object, and the static
|
||||
* types of the captured arguments for the lambda. In
|
||||
* the event that the implementation method is an
|
||||
* instance method, the first argument in the invocation
|
||||
* signature will correspond to the receiver.
|
||||
* @param samMethodName Name of the method in the functional interface to
|
||||
* which the lambda or method reference is being
|
||||
* converted, represented as a String.
|
||||
* @param samMethodType Type of the method in the functional interface to
|
||||
* which the lambda or method reference is being
|
||||
* converted, represented as a MethodType.
|
||||
* @param implMethod The implementation method which should be called (with
|
||||
* suitable adaptation of argument types, return types,
|
||||
* and adjustment for captured arguments) when methods of
|
||||
* the resulting functional interface instance are invoked.
|
||||
* @param instantiatedMethodType The signature of the primary functional
|
||||
* interface method after type variables are
|
||||
* substituted with their instantiation from
|
||||
* the capture site
|
||||
* @param isSerializable Should the lambda be made serializable? If set,
|
||||
* either the target type or one of the additional SAM
|
||||
* types must extend {@code Serializable}.
|
||||
* @param markerInterfaces Additional interfaces which the lambda object
|
||||
* should implement.
|
||||
* @param additionalBridges Method types for additional signatures to be
|
||||
* bridged to the implementation method
|
||||
* @throws LambdaConversionException If any of the meta-factory protocol
|
||||
* invariants are violated
|
||||
*/
|
||||
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
|
||||
MethodType invokedType,
|
||||
String samMethodName,
|
||||
MethodType samMethodType,
|
||||
MethodHandle implMethod,
|
||||
MethodType instantiatedMethodType,
|
||||
boolean isSerializable,
|
||||
Class<?>[] markerInterfaces,
|
||||
MethodType[] additionalBridges)
|
||||
throws LambdaConversionException {
|
||||
super(caller, invokedType, samMethodName, samMethodType,
|
||||
implMethod, instantiatedMethodType,
|
||||
isSerializable, markerInterfaces, additionalBridges);
|
||||
implMethodClassName = implDefiningClass.getName().replace('.', '/');
|
||||
implMethodName = implInfo.getName();
|
||||
implMethodDesc = implMethodType.toMethodDescriptorString();
|
||||
implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial)
|
||||
? implDefiningClass
|
||||
: implMethodType.returnType();
|
||||
constructorType = invokedType.changeReturnType(Void.TYPE);
|
||||
lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
|
||||
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
int parameterCount = invokedType.parameterCount();
|
||||
if (parameterCount > 0) {
|
||||
argNames = new String[parameterCount];
|
||||
argDescs = new String[parameterCount];
|
||||
for (int i = 0; i < parameterCount; i++) {
|
||||
argNames[i] = "arg$" + (i + 1);
|
||||
argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
|
||||
}
|
||||
} else {
|
||||
argNames = argDescs = EMPTY_STRING_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the CallSite. Generate a class file which implements the functional
|
||||
* interface, define the class, if there are no parameters create an instance
|
||||
* of the class which the CallSite will return, otherwise, generate handles
|
||||
* which will call the class' constructor.
|
||||
*
|
||||
* @return a CallSite, which, when invoked, will return an instance of the
|
||||
* functional interface
|
||||
* @throws ReflectiveOperationException
|
||||
* @throws LambdaConversionException If properly formed functional interface
|
||||
* is not found
|
||||
*/
|
||||
@Override
|
||||
CallSite buildCallSite() throws LambdaConversionException {
|
||||
final Class<?> innerClass = spinInnerClass();
|
||||
if (invokedType.parameterCount() == 0) {
|
||||
final Constructor<?>[] ctrs = AccessController.doPrivileged(
|
||||
new PrivilegedAction<Constructor<?>[]>() {
|
||||
@Override
|
||||
public Constructor<?>[] run() {
|
||||
Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
|
||||
if (ctrs.length == 1) {
|
||||
// The lambda implementing inner class constructor is private, set
|
||||
// it accessible (by us) before creating the constant sole instance
|
||||
ctrs[0].setAccessible(true);
|
||||
}
|
||||
return ctrs;
|
||||
}
|
||||
});
|
||||
if (ctrs.length != 1) {
|
||||
throw new LambdaConversionException("Expected one lambda constructor for "
|
||||
+ innerClass.getCanonicalName() + ", got " + ctrs.length);
|
||||
}
|
||||
|
||||
try {
|
||||
Object inst = ctrs[0].newInstance();
|
||||
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
|
||||
}
|
||||
catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException("Exception instantiating lambda object", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
UNSAFE.ensureClassInitialized(innerClass);
|
||||
return new ConstantCallSite(
|
||||
MethodHandles.Lookup.IMPL_LOOKUP
|
||||
.findStatic(innerClass, NAME_FACTORY, invokedType));
|
||||
}
|
||||
catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException("Exception finding constructor", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a class file which implements the functional
|
||||
* interface, define and return the class.
|
||||
*
|
||||
* @implNote The class that is generated does not include signature
|
||||
* information for exceptions that may be present on the SAM method.
|
||||
* This is to reduce classfile size, and is harmless as checked exceptions
|
||||
* are erased anyway, no one will ever compile against this classfile,
|
||||
* and we make no guarantees about the reflective properties of lambda
|
||||
* objects.
|
||||
*
|
||||
* @return a Class which implements the functional interface
|
||||
* @throws LambdaConversionException If properly formed functional interface
|
||||
* is not found
|
||||
*/
|
||||
private Class<?> spinInnerClass() throws LambdaConversionException {
|
||||
String[] interfaces;
|
||||
String samIntf = samBase.getName().replace('.', '/');
|
||||
boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
|
||||
if (markerInterfaces.length == 0) {
|
||||
interfaces = new String[]{samIntf};
|
||||
} else {
|
||||
// Assure no duplicate interfaces (ClassFormatError)
|
||||
Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
|
||||
itfs.add(samIntf);
|
||||
for (Class<?> markerInterface : markerInterfaces) {
|
||||
itfs.add(markerInterface.getName().replace('.', '/'));
|
||||
accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
|
||||
}
|
||||
interfaces = itfs.toArray(new String[itfs.size()]);
|
||||
}
|
||||
|
||||
cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
|
||||
lambdaClassName, null,
|
||||
JAVA_LANG_OBJECT, interfaces);
|
||||
|
||||
// Generate final fields to be filled in by constructor
|
||||
for (int i = 0; i < argDescs.length; i++) {
|
||||
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
|
||||
argNames[i],
|
||||
argDescs[i],
|
||||
null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
generateConstructor();
|
||||
|
||||
if (invokedType.parameterCount() != 0) {
|
||||
generateFactory();
|
||||
}
|
||||
|
||||
// Forward the SAM method
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
|
||||
samMethodType.toMethodDescriptorString(), null, null);
|
||||
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
|
||||
new ForwardingMethodGenerator(mv).generate(samMethodType);
|
||||
|
||||
// Forward the bridges
|
||||
if (additionalBridges != null) {
|
||||
for (MethodType mt : additionalBridges) {
|
||||
mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
|
||||
mt.toMethodDescriptorString(), null, null);
|
||||
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
|
||||
new ForwardingMethodGenerator(mv).generate(mt);
|
||||
}
|
||||
}
|
||||
|
||||
if (isSerializable)
|
||||
generateSerializationFriendlyMethods();
|
||||
else if (accidentallySerializable)
|
||||
generateSerializationHostileMethods();
|
||||
|
||||
cw.visitEnd();
|
||||
|
||||
// Define the generated class in this VM.
|
||||
|
||||
final byte[] classBytes = cw.toByteArray();
|
||||
|
||||
// If requested, dump out to a file for debugging purposes
|
||||
if (dumper != null) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
dumper.dumpClass(lambdaClassName, classBytes);
|
||||
return null;
|
||||
}
|
||||
}, null,
|
||||
new FilePermission("<<ALL FILES>>", "read, write"),
|
||||
// createDirectories may need it
|
||||
new PropertyPermission("user.dir", "read"));
|
||||
}
|
||||
|
||||
return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the factory method for the class
|
||||
*/
|
||||
private void generateFactory() {
|
||||
MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
|
||||
m.visitCode();
|
||||
m.visitTypeInsn(NEW, lambdaClassName);
|
||||
m.visitInsn(Opcodes.DUP);
|
||||
int parameterCount = invokedType.parameterCount();
|
||||
for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
|
||||
Class<?> argType = invokedType.parameterType(typeIndex);
|
||||
m.visitVarInsn(getLoadOpcode(argType), varIndex);
|
||||
varIndex += getParameterSize(argType);
|
||||
}
|
||||
m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
|
||||
m.visitInsn(ARETURN);
|
||||
m.visitMaxs(-1, -1);
|
||||
m.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the constructor for the class
|
||||
*/
|
||||
private void generateConstructor() {
|
||||
// Generate constructor
|
||||
MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
|
||||
constructorType.toMethodDescriptorString(), null, null);
|
||||
ctor.visitCode();
|
||||
ctor.visitVarInsn(ALOAD, 0);
|
||||
ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
|
||||
METHOD_DESCRIPTOR_VOID, false);
|
||||
int parameterCount = invokedType.parameterCount();
|
||||
for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
|
||||
ctor.visitVarInsn(ALOAD, 0);
|
||||
Class<?> argType = invokedType.parameterType(i);
|
||||
ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
|
||||
lvIndex += getParameterSize(argType);
|
||||
ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
|
||||
}
|
||||
ctor.visitInsn(RETURN);
|
||||
// Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
||||
ctor.visitMaxs(-1, -1);
|
||||
ctor.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a writeReplace method that supports serialization
|
||||
*/
|
||||
private void generateSerializationFriendlyMethods() {
|
||||
TypeConvertingMethodAdapter mv
|
||||
= new TypeConvertingMethodAdapter(
|
||||
cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
|
||||
NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
|
||||
null, null));
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitLdcInsn(Type.getType(targetClass));
|
||||
mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
|
||||
mv.visitLdcInsn(samMethodName);
|
||||
mv.visitLdcInsn(samMethodType.toMethodDescriptorString());
|
||||
mv.visitLdcInsn(implInfo.getReferenceKind());
|
||||
mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
|
||||
mv.visitLdcInsn(implInfo.getName());
|
||||
mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
|
||||
mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
|
||||
mv.iconst(argDescs.length);
|
||||
mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
|
||||
for (int i = 0; i < argDescs.length; i++) {
|
||||
mv.visitInsn(DUP);
|
||||
mv.iconst(i);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
|
||||
mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
|
||||
mv.visitInsn(AASTORE);
|
||||
}
|
||||
mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
|
||||
DESCR_CTOR_SERIALIZED_LAMBDA, false);
|
||||
mv.visitInsn(ARETURN);
|
||||
// Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a readObject/writeObject method that is hostile to serialization
|
||||
*/
|
||||
private void generateSerializationHostileMethods() {
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
|
||||
NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
|
||||
null, SER_HOSTILE_EXCEPTIONS);
|
||||
mv.visitCode();
|
||||
mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitLdcInsn("Non-serializable lambda");
|
||||
mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
|
||||
DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
|
||||
mv.visitInsn(ATHROW);
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
|
||||
mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
|
||||
NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
|
||||
null, SER_HOSTILE_EXCEPTIONS);
|
||||
mv.visitCode();
|
||||
mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitLdcInsn("Non-serializable lambda");
|
||||
mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
|
||||
DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
|
||||
mv.visitInsn(ATHROW);
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* This class generates a method body which calls the lambda implementation
|
||||
* method, converting arguments, as needed.
|
||||
*/
|
||||
private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
|
||||
|
||||
ForwardingMethodGenerator(MethodVisitor mv) {
|
||||
super(mv);
|
||||
}
|
||||
|
||||
void generate(MethodType methodType) {
|
||||
visitCode();
|
||||
|
||||
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
|
||||
visitTypeInsn(NEW, implMethodClassName);
|
||||
visitInsn(DUP);
|
||||
}
|
||||
for (int i = 0; i < argNames.length; i++) {
|
||||
visitVarInsn(ALOAD, 0);
|
||||
visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
|
||||
}
|
||||
|
||||
convertArgumentTypes(methodType);
|
||||
|
||||
// Invoke the method we want to forward to
|
||||
visitMethodInsn(invocationOpcode(), implMethodClassName,
|
||||
implMethodName, implMethodDesc,
|
||||
implDefiningClass.isInterface());
|
||||
|
||||
// Convert the return value (if any) and return it
|
||||
// Note: if adapting from non-void to void, the 'return'
|
||||
// instruction will pop the unneeded result
|
||||
Class<?> samReturnClass = methodType.returnType();
|
||||
convertType(implMethodReturnClass, samReturnClass, samReturnClass);
|
||||
visitInsn(getReturnOpcode(samReturnClass));
|
||||
// Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
|
||||
visitMaxs(-1, -1);
|
||||
visitEnd();
|
||||
}
|
||||
|
||||
private void convertArgumentTypes(MethodType samType) {
|
||||
int lvIndex = 0;
|
||||
boolean samIncludesReceiver = implIsInstanceMethod &&
|
||||
invokedType.parameterCount() == 0;
|
||||
int samReceiverLength = samIncludesReceiver ? 1 : 0;
|
||||
if (samIncludesReceiver) {
|
||||
// push receiver
|
||||
Class<?> rcvrType = samType.parameterType(0);
|
||||
visitVarInsn(getLoadOpcode(rcvrType), lvIndex + 1);
|
||||
lvIndex += getParameterSize(rcvrType);
|
||||
convertType(rcvrType, implDefiningClass, instantiatedMethodType.parameterType(0));
|
||||
}
|
||||
int samParametersLength = samType.parameterCount();
|
||||
int argOffset = implMethodType.parameterCount() - samParametersLength;
|
||||
for (int i = samReceiverLength; i < samParametersLength; i++) {
|
||||
Class<?> argType = samType.parameterType(i);
|
||||
visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
|
||||
lvIndex += getParameterSize(argType);
|
||||
convertType(argType, implMethodType.parameterType(argOffset + i), instantiatedMethodType.parameterType(i));
|
||||
}
|
||||
}
|
||||
|
||||
private int invocationOpcode() throws InternalError {
|
||||
switch (implKind) {
|
||||
case MethodHandleInfo.REF_invokeStatic:
|
||||
return INVOKESTATIC;
|
||||
case MethodHandleInfo.REF_newInvokeSpecial:
|
||||
return INVOKESPECIAL;
|
||||
case MethodHandleInfo.REF_invokeVirtual:
|
||||
return INVOKEVIRTUAL;
|
||||
case MethodHandleInfo.REF_invokeInterface:
|
||||
return INVOKEINTERFACE;
|
||||
case MethodHandleInfo.REF_invokeSpecial:
|
||||
return INVOKESPECIAL;
|
||||
default:
|
||||
throw new InternalError("Unexpected invocation kind: " + implKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int getParameterSize(Class<?> c) {
|
||||
if (c == Void.TYPE) {
|
||||
return 0;
|
||||
} else if (c == Long.TYPE || c == Double.TYPE) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getLoadOpcode(Class<?> c) {
|
||||
if(c == Void.TYPE) {
|
||||
throw new InternalError("Unexpected void type of load opcode");
|
||||
}
|
||||
return ILOAD + getOpcodeOffset(c);
|
||||
}
|
||||
|
||||
static int getReturnOpcode(Class<?> c) {
|
||||
if(c == Void.TYPE) {
|
||||
return RETURN;
|
||||
}
|
||||
return IRETURN + getOpcodeOffset(c);
|
||||
}
|
||||
|
||||
private static int getOpcodeOffset(Class<?> c) {
|
||||
if (c.isPrimitive()) {
|
||||
if (c == Long.TYPE) {
|
||||
return 1;
|
||||
} else if (c == Float.TYPE) {
|
||||
return 2;
|
||||
} else if (c == Double.TYPE) {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
33
jdkSrc/jdk8/java/lang/invoke/InvokeDynamic.java
Normal file
33
jdkSrc/jdk8/java/lang/invoke/InvokeDynamic.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* This is a place-holder class. Some HotSpot implementations need to see it.
|
||||
*/
|
||||
final class InvokeDynamic {
|
||||
private InvokeDynamic() { throw new InternalError(); } // do not instantiate
|
||||
}
|
||||
1448
jdkSrc/jdk8/java/lang/invoke/InvokerBytecodeGenerator.java
Normal file
1448
jdkSrc/jdk8/java/lang/invoke/InvokerBytecodeGenerator.java
Normal file
File diff suppressed because it is too large
Load Diff
453
jdkSrc/jdk8/java/lang/invoke/Invokers.java
Normal file
453
jdkSrc/jdk8/java/lang/invoke/Invokers.java
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, 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.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
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.
|
||||
* @author jrose
|
||||
*/
|
||||
class Invokers {
|
||||
// exact type (sans leading taget MH) for the outgoing call
|
||||
private final MethodType targetType;
|
||||
|
||||
// Cached adapter information:
|
||||
private final @Stable MethodHandle[] invokers = new MethodHandle[INV_LIMIT];
|
||||
// Indexes into invokers:
|
||||
static final int
|
||||
INV_EXACT = 0, // MethodHandles.exactInvoker
|
||||
INV_GENERIC = 1, // MethodHandles.invoker (generic invocation)
|
||||
INV_BASIC = 2, // MethodHandles.basicInvoker
|
||||
INV_LIMIT = 3;
|
||||
|
||||
/** Compute and cache information common to all collecting adapters
|
||||
* that implement members of the erasure-family of the given erased type.
|
||||
*/
|
||||
/*non-public*/ Invokers(MethodType targetType) {
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle exactInvoker() {
|
||||
MethodHandle invoker = cachedInvoker(INV_EXACT);
|
||||
if (invoker != null) return invoker;
|
||||
invoker = makeExactOrGeneralInvoker(true);
|
||||
return setCachedInvoker(INV_EXACT, invoker);
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle genericInvoker() {
|
||||
MethodHandle invoker = cachedInvoker(INV_GENERIC);
|
||||
if (invoker != null) return invoker;
|
||||
invoker = makeExactOrGeneralInvoker(false);
|
||||
return setCachedInvoker(INV_GENERIC, invoker);
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle basicInvoker() {
|
||||
MethodHandle invoker = cachedInvoker(INV_BASIC);
|
||||
if (invoker != null) return invoker;
|
||||
MethodType basicType = targetType.basicType();
|
||||
if (basicType != targetType) {
|
||||
// double cache; not used significantly
|
||||
return setCachedInvoker(INV_BASIC, basicType.invokers().basicInvoker());
|
||||
}
|
||||
invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_BASIC_INV);
|
||||
if (invoker == null) {
|
||||
MemberName method = invokeBasicMethod(basicType);
|
||||
invoker = DirectMethodHandle.make(method);
|
||||
assert(checkInvoker(invoker));
|
||||
invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_BASIC_INV, invoker);
|
||||
}
|
||||
return setCachedInvoker(INV_BASIC, invoker);
|
||||
}
|
||||
|
||||
private MethodHandle cachedInvoker(int idx) {
|
||||
return invokers[idx];
|
||||
}
|
||||
|
||||
private synchronized MethodHandle setCachedInvoker(int idx, final MethodHandle invoker) {
|
||||
// Simulate a CAS, to avoid racy duplication of results.
|
||||
MethodHandle prev = invokers[idx];
|
||||
if (prev != null) return prev;
|
||||
return invokers[idx] = invoker;
|
||||
}
|
||||
|
||||
private MethodHandle makeExactOrGeneralInvoker(boolean isExact) {
|
||||
MethodType mtype = targetType;
|
||||
MethodType invokerType = mtype.invokerType();
|
||||
int which = (isExact ? MethodTypeForm.LF_EX_INVOKER : MethodTypeForm.LF_GEN_INVOKER);
|
||||
LambdaForm lform = invokeHandleForm(mtype, false, which);
|
||||
MethodHandle invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype);
|
||||
String whichName = (isExact ? "invokeExact" : "invoke");
|
||||
invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke(whichName, mtype), false);
|
||||
assert(checkInvoker(invoker));
|
||||
maybeCompileToBytecode(invoker);
|
||||
return invoker;
|
||||
}
|
||||
|
||||
/** If the target type seems to be common enough, eagerly compile the invoker to bytecodes. */
|
||||
private void maybeCompileToBytecode(MethodHandle invoker) {
|
||||
final int EAGER_COMPILE_ARITY_LIMIT = 10;
|
||||
if (targetType == targetType.erase() &&
|
||||
targetType.parameterCount() < EAGER_COMPILE_ARITY_LIMIT) {
|
||||
invoker.form.compileToBytecode();
|
||||
}
|
||||
}
|
||||
|
||||
// This next one is called from LambdaForm.NamedFunction.<init>.
|
||||
/*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) {
|
||||
assert(basicType == basicType.basicType());
|
||||
try {
|
||||
//Lookup.findVirtual(MethodHandle.class, name, type);
|
||||
return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, "invokeBasic", basicType);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError("JVM cannot find invoker for "+basicType, ex);
|
||||
}
|
||||
}
|
||||
|
||||
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 true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find or create an invoker which passes unchanged a given number of arguments
|
||||
* and spreads the rest from a trailing array argument.
|
||||
* The invoker target type is the post-spread type {@code (TYPEOF(uarg*), TYPEOF(sarg*))=>RT}.
|
||||
* All the {@code sarg}s must have a common type {@code C}. (If there are none, {@code Object} is assumed.}
|
||||
* @param leadingArgCount the number of unchanged (non-spread) arguments
|
||||
* @return {@code invoker.invokeExact(mh, uarg*, C[]{sarg*}) := (RT)mh.invoke(uarg*, sarg*)}
|
||||
*/
|
||||
/*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) {
|
||||
int spreadArgCount = targetType.parameterCount() - leadingArgCount;
|
||||
MethodType postSpreadType = targetType;
|
||||
Class<?> argArrayType = impliedRestargType(postSpreadType, leadingArgCount);
|
||||
if (postSpreadType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) {
|
||||
return genericInvoker().asSpreader(argArrayType, spreadArgCount);
|
||||
}
|
||||
// Cannot build a generic invoker here of type ginvoker.invoke(mh, a*[254]).
|
||||
// Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a)
|
||||
// where filter(mh) == mh.asSpreader(Object[], spreadArgCount)
|
||||
MethodType preSpreadType = postSpreadType
|
||||
.replaceParameterTypes(leadingArgCount, postSpreadType.parameterCount(), argArrayType);
|
||||
MethodHandle arrayInvoker = MethodHandles.invoker(preSpreadType);
|
||||
MethodHandle makeSpreader = MethodHandles.insertArguments(Lazy.MH_asSpreader, 1, argArrayType, spreadArgCount);
|
||||
return MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader);
|
||||
}
|
||||
|
||||
private static Class<?> impliedRestargType(MethodType restargType, int fromPos) {
|
||||
if (restargType.isGeneric()) return Object[].class; // can be nothing else
|
||||
int maxPos = restargType.parameterCount();
|
||||
if (fromPos >= maxPos) return Object[].class; // reasonable default
|
||||
Class<?> argType = restargType.parameterType(fromPos);
|
||||
for (int i = fromPos+1; i < maxPos; i++) {
|
||||
if (argType != restargType.parameterType(i))
|
||||
throw newIllegalArgumentException("need homogeneous rest arguments", restargType);
|
||||
}
|
||||
if (argType == Object.class) return Object[].class;
|
||||
return Array.newInstance(argType, 0).getClass();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Invokers"+targetType;
|
||||
}
|
||||
|
||||
static MemberName methodHandleInvokeLinkerMethod(String name,
|
||||
MethodType mtype,
|
||||
Object[] appendixResult) {
|
||||
int which;
|
||||
switch (name) {
|
||||
case "invokeExact": which = MethodTypeForm.LF_EX_LINKER; break;
|
||||
case "invoke": which = MethodTypeForm.LF_GEN_LINKER; break;
|
||||
default: throw new InternalError("not invoker: "+name);
|
||||
}
|
||||
LambdaForm lform;
|
||||
if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - MH_LINKER_ARG_APPENDED) {
|
||||
lform = invokeHandleForm(mtype, false, which);
|
||||
appendixResult[0] = mtype;
|
||||
} else {
|
||||
lform = invokeHandleForm(mtype, true, which);
|
||||
}
|
||||
return lform.vmentry;
|
||||
}
|
||||
|
||||
// argument count to account for trailing "appendix value" (typically the mtype)
|
||||
private static final int MH_LINKER_ARG_APPENDED = 1;
|
||||
|
||||
/** Returns an adapter for invokeExact or generic invoke, as a MH or constant pool linker.
|
||||
* If !customized, caller is responsible for supplying, during adapter execution,
|
||||
* a copy of the exact mtype. This is because the adapter might be generalized to
|
||||
* a basic type.
|
||||
* @param mtype the caller's method type (either basic or full-custom)
|
||||
* @param customized whether to use a trailing appendix argument (to carry the mtype)
|
||||
* @param which bit-encoded 0x01 whether it is a CP adapter ("linker") or MHs.invoker value ("invoker");
|
||||
* 0x02 whether it is for invokeExact or generic invoke
|
||||
*/
|
||||
private static LambdaForm invokeHandleForm(MethodType mtype, boolean customized, int which) {
|
||||
boolean isCached;
|
||||
if (!customized) {
|
||||
mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
|
||||
isCached = true;
|
||||
} else {
|
||||
isCached = false; // maybe cache if mtype == mtype.basicType()
|
||||
}
|
||||
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;
|
||||
if (isCached) {
|
||||
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 && !customized ? 1 : 0);
|
||||
int nameCursor = OUTARG_LIMIT;
|
||||
final int MTYPE_ARG = customized ? -1 : nameCursor++; // might be last in-argument
|
||||
final int CHECK_TYPE = nameCursor++;
|
||||
final int CHECK_CUSTOM = (CUSTOMIZE_THRESHOLD >= 0) ? nameCursor++ : -1;
|
||||
final int LINKER_CALL = nameCursor++;
|
||||
MethodType invokerFormType = mtype.invokerType();
|
||||
if (isLinker) {
|
||||
if (!customized)
|
||||
invokerFormType = invokerFormType.appendParameterTypes(MemberName.class);
|
||||
} else {
|
||||
invokerFormType = invokerFormType.invokerType();
|
||||
}
|
||||
Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
|
||||
assert(names.length == nameCursor)
|
||||
: Arrays.asList(mtype, customized, which, nameCursor, names.length);
|
||||
if (MTYPE_ARG >= INARG_LIMIT) {
|
||||
assert(names[MTYPE_ARG] == null);
|
||||
BoundMethodHandle.SpeciesData speciesData = BoundMethodHandle.speciesData_L();
|
||||
names[THIS_MH] = names[THIS_MH].withConstraint(speciesData);
|
||||
NamedFunction getter = speciesData.getterFunction(0);
|
||||
names[MTYPE_ARG] = new Name(getter, names[THIS_MH]);
|
||||
// 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 = mtype.basicType();
|
||||
Object[] outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class);
|
||||
Object mtypeArg = (customized ? mtype : names[MTYPE_ARG]);
|
||||
if (!isGeneric) {
|
||||
names[CHECK_TYPE] = new Name(NF_checkExactType, names[CALL_MH], mtypeArg);
|
||||
// mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*)
|
||||
} else {
|
||||
names[CHECK_TYPE] = new Name(NF_checkGenericType, names[CALL_MH], mtypeArg);
|
||||
// mh.invokeGeneric(a*):R => checkGenericType(mh, TYPEOF(a*:R)).invokeBasic(a*)
|
||||
outArgs[0] = names[CHECK_TYPE];
|
||||
}
|
||||
if (CHECK_CUSTOM != -1) {
|
||||
names[CHECK_CUSTOM] = new Name(NF_checkCustomized, outArgs[0]);
|
||||
}
|
||||
names[LINKER_CALL] = new Name(outCallType, outArgs);
|
||||
lform = new LambdaForm(debugName, INARG_LIMIT, names);
|
||||
if (isLinker)
|
||||
lform.compileToBytecode(); // JVM needs a real methodOop
|
||||
if (isCached)
|
||||
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.
|
||||
* Directly returns the type-adjusted MH to invoke, as follows:
|
||||
* {@code (R)MH.invoke(a*) => MH.asType(TYPEOF(a*:R)).invokeBasic(a*)}
|
||||
*/
|
||||
/*non-public*/ static
|
||||
@ForceInline
|
||||
Object checkGenericType(Object mhObj, Object expectedObj) {
|
||||
MethodHandle mh = (MethodHandle) mhObj;
|
||||
MethodType expected = (MethodType) expectedObj;
|
||||
return mh.asType(expected);
|
||||
/* Maybe add more paths here. Possible optimizations:
|
||||
* for (R)MH.invoke(a*),
|
||||
* let MT0 = TYPEOF(a*:R), MT1 = MH.type
|
||||
*
|
||||
* if MT0==MT1 or MT1 can be safely called by MT0
|
||||
* => MH.invokeBasic(a*)
|
||||
* if MT1 can be safely called by MT0[R := Object]
|
||||
* => MH.invokeBasic(a*) & checkcast(R)
|
||||
* if MT1 can be safely called by MT0[* := Object]
|
||||
* => checkcast(A)* & MH.invokeBasic(a*) & checkcast(R)
|
||||
* if a big adapter BA can be pulled out of (MT0,MT1)
|
||||
* => BA.invokeBasic(MT0,MH,a*)
|
||||
* if a local adapter LA can cached on static CS0 = new GICS(MT0)
|
||||
* => CS0.LA.invokeBasic(MH,a*)
|
||||
* else
|
||||
* => MH.asType(MT0).invokeBasic(A*)
|
||||
*/
|
||||
}
|
||||
|
||||
static MemberName linkToCallSiteMethod(MethodType mtype) {
|
||||
LambdaForm lform = callSiteForm(mtype, false);
|
||||
return lform.vmentry;
|
||||
}
|
||||
|
||||
static MemberName linkToTargetMethod(MethodType mtype) {
|
||||
LambdaForm lform = callSiteForm(mtype, true);
|
||||
return lform.vmentry;
|
||||
}
|
||||
|
||||
// skipCallSite is true if we are optimizing a ConstantCallSite
|
||||
private static LambdaForm callSiteForm(MethodType mtype, boolean skipCallSite) {
|
||||
mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
|
||||
final int which = (skipCallSite ? MethodTypeForm.LF_MH_LINKER : MethodTypeForm.LF_CS_LINKER);
|
||||
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 ARG_BASE = 0;
|
||||
final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
|
||||
final int INARG_LIMIT = OUTARG_LIMIT + 1;
|
||||
int nameCursor = OUTARG_LIMIT;
|
||||
final int APPENDIX_ARG = nameCursor++; // the last in-argument
|
||||
final int CSITE_ARG = skipCallSite ? -1 : APPENDIX_ARG;
|
||||
final int CALL_MH = skipCallSite ? APPENDIX_ARG : nameCursor++; // result of getTarget
|
||||
final int LINKER_CALL = nameCursor++;
|
||||
MethodType invokerFormType = mtype.appendParameterTypes(skipCallSite ? MethodHandle.class : CallSite.class);
|
||||
Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
|
||||
assert(names.length == nameCursor);
|
||||
assert(names[APPENDIX_ARG] != null);
|
||||
if (!skipCallSite)
|
||||
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(mtype, outArgs);
|
||||
lform = new LambdaForm((skipCallSite ? "linkToTargetMethod" : "linkToCallSite"), INARG_LIMIT, names);
|
||||
lform.compileToBytecode(); // JVM needs a real methodOop
|
||||
lform = mtype.form().setCachedLambdaForm(which, lform);
|
||||
return lform;
|
||||
}
|
||||
|
||||
/** Static definition of MethodHandle.invokeGeneric checking code. */
|
||||
/*non-public*/ static
|
||||
@ForceInline
|
||||
Object getCallSiteTarget(Object site) {
|
||||
return ((CallSite)site).getTarget();
|
||||
}
|
||||
|
||||
/*non-public*/ static
|
||||
@ForceInline
|
||||
void checkCustomized(Object o) {
|
||||
MethodHandle mh = (MethodHandle)o;
|
||||
if (mh.form.customized == null) {
|
||||
maybeCustomize(mh);
|
||||
}
|
||||
}
|
||||
|
||||
/*non-public*/ static
|
||||
@DontInline
|
||||
void maybeCustomize(MethodHandle mh) {
|
||||
byte count = mh.customizationCount;
|
||||
if (count >= CUSTOMIZE_THRESHOLD) {
|
||||
mh.customize();
|
||||
} else {
|
||||
mh.customizationCount = (byte)(count+1);
|
||||
}
|
||||
}
|
||||
|
||||
// Local constant functions:
|
||||
private static final NamedFunction
|
||||
NF_checkExactType,
|
||||
NF_checkGenericType,
|
||||
NF_getCallSiteTarget,
|
||||
NF_checkCustomized;
|
||||
static {
|
||||
try {
|
||||
NamedFunction nfs[] = {
|
||||
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_checkCustomized = new NamedFunction(Invokers.class
|
||||
.getDeclaredMethod("checkCustomized", Object.class))
|
||||
};
|
||||
for (NamedFunction nf : nfs) {
|
||||
// Each nf must be statically invocable or we get tied up in our bootstraps.
|
||||
assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
|
||||
nf.resolve();
|
||||
}
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Lazy {
|
||||
private static final MethodHandle MH_asSpreader;
|
||||
|
||||
static {
|
||||
try {
|
||||
MH_asSpreader = IMPL_LOOKUP.findVirtual(MethodHandle.class, "asSpreader",
|
||||
MethodType.methodType(MethodHandle.class, Class.class, int.class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
jdkSrc/jdk8/java/lang/invoke/LambdaConversionException.java
Normal file
76
jdkSrc/jdk8/java/lang/invoke/LambdaConversionException.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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;
|
||||
|
||||
/**
|
||||
* LambdaConversionException
|
||||
*/
|
||||
public class LambdaConversionException extends Exception {
|
||||
private static final long serialVersionUID = 292L + 8L;
|
||||
|
||||
/**
|
||||
* Constructs a {@code LambdaConversionException}.
|
||||
*/
|
||||
public LambdaConversionException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code LambdaConversionException} with a message.
|
||||
* @param message the detail message
|
||||
*/
|
||||
public LambdaConversionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code LambdaConversionException} with a message and cause.
|
||||
* @param message the detail message
|
||||
* @param cause the cause
|
||||
*/
|
||||
public LambdaConversionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code LambdaConversionException} with a cause.
|
||||
* @param cause the cause
|
||||
*/
|
||||
public LambdaConversionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code LambdaConversionException} with a message,
|
||||
* cause, and other settings.
|
||||
* @param message the detail message
|
||||
* @param cause the cause
|
||||
* @param enableSuppression whether or not suppressed exceptions are enabled
|
||||
* @param writableStackTrace whether or not the stack trace is writable
|
||||
*/
|
||||
public LambdaConversionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
1846
jdkSrc/jdk8/java/lang/invoke/LambdaForm.java
Normal file
1846
jdkSrc/jdk8/java/lang/invoke/LambdaForm.java
Normal file
File diff suppressed because it is too large
Load Diff
398
jdkSrc/jdk8/java/lang/invoke/LambdaFormBuffer.java
Normal file
398
jdkSrc/jdk8/java/lang/invoke/LambdaFormBuffer.java
Normal file
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2014, 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.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
|
||||
/** Working storage for an LF that is being transformed.
|
||||
* Similarly to a StringBuffer, the editing can take place in multiple steps.
|
||||
*/
|
||||
final class LambdaFormBuffer {
|
||||
private int arity, length;
|
||||
private Name[] names;
|
||||
private Name[] originalNames; // snapshot of pre-transaction names
|
||||
private byte flags;
|
||||
private int firstChange;
|
||||
private Name resultName;
|
||||
private String debugName;
|
||||
private ArrayList<Name> dups;
|
||||
|
||||
private static final int F_TRANS = 0x10, F_OWNED = 0x03;
|
||||
|
||||
LambdaFormBuffer(LambdaForm lf) {
|
||||
this.arity = lf.arity;
|
||||
setNames(lf.names);
|
||||
int result = lf.result;
|
||||
if (result == LAST_RESULT) result = length - 1;
|
||||
if (result >= 0 && lf.names[result].type != V_TYPE)
|
||||
resultName = lf.names[result];
|
||||
debugName = lf.debugName;
|
||||
assert(lf.nameRefsAreLegal());
|
||||
}
|
||||
|
||||
private LambdaForm lambdaForm() {
|
||||
assert(!inTrans()); // need endEdit call to tidy things up
|
||||
return new LambdaForm(debugName, arity, nameArray(), resultIndex());
|
||||
}
|
||||
|
||||
Name name(int i) {
|
||||
assert(i < length);
|
||||
return names[i];
|
||||
}
|
||||
|
||||
Name[] nameArray() {
|
||||
return Arrays.copyOf(names, length);
|
||||
}
|
||||
|
||||
int resultIndex() {
|
||||
if (resultName == null) return VOID_RESULT;
|
||||
int index = indexOf(resultName, names);
|
||||
assert(index >= 0);
|
||||
return index;
|
||||
}
|
||||
|
||||
void setNames(Name[] names2) {
|
||||
names = originalNames = names2; // keep a record of where everything was to start with
|
||||
length = names2.length;
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
private boolean verifyArity() {
|
||||
for (int i = 0; i < arity && i < firstChange; i++) {
|
||||
assert(names[i].isParam()) : "#" + i + "=" + names[i];
|
||||
}
|
||||
for (int i = arity; i < length; i++) {
|
||||
assert(!names[i].isParam()) : "#" + i + "=" + names[i];
|
||||
}
|
||||
for (int i = length; i < names.length; i++) {
|
||||
assert(names[i] == null) : "#" + i + "=" + names[i];
|
||||
}
|
||||
// check resultName also
|
||||
if (resultName != null) {
|
||||
int resultIndex = indexOf(resultName, names);
|
||||
assert(resultIndex >= 0) : "not found: " + resultName.exprString() + Arrays.asList(names);
|
||||
assert(names[resultIndex] == resultName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean verifyFirstChange() {
|
||||
assert(inTrans());
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (names[i] != originalNames[i]) {
|
||||
assert(firstChange == i) : Arrays.asList(firstChange, i, originalNames[i].exprString(), Arrays.asList(names));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
assert(firstChange == length) : Arrays.asList(firstChange, Arrays.asList(names));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int indexOf(NamedFunction fn, NamedFunction[] fns) {
|
||||
for (int i = 0; i < fns.length; i++) {
|
||||
if (fns[i] == fn) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int indexOf(Name n, Name[] ns) {
|
||||
for (int i = 0; i < ns.length; i++) {
|
||||
if (ns[i] == n) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean inTrans() {
|
||||
return (flags & F_TRANS) != 0;
|
||||
}
|
||||
|
||||
int ownedCount() {
|
||||
return flags & F_OWNED;
|
||||
}
|
||||
|
||||
void growNames(int insertPos, int growLength) {
|
||||
int oldLength = length;
|
||||
int newLength = oldLength + growLength;
|
||||
int oc = ownedCount();
|
||||
if (oc == 0 || newLength > names.length) {
|
||||
names = Arrays.copyOf(names, (names.length + growLength) * 5 / 4);
|
||||
if (oc == 0) {
|
||||
flags++;
|
||||
oc++;
|
||||
assert(ownedCount() == oc);
|
||||
}
|
||||
}
|
||||
if (originalNames != null && originalNames.length < names.length) {
|
||||
originalNames = Arrays.copyOf(originalNames, names.length);
|
||||
if (oc == 1) {
|
||||
flags++;
|
||||
oc++;
|
||||
assert(ownedCount() == oc);
|
||||
}
|
||||
}
|
||||
if (growLength == 0) return;
|
||||
int insertEnd = insertPos + growLength;
|
||||
int tailLength = oldLength - insertPos;
|
||||
System.arraycopy(names, insertPos, names, insertEnd, tailLength);
|
||||
Arrays.fill(names, insertPos, insertEnd, null);
|
||||
if (originalNames != null) {
|
||||
System.arraycopy(originalNames, insertPos, originalNames, insertEnd, tailLength);
|
||||
Arrays.fill(originalNames, insertPos, insertEnd, null);
|
||||
}
|
||||
length = newLength;
|
||||
if (firstChange >= insertPos) {
|
||||
firstChange += growLength;
|
||||
}
|
||||
}
|
||||
|
||||
int lastIndexOf(Name n) {
|
||||
int result = -1;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (names[i] == n) result = i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** We have just overwritten the name at pos1 with the name at pos2.
|
||||
* This means that there are two copies of the name, which we will have to fix later.
|
||||
*/
|
||||
private void noteDuplicate(int pos1, int pos2) {
|
||||
Name n = names[pos1];
|
||||
assert(n == names[pos2]);
|
||||
assert(originalNames[pos1] != null); // something was replaced at pos1
|
||||
assert(originalNames[pos2] == null || originalNames[pos2] == n);
|
||||
if (dups == null) {
|
||||
dups = new ArrayList<>();
|
||||
}
|
||||
dups.add(n);
|
||||
}
|
||||
|
||||
/** Replace duplicate names by nulls, and remove all nulls. */
|
||||
private void clearDuplicatesAndNulls() {
|
||||
if (dups != null) {
|
||||
// Remove duplicates.
|
||||
assert(ownedCount() >= 1);
|
||||
for (Name dup : dups) {
|
||||
for (int i = firstChange; i < length; i++) {
|
||||
if (names[i] == dup && originalNames[i] != dup) {
|
||||
names[i] = null;
|
||||
assert(Arrays.asList(names).contains(dup));
|
||||
break; // kill only one dup
|
||||
}
|
||||
}
|
||||
}
|
||||
dups.clear();
|
||||
}
|
||||
// Now that we are done with originalNames, remove "killed" names.
|
||||
int oldLength = length;
|
||||
for (int i = firstChange; i < length; i++) {
|
||||
if (names[i] == null) {
|
||||
System.arraycopy(names, i + 1, names, i, (--length - i));
|
||||
--i; // restart loop at this position
|
||||
}
|
||||
}
|
||||
if (length < oldLength) {
|
||||
Arrays.fill(names, length, oldLength, null);
|
||||
}
|
||||
assert(!Arrays.asList(names).subList(0, length).contains(null));
|
||||
}
|
||||
|
||||
/** Create a private, writable copy of names.
|
||||
* Preserve the original copy, for reference.
|
||||
*/
|
||||
void startEdit() {
|
||||
assert(verifyArity());
|
||||
int oc = ownedCount();
|
||||
assert(!inTrans()); // no nested transactions
|
||||
flags |= F_TRANS;
|
||||
Name[] oldNames = names;
|
||||
Name[] ownBuffer = (oc == 2 ? originalNames : null);
|
||||
assert(ownBuffer != oldNames);
|
||||
if (ownBuffer != null && ownBuffer.length >= length) {
|
||||
names = copyNamesInto(ownBuffer);
|
||||
} else {
|
||||
// make a new buffer to hold the names
|
||||
final int SLOP = 2;
|
||||
names = Arrays.copyOf(oldNames, Math.max(length + SLOP, oldNames.length));
|
||||
if (oc < 2) ++flags;
|
||||
assert(ownedCount() == oc + 1);
|
||||
}
|
||||
originalNames = oldNames;
|
||||
assert(originalNames != names);
|
||||
firstChange = length;
|
||||
assert(inTrans());
|
||||
}
|
||||
|
||||
private void changeName(int i, Name name) {
|
||||
assert(inTrans());
|
||||
assert(i < length);
|
||||
Name oldName = names[i];
|
||||
assert(oldName == originalNames[i]); // no multiple changes
|
||||
assert(verifyFirstChange());
|
||||
if (ownedCount() == 0)
|
||||
growNames(0, 0);
|
||||
names[i] = name;
|
||||
if (firstChange > i) {
|
||||
firstChange = i;
|
||||
}
|
||||
if (resultName != null && resultName == oldName) {
|
||||
resultName = name;
|
||||
}
|
||||
}
|
||||
|
||||
/** Change the result name. Null means a void result. */
|
||||
void setResult(Name name) {
|
||||
assert(name == null || lastIndexOf(name) >= 0);
|
||||
resultName = name;
|
||||
}
|
||||
|
||||
/** Finish a transaction. */
|
||||
LambdaForm endEdit() {
|
||||
assert(verifyFirstChange());
|
||||
// Assuming names have been changed pairwise from originalNames[i] to names[i],
|
||||
// update arguments to ensure referential integrity.
|
||||
for (int i = Math.max(firstChange, arity); i < length; i++) {
|
||||
Name name = names[i];
|
||||
if (name == null) continue; // space for removed duplicate
|
||||
Name newName = name.replaceNames(originalNames, names, firstChange, i);
|
||||
if (newName != name) {
|
||||
names[i] = newName;
|
||||
if (resultName == name) {
|
||||
resultName = newName;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(inTrans());
|
||||
flags &= ~F_TRANS;
|
||||
clearDuplicatesAndNulls();
|
||||
originalNames = null;
|
||||
// If any parameters have been changed, then reorder them as needed.
|
||||
// This is a "sheep-and-goats" stable sort, pushing all non-parameters
|
||||
// to the right of all parameters.
|
||||
if (firstChange < arity) {
|
||||
Name[] exprs = new Name[arity - firstChange];
|
||||
int argp = firstChange, exprp = 0;
|
||||
for (int i = firstChange; i < arity; i++) {
|
||||
Name name = names[i];
|
||||
if (name.isParam()) {
|
||||
names[argp++] = name;
|
||||
} else {
|
||||
exprs[exprp++] = name;
|
||||
}
|
||||
}
|
||||
assert(exprp == (arity - argp));
|
||||
// copy the exprs just after the last remaining param
|
||||
System.arraycopy(exprs, 0, names, argp, exprp);
|
||||
// adjust arity
|
||||
arity -= exprp;
|
||||
}
|
||||
assert(verifyArity());
|
||||
return lambdaForm();
|
||||
}
|
||||
|
||||
private Name[] copyNamesInto(Name[] buffer) {
|
||||
System.arraycopy(names, 0, buffer, 0, length);
|
||||
Arrays.fill(buffer, length, buffer.length, null);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/** Replace any Name whose function is in oldFns with a copy
|
||||
* whose function is in the corresponding position in newFns.
|
||||
* Only do this if the arguments are exactly equal to the given.
|
||||
*/
|
||||
LambdaFormBuffer replaceFunctions(NamedFunction[] oldFns, NamedFunction[] newFns,
|
||||
Object... forArguments) {
|
||||
assert(inTrans());
|
||||
if (oldFns.length == 0) return this;
|
||||
for (int i = arity; i < length; i++) {
|
||||
Name n = names[i];
|
||||
int nfi = indexOf(n.function, oldFns);
|
||||
if (nfi >= 0 && Arrays.equals(n.arguments, forArguments)) {
|
||||
changeName(i, new Name(newFns[nfi], n.arguments));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void replaceName(int pos, Name binding) {
|
||||
assert(inTrans());
|
||||
assert(verifyArity());
|
||||
assert(pos < arity);
|
||||
Name param = names[pos];
|
||||
assert(param.isParam());
|
||||
assert(param.type == binding.type);
|
||||
changeName(pos, binding);
|
||||
}
|
||||
|
||||
/** Replace a parameter by a fresh parameter. */
|
||||
LambdaFormBuffer renameParameter(int pos, Name newParam) {
|
||||
assert(newParam.isParam());
|
||||
replaceName(pos, newParam);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Replace a parameter by a fresh expression. */
|
||||
LambdaFormBuffer replaceParameterByNewExpression(int pos, Name binding) {
|
||||
assert(!binding.isParam());
|
||||
assert(lastIndexOf(binding) < 0); // else use replaceParameterByCopy
|
||||
replaceName(pos, binding);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Replace a parameter by another parameter or expression already in the form. */
|
||||
LambdaFormBuffer replaceParameterByCopy(int pos, int valuePos) {
|
||||
assert(pos != valuePos);
|
||||
replaceName(pos, names[valuePos]);
|
||||
noteDuplicate(pos, valuePos); // temporarily, will occur twice in the names array
|
||||
return this;
|
||||
}
|
||||
|
||||
private void insertName(int pos, Name expr, boolean isParameter) {
|
||||
assert(inTrans());
|
||||
assert(verifyArity());
|
||||
assert(isParameter ? pos <= arity : pos >= arity);
|
||||
growNames(pos, 1);
|
||||
if (isParameter) arity += 1;
|
||||
changeName(pos, expr);
|
||||
}
|
||||
|
||||
/** Insert a fresh expression. */
|
||||
LambdaFormBuffer insertExpression(int pos, Name expr) {
|
||||
assert(!expr.isParam());
|
||||
insertName(pos, expr, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Insert a fresh parameter. */
|
||||
LambdaFormBuffer insertParameter(int pos, Name param) {
|
||||
assert(param.isParam());
|
||||
insertName(pos, param, true);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
858
jdkSrc/jdk8/java/lang/invoke/LambdaFormEditor.java
Normal file
858
jdkSrc/jdk8/java/lang/invoke/LambdaFormEditor.java
Normal file
@@ -0,0 +1,858 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.ref.SoftReference;
|
||||
import java.util.Arrays;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
/** Transforms on LFs.
|
||||
* A lambda-form editor can derive new LFs from its base LF.
|
||||
* The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes.
|
||||
* To support this caching, a LF has an optional pointer to its editor.
|
||||
*/
|
||||
class LambdaFormEditor {
|
||||
final LambdaForm lambdaForm;
|
||||
|
||||
private LambdaFormEditor(LambdaForm lambdaForm) {
|
||||
this.lambdaForm = lambdaForm;
|
||||
}
|
||||
|
||||
// Factory method.
|
||||
static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) {
|
||||
// TO DO: Consider placing intern logic here, to cut down on duplication.
|
||||
// lambdaForm = findPreexistingEquivalent(lambdaForm)
|
||||
|
||||
// Always use uncustomized version for editing.
|
||||
// It helps caching and customized LambdaForms reuse transformCache field to keep a link to uncustomized version.
|
||||
return new LambdaFormEditor(lambdaForm.uncustomize());
|
||||
}
|
||||
|
||||
/** A description of a cached transform, possibly associated with the result of the transform.
|
||||
* The logical content is a sequence of byte values, starting with a Kind.ordinal value.
|
||||
* The sequence is unterminated, ending with an indefinite number of zero bytes.
|
||||
* Sequences that are simple (short enough and with small enough values) pack into a 64-bit long.
|
||||
*/
|
||||
private static final class Transform extends SoftReference<LambdaForm> {
|
||||
final long packedBytes;
|
||||
final byte[] fullBytes;
|
||||
|
||||
private enum Kind {
|
||||
NO_KIND, // necessary because ordinal must be greater than zero
|
||||
BIND_ARG, ADD_ARG, DUP_ARG,
|
||||
SPREAD_ARGS,
|
||||
FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
|
||||
COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
|
||||
FOLD_ARGS, FOLD_ARGS_TO_VOID,
|
||||
PERMUTE_ARGS
|
||||
//maybe add more for guard with test, catch exception, pointwise type conversions
|
||||
}
|
||||
|
||||
private static final boolean STRESS_TEST = false; // turn on to disable most packing
|
||||
private static final int
|
||||
PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4),
|
||||
PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1,
|
||||
PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE);
|
||||
|
||||
private static long packedBytes(byte[] bytes) {
|
||||
if (bytes.length > PACKED_BYTE_MAX_LENGTH) return 0;
|
||||
long pb = 0;
|
||||
int bitset = 0;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
int b = bytes[i] & 0xFF;
|
||||
bitset |= b;
|
||||
pb |= (long)b << (i * PACKED_BYTE_SIZE);
|
||||
}
|
||||
if (!inRange(bitset))
|
||||
return 0;
|
||||
return pb;
|
||||
}
|
||||
private static long packedBytes(int b0, int b1) {
|
||||
assert(inRange(b0 | b1));
|
||||
return ( (b0 << 0*PACKED_BYTE_SIZE)
|
||||
| (b1 << 1*PACKED_BYTE_SIZE));
|
||||
}
|
||||
private static long packedBytes(int b0, int b1, int b2) {
|
||||
assert(inRange(b0 | b1 | b2));
|
||||
return ( (b0 << 0*PACKED_BYTE_SIZE)
|
||||
| (b1 << 1*PACKED_BYTE_SIZE)
|
||||
| (b2 << 2*PACKED_BYTE_SIZE));
|
||||
}
|
||||
private static long packedBytes(int b0, int b1, int b2, int b3) {
|
||||
assert(inRange(b0 | b1 | b2 | b3));
|
||||
return ( (b0 << 0*PACKED_BYTE_SIZE)
|
||||
| (b1 << 1*PACKED_BYTE_SIZE)
|
||||
| (b2 << 2*PACKED_BYTE_SIZE)
|
||||
| (b3 << 3*PACKED_BYTE_SIZE));
|
||||
}
|
||||
private static boolean inRange(int bitset) {
|
||||
assert((bitset & 0xFF) == bitset); // incoming values must fit in *unsigned* byte
|
||||
return ((bitset & ~PACKED_BYTE_MASK) == 0);
|
||||
}
|
||||
private static byte[] fullBytes(int... byteValues) {
|
||||
byte[] bytes = new byte[byteValues.length];
|
||||
int i = 0;
|
||||
for (int bv : byteValues) {
|
||||
bytes[i++] = bval(bv);
|
||||
}
|
||||
assert(packedBytes(bytes) == 0);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private byte byteAt(int i) {
|
||||
long pb = packedBytes;
|
||||
if (pb == 0) {
|
||||
if (i >= fullBytes.length) return 0;
|
||||
return fullBytes[i];
|
||||
}
|
||||
assert(fullBytes == null);
|
||||
if (i > PACKED_BYTE_MAX_LENGTH) return 0;
|
||||
int pos = (i * PACKED_BYTE_SIZE);
|
||||
return (byte)((pb >>> pos) & PACKED_BYTE_MASK);
|
||||
}
|
||||
|
||||
Kind kind() { return Kind.values()[byteAt(0)]; }
|
||||
|
||||
private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
|
||||
super(result);
|
||||
this.packedBytes = packedBytes;
|
||||
this.fullBytes = fullBytes;
|
||||
}
|
||||
private Transform(long packedBytes) {
|
||||
this(packedBytes, null, null);
|
||||
assert(packedBytes != 0);
|
||||
}
|
||||
private Transform(byte[] fullBytes) {
|
||||
this(0, fullBytes, null);
|
||||
}
|
||||
|
||||
private static byte bval(int b) {
|
||||
assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte
|
||||
return (byte)b;
|
||||
}
|
||||
private static byte bval(Kind k) {
|
||||
return bval(k.ordinal());
|
||||
}
|
||||
static Transform of(Kind k, int b1) {
|
||||
byte b0 = bval(k);
|
||||
if (inRange(b0 | b1))
|
||||
return new Transform(packedBytes(b0, b1));
|
||||
else
|
||||
return new Transform(fullBytes(b0, b1));
|
||||
}
|
||||
static Transform of(Kind k, int b1, int b2) {
|
||||
byte b0 = (byte) k.ordinal();
|
||||
if (inRange(b0 | b1 | b2))
|
||||
return new Transform(packedBytes(b0, b1, b2));
|
||||
else
|
||||
return new Transform(fullBytes(b0, b1, b2));
|
||||
}
|
||||
static Transform of(Kind k, int b1, int b2, int b3) {
|
||||
byte b0 = (byte) k.ordinal();
|
||||
if (inRange(b0 | b1 | b2 | b3))
|
||||
return new Transform(packedBytes(b0, b1, b2, b3));
|
||||
else
|
||||
return new Transform(fullBytes(b0, b1, b2, b3));
|
||||
}
|
||||
private static final byte[] NO_BYTES = {};
|
||||
static Transform of(Kind k, int... b123) {
|
||||
return ofBothArrays(k, b123, NO_BYTES);
|
||||
}
|
||||
static Transform of(Kind k, int b1, byte[] b234) {
|
||||
return ofBothArrays(k, new int[]{ b1 }, b234);
|
||||
}
|
||||
static Transform of(Kind k, int b1, int b2, byte[] b345) {
|
||||
return ofBothArrays(k, new int[]{ b1, b2 }, b345);
|
||||
}
|
||||
private static Transform ofBothArrays(Kind k, int[] b123, byte[] b456) {
|
||||
byte[] fullBytes = new byte[1 + b123.length + b456.length];
|
||||
int i = 0;
|
||||
fullBytes[i++] = bval(k);
|
||||
for (int bv : b123) {
|
||||
fullBytes[i++] = bval(bv);
|
||||
}
|
||||
for (byte bv : b456) {
|
||||
fullBytes[i++] = bv;
|
||||
}
|
||||
long packedBytes = packedBytes(fullBytes);
|
||||
if (packedBytes != 0)
|
||||
return new Transform(packedBytes);
|
||||
else
|
||||
return new Transform(fullBytes);
|
||||
}
|
||||
|
||||
Transform withResult(LambdaForm result) {
|
||||
return new Transform(this.packedBytes, this.fullBytes, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Transform && equals((Transform)obj);
|
||||
}
|
||||
public boolean equals(Transform that) {
|
||||
return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes);
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (packedBytes != 0) {
|
||||
assert(fullBytes == null);
|
||||
return Long.hashCode(packedBytes);
|
||||
}
|
||||
return Arrays.hashCode(fullBytes);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
long bits = packedBytes;
|
||||
if (bits != 0) {
|
||||
buf.append("(");
|
||||
while (bits != 0) {
|
||||
buf.append(bits & PACKED_BYTE_MASK);
|
||||
bits >>>= PACKED_BYTE_SIZE;
|
||||
if (bits != 0) buf.append(",");
|
||||
}
|
||||
buf.append(")");
|
||||
}
|
||||
if (fullBytes != null) {
|
||||
buf.append("unpacked");
|
||||
buf.append(Arrays.toString(fullBytes));
|
||||
}
|
||||
LambdaForm result = get();
|
||||
if (result != null) {
|
||||
buf.append(" result=");
|
||||
buf.append(result);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/** Find a previously cached transform equivalent to the given one, and return its result. */
|
||||
private LambdaForm getInCache(Transform key) {
|
||||
assert(key.get() == null);
|
||||
// The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap.
|
||||
Object c = lambdaForm.transformCache;
|
||||
Transform k = null;
|
||||
if (c instanceof ConcurrentHashMap) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
|
||||
k = m.get(key);
|
||||
} else if (c == null) {
|
||||
return null;
|
||||
} else if (c instanceof Transform) {
|
||||
// one-element cache avoids overhead of an array
|
||||
Transform t = (Transform)c;
|
||||
if (t.equals(key)) k = t;
|
||||
} else {
|
||||
Transform[] ta = (Transform[])c;
|
||||
for (int i = 0; i < ta.length; i++) {
|
||||
Transform t = ta[i];
|
||||
if (t == null) break;
|
||||
if (t.equals(key)) { k = t; break; }
|
||||
}
|
||||
}
|
||||
assert(k == null || key.equals(k));
|
||||
return (k != null) ? k.get() : null;
|
||||
}
|
||||
|
||||
/** Arbitrary but reasonable limits on Transform[] size for cache. */
|
||||
private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16;
|
||||
|
||||
/** Cache a transform with its result, and return that result.
|
||||
* But if an equivalent transform has already been cached, return its result instead.
|
||||
*/
|
||||
private LambdaForm putInCache(Transform key, LambdaForm form) {
|
||||
key = key.withResult(form);
|
||||
for (int pass = 0; ; pass++) {
|
||||
Object c = lambdaForm.transformCache;
|
||||
if (c instanceof ConcurrentHashMap) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
|
||||
Transform k = m.putIfAbsent(key, key);
|
||||
if (k == null) return form;
|
||||
LambdaForm result = k.get();
|
||||
if (result != null) {
|
||||
return result;
|
||||
} else {
|
||||
if (m.replace(key, k, key)) {
|
||||
return form;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(pass == 0);
|
||||
synchronized (lambdaForm) {
|
||||
c = lambdaForm.transformCache;
|
||||
if (c instanceof ConcurrentHashMap)
|
||||
continue;
|
||||
if (c == null) {
|
||||
lambdaForm.transformCache = key;
|
||||
return form;
|
||||
}
|
||||
Transform[] ta;
|
||||
if (c instanceof Transform) {
|
||||
Transform k = (Transform)c;
|
||||
if (k.equals(key)) {
|
||||
LambdaForm result = k.get();
|
||||
if (result == null) {
|
||||
lambdaForm.transformCache = key;
|
||||
return form;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
} else if (k.get() == null) { // overwrite stale entry
|
||||
lambdaForm.transformCache = key;
|
||||
return form;
|
||||
}
|
||||
// expand one-element cache to small array
|
||||
ta = new Transform[MIN_CACHE_ARRAY_SIZE];
|
||||
ta[0] = k;
|
||||
lambdaForm.transformCache = ta;
|
||||
} else {
|
||||
// it is already expanded
|
||||
ta = (Transform[])c;
|
||||
}
|
||||
int len = ta.length;
|
||||
int stale = -1;
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
Transform k = ta[i];
|
||||
if (k == null) {
|
||||
break;
|
||||
}
|
||||
if (k.equals(key)) {
|
||||
LambdaForm result = k.get();
|
||||
if (result == null) {
|
||||
ta[i] = key;
|
||||
return form;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
} else if (stale < 0 && k.get() == null) {
|
||||
stale = i; // remember 1st stale entry index
|
||||
}
|
||||
}
|
||||
if (i < len || stale >= 0) {
|
||||
// just fall through to cache update
|
||||
} else if (len < MAX_CACHE_ARRAY_SIZE) {
|
||||
len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE);
|
||||
ta = Arrays.copyOf(ta, len);
|
||||
lambdaForm.transformCache = ta;
|
||||
} else {
|
||||
ConcurrentHashMap<Transform, Transform> m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2);
|
||||
for (Transform k : ta) {
|
||||
m.put(k, k);
|
||||
}
|
||||
lambdaForm.transformCache = m;
|
||||
// The second iteration will update for this query, concurrently.
|
||||
continue;
|
||||
}
|
||||
int idx = (stale >= 0) ? stale : i;
|
||||
ta[idx] = key;
|
||||
return form;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LambdaFormBuffer buffer() {
|
||||
return new LambdaFormBuffer(lambdaForm);
|
||||
}
|
||||
|
||||
/// Editing methods for method handles. These need to have fast paths.
|
||||
|
||||
private BoundMethodHandle.SpeciesData oldSpeciesData() {
|
||||
return BoundMethodHandle.speciesData(lambdaForm);
|
||||
}
|
||||
private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) {
|
||||
return oldSpeciesData().extendWith(type);
|
||||
}
|
||||
|
||||
BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = L_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendL(type2, form2, value);
|
||||
}
|
||||
BoundMethodHandle bindArgumentI(BoundMethodHandle mh, int pos, int value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = I_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendI(type2, form2, value);
|
||||
}
|
||||
|
||||
BoundMethodHandle bindArgumentJ(BoundMethodHandle mh, int pos, long value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = J_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendJ(type2, form2, value);
|
||||
}
|
||||
|
||||
BoundMethodHandle bindArgumentF(BoundMethodHandle mh, int pos, float value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = F_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendF(type2, form2, value);
|
||||
}
|
||||
|
||||
BoundMethodHandle bindArgumentD(BoundMethodHandle mh, int pos, double value) {
|
||||
assert(mh.speciesData() == oldSpeciesData());
|
||||
BasicType bt = D_TYPE;
|
||||
MethodType type2 = bindArgumentType(mh, pos, bt);
|
||||
LambdaForm form2 = bindArgumentForm(1+pos);
|
||||
return mh.copyWithExtendD(type2, form2, value);
|
||||
}
|
||||
|
||||
private MethodType bindArgumentType(BoundMethodHandle mh, int pos, BasicType bt) {
|
||||
assert(mh.form.uncustomize() == lambdaForm);
|
||||
assert(mh.form.names[1+pos].type == bt);
|
||||
assert(BasicType.basicType(mh.type().parameterType(pos)) == bt);
|
||||
return mh.type().dropParameterTypes(pos, pos+1);
|
||||
}
|
||||
|
||||
/// Editing methods for lambda forms.
|
||||
// Each editing method can (potentially) cache the edited LF so that it can be reused later.
|
||||
|
||||
LambdaForm bindArgumentForm(int pos) {
|
||||
Transform key = Transform.of(Transform.Kind.BIND_ARG, pos);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos)));
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
|
||||
BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos));
|
||||
Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values
|
||||
Name newBaseAddress;
|
||||
NamedFunction getter = newData.getterFunction(oldData.fieldCount());
|
||||
|
||||
if (pos != 0) {
|
||||
// The newly created LF will run with a different BMH.
|
||||
// Switch over any pre-existing BMH field references to the new BMH class.
|
||||
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
|
||||
newBaseAddress = oldBaseAddress.withConstraint(newData);
|
||||
buf.renameParameter(0, newBaseAddress);
|
||||
buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress));
|
||||
} else {
|
||||
// cannot bind the MH arg itself, unless oldData is empty
|
||||
assert(oldData == BoundMethodHandle.SpeciesData.EMPTY);
|
||||
newBaseAddress = new Name(L_TYPE).withConstraint(newData);
|
||||
buf.replaceParameterByNewExpression(0, new Name(getter, newBaseAddress));
|
||||
buf.insertParameter(0, newBaseAddress);
|
||||
}
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm addArgumentForm(int pos, BasicType type) {
|
||||
Transform key = Transform.of(Transform.Kind.ADD_ARG, pos, type.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity+1);
|
||||
assert(form.parameterType(pos) == type);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
buf.insertParameter(pos, new Name(type));
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm dupArgumentForm(int srcPos, int dstPos) {
|
||||
Transform key = Transform.of(Transform.Kind.DUP_ARG, srcPos, dstPos);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity-1);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
assert(lambdaForm.parameter(srcPos).constraint == null);
|
||||
assert(lambdaForm.parameter(dstPos).constraint == null);
|
||||
buf.replaceParameterByCopy(dstPos, srcPos);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm spreadArgumentsForm(int pos, Class<?> arrayType, int arrayLength) {
|
||||
Class<?> elementType = arrayType.getComponentType();
|
||||
Class<?> erasedArrayType = arrayType;
|
||||
if (!elementType.isPrimitive())
|
||||
erasedArrayType = Object[].class;
|
||||
BasicType bt = basicType(elementType);
|
||||
int elementTypeKey = bt.ordinal();
|
||||
if (bt.basicTypeClass() != elementType) {
|
||||
if (elementType.isPrimitive()) {
|
||||
elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
|
||||
}
|
||||
}
|
||||
Transform key = Transform.of(Transform.Kind.SPREAD_ARGS, pos, elementTypeKey, arrayLength);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - arrayLength + 1);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
assert(pos <= MethodType.MAX_JVM_ARITY);
|
||||
assert(pos + arrayLength <= lambdaForm.arity);
|
||||
assert(pos > 0); // cannot spread the MH arg itself
|
||||
|
||||
Name spreadParam = new Name(L_TYPE);
|
||||
Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength);
|
||||
|
||||
// insert the new expressions
|
||||
int exprPos = lambdaForm.arity();
|
||||
buf.insertExpression(exprPos++, checkSpread);
|
||||
// adjust the arguments
|
||||
MethodHandle aload = MethodHandles.arrayElementGetter(erasedArrayType);
|
||||
for (int i = 0; i < arrayLength; i++) {
|
||||
Name loadArgument = new Name(aload, spreadParam, i);
|
||||
buf.insertExpression(exprPos + i, loadArgument);
|
||||
buf.replaceParameterByCopy(pos + i, exprPos + i);
|
||||
}
|
||||
buf.insertParameter(pos, spreadParam);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm collectArgumentsForm(int pos, MethodType collectorType) {
|
||||
int collectorArity = collectorType.parameterCount();
|
||||
boolean dropResult = (collectorType.returnType() == void.class);
|
||||
if (collectorArity == 1 && !dropResult) {
|
||||
return filterArgumentForm(pos, basicType(collectorType.parameterType(0)));
|
||||
}
|
||||
BasicType[] newTypes = BasicType.basicTypes(collectorType.parameterList());
|
||||
Transform.Kind kind = (dropResult
|
||||
? Transform.Kind.COLLECT_ARGS_TO_VOID
|
||||
: Transform.Kind.COLLECT_ARGS);
|
||||
if (dropResult && collectorArity == 0) pos = 1; // pure side effect
|
||||
Transform key = Transform.of(kind, pos, collectorArity, BasicType.basicTypesOrd(newTypes));
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1) + collectorArity);
|
||||
return form;
|
||||
}
|
||||
form = makeArgumentCombinationForm(pos, collectorType, false, dropResult);
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm collectArgumentArrayForm(int pos, MethodHandle arrayCollector) {
|
||||
MethodType collectorType = arrayCollector.type();
|
||||
int collectorArity = collectorType.parameterCount();
|
||||
assert(arrayCollector.intrinsicName() == Intrinsic.NEW_ARRAY);
|
||||
Class<?> arrayType = collectorType.returnType();
|
||||
Class<?> elementType = arrayType.getComponentType();
|
||||
BasicType argType = basicType(elementType);
|
||||
int argTypeKey = argType.ordinal();
|
||||
if (argType.basicTypeClass() != elementType) {
|
||||
// return null if it requires more metadata (like String[].class)
|
||||
if (!elementType.isPrimitive())
|
||||
return null;
|
||||
argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
|
||||
}
|
||||
assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType)));
|
||||
Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY;
|
||||
Transform key = Transform.of(kind, pos, collectorArity, argTypeKey);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - 1 + collectorArity);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
assert(pos + 1 <= lambdaForm.arity);
|
||||
assert(pos > 0); // cannot filter the MH arg itself
|
||||
|
||||
Name[] newParams = new Name[collectorArity];
|
||||
for (int i = 0; i < collectorArity; i++) {
|
||||
newParams[i] = new Name(pos + i, argType);
|
||||
}
|
||||
Name callCombiner = new Name(arrayCollector, (Object[]) /*...*/ newParams);
|
||||
|
||||
// insert the new expression
|
||||
int exprPos = lambdaForm.arity();
|
||||
buf.insertExpression(exprPos, callCombiner);
|
||||
|
||||
// insert new arguments
|
||||
int argPos = pos + 1; // skip result parameter
|
||||
for (Name newParam : newParams) {
|
||||
buf.insertParameter(argPos++, newParam);
|
||||
}
|
||||
assert(buf.lastIndexOf(callCombiner) == exprPos+newParams.length);
|
||||
buf.replaceParameterByCopy(pos, exprPos+newParams.length);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm filterArgumentForm(int pos, BasicType newType) {
|
||||
Transform key = Transform.of(Transform.Kind.FILTER_ARG, pos, newType.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity);
|
||||
assert(form.parameterType(pos) == newType);
|
||||
return form;
|
||||
}
|
||||
|
||||
BasicType oldType = lambdaForm.parameterType(pos);
|
||||
MethodType filterType = MethodType.methodType(oldType.basicTypeClass(),
|
||||
newType.basicTypeClass());
|
||||
form = makeArgumentCombinationForm(pos, filterType, false, false);
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
private LambdaForm makeArgumentCombinationForm(int pos,
|
||||
MethodType combinerType,
|
||||
boolean keepArguments, boolean dropResult) {
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
int combinerArity = combinerType.parameterCount();
|
||||
int resultArity = (dropResult ? 0 : 1);
|
||||
|
||||
assert(pos <= MethodType.MAX_JVM_ARITY);
|
||||
assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= lambdaForm.arity);
|
||||
assert(pos > 0); // cannot filter the MH arg itself
|
||||
assert(combinerType == combinerType.basicType());
|
||||
assert(combinerType.returnType() != void.class || dropResult);
|
||||
|
||||
BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
|
||||
BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE);
|
||||
|
||||
// The newly created LF will run with a different BMH.
|
||||
// Switch over any pre-existing BMH field references to the new BMH class.
|
||||
Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values
|
||||
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
|
||||
Name newBaseAddress = oldBaseAddress.withConstraint(newData);
|
||||
buf.renameParameter(0, newBaseAddress);
|
||||
|
||||
Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
|
||||
Object[] combinerArgs = new Object[1 + combinerArity];
|
||||
combinerArgs[0] = getCombiner;
|
||||
Name[] newParams;
|
||||
if (keepArguments) {
|
||||
newParams = new Name[0];
|
||||
System.arraycopy(lambdaForm.names, pos + resultArity,
|
||||
combinerArgs, 1, combinerArity);
|
||||
} else {
|
||||
newParams = new Name[combinerArity];
|
||||
BasicType[] newTypes = basicTypes(combinerType.parameterList());
|
||||
for (int i = 0; i < newTypes.length; i++) {
|
||||
newParams[i] = new Name(pos + i, newTypes[i]);
|
||||
}
|
||||
System.arraycopy(newParams, 0,
|
||||
combinerArgs, 1, combinerArity);
|
||||
}
|
||||
Name callCombiner = new Name(combinerType, combinerArgs);
|
||||
|
||||
// insert the two new expressions
|
||||
int exprPos = lambdaForm.arity();
|
||||
buf.insertExpression(exprPos+0, getCombiner);
|
||||
buf.insertExpression(exprPos+1, callCombiner);
|
||||
|
||||
// insert new arguments, if needed
|
||||
int argPos = pos + resultArity; // skip result parameter
|
||||
for (Name newParam : newParams) {
|
||||
buf.insertParameter(argPos++, newParam);
|
||||
}
|
||||
assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length);
|
||||
if (!dropResult) {
|
||||
buf.replaceParameterByCopy(pos, exprPos+1+newParams.length);
|
||||
}
|
||||
|
||||
return buf.endEdit();
|
||||
}
|
||||
|
||||
LambdaForm filterReturnForm(BasicType newType, boolean constantZero) {
|
||||
Transform.Kind kind = (constantZero ? Transform.Kind.FILTER_RETURN_TO_ZERO : Transform.Kind.FILTER_RETURN);
|
||||
Transform key = Transform.of(kind, newType.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity);
|
||||
assert(form.returnType() == newType);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
int insPos = lambdaForm.names.length;
|
||||
Name callFilter;
|
||||
if (constantZero) {
|
||||
// Synthesize a constant zero value for the given type.
|
||||
if (newType == V_TYPE)
|
||||
callFilter = null;
|
||||
else
|
||||
callFilter = new Name(constantZero(newType));
|
||||
} else {
|
||||
BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
|
||||
BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE);
|
||||
|
||||
// The newly created LF will run with a different BMH.
|
||||
// Switch over any pre-existing BMH field references to the new BMH class.
|
||||
Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values
|
||||
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
|
||||
Name newBaseAddress = oldBaseAddress.withConstraint(newData);
|
||||
buf.renameParameter(0, newBaseAddress);
|
||||
|
||||
Name getFilter = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
|
||||
buf.insertExpression(insPos++, getFilter);
|
||||
BasicType oldType = lambdaForm.returnType();
|
||||
if (oldType == V_TYPE) {
|
||||
MethodType filterType = MethodType.methodType(newType.basicTypeClass());
|
||||
callFilter = new Name(filterType, getFilter);
|
||||
} else {
|
||||
MethodType filterType = MethodType.methodType(newType.basicTypeClass(), oldType.basicTypeClass());
|
||||
callFilter = new Name(filterType, getFilter, lambdaForm.names[lambdaForm.result]);
|
||||
}
|
||||
}
|
||||
|
||||
if (callFilter != null)
|
||||
buf.insertExpression(insPos++, callFilter);
|
||||
buf.setResult(callFilter);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) {
|
||||
int combinerArity = combinerType.parameterCount();
|
||||
Transform.Kind kind = (dropResult ? Transform.Kind.FOLD_ARGS_TO_VOID : Transform.Kind.FOLD_ARGS);
|
||||
Transform key = Transform.of(kind, foldPos, combinerArity);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - (kind == Transform.Kind.FOLD_ARGS ? 1 : 0));
|
||||
return form;
|
||||
}
|
||||
form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult);
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm permuteArgumentsForm(int skip, int[] reorder) {
|
||||
assert(skip == 1); // skip only the leading MH argument, names[0]
|
||||
int length = lambdaForm.names.length;
|
||||
int outArgs = reorder.length;
|
||||
int inTypes = 0;
|
||||
boolean nullPerm = true;
|
||||
for (int i = 0; i < reorder.length; i++) {
|
||||
int inArg = reorder[i];
|
||||
if (inArg != i) nullPerm = false;
|
||||
inTypes = Math.max(inTypes, inArg+1);
|
||||
}
|
||||
assert(skip + reorder.length == lambdaForm.arity);
|
||||
if (nullPerm) return lambdaForm; // do not bother to cache
|
||||
Transform key = Transform.of(Transform.Kind.PERMUTE_ARGS, reorder);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == skip+inTypes) : form;
|
||||
return form;
|
||||
}
|
||||
|
||||
BasicType[] types = new BasicType[inTypes];
|
||||
for (int i = 0; i < outArgs; i++) {
|
||||
int inArg = reorder[i];
|
||||
types[inArg] = lambdaForm.names[skip + i].type;
|
||||
}
|
||||
assert (skip + outArgs == lambdaForm.arity);
|
||||
assert (permutedTypesMatch(reorder, types, lambdaForm.names, skip));
|
||||
int pos = 0;
|
||||
while (pos < outArgs && reorder[pos] == pos) {
|
||||
pos += 1;
|
||||
}
|
||||
Name[] names2 = new Name[length - outArgs + inTypes];
|
||||
System.arraycopy(lambdaForm.names, 0, names2, 0, skip + pos);
|
||||
int bodyLength = length - lambdaForm.arity;
|
||||
System.arraycopy(lambdaForm.names, skip + outArgs, names2, skip + inTypes, bodyLength);
|
||||
int arity2 = names2.length - bodyLength;
|
||||
int result2 = lambdaForm.result;
|
||||
if (result2 >= skip) {
|
||||
if (result2 < skip + outArgs) {
|
||||
result2 = reorder[result2 - skip] + skip;
|
||||
} else {
|
||||
result2 = result2 - outArgs + inTypes;
|
||||
}
|
||||
}
|
||||
for (int j = pos; j < outArgs; j++) {
|
||||
Name n = lambdaForm.names[skip + j];
|
||||
int i = reorder[j];
|
||||
Name n2 = names2[skip + i];
|
||||
if (n2 == null) {
|
||||
names2[skip + i] = n2 = new Name(types[i]);
|
||||
} else {
|
||||
assert (n2.type == types[i]);
|
||||
}
|
||||
for (int k = arity2; k < names2.length; k++) {
|
||||
names2[k] = names2[k].replaceName(n, n2);
|
||||
}
|
||||
}
|
||||
for (int i = skip + pos; i < arity2; i++) {
|
||||
if (names2[i] == null) {
|
||||
names2[i] = argument(i, types[i - skip]);
|
||||
}
|
||||
}
|
||||
for (int j = lambdaForm.arity; j < lambdaForm.names.length; j++) {
|
||||
int i = j - lambdaForm.arity + arity2;
|
||||
Name n = lambdaForm.names[j];
|
||||
Name n2 = names2[i];
|
||||
if (n != n2) {
|
||||
for (int k = i + 1; k < names2.length; k++) {
|
||||
names2[k] = names2[k].replaceName(n, n2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form = new LambdaForm(lambdaForm.debugName, arity2, names2, result2);
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) {
|
||||
for (int i = 0; i < reorder.length; i++) {
|
||||
assert (names[skip + i].isParam());
|
||||
assert (names[skip + i].type == types[reorder[i]]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
476
jdkSrc/jdk8/java/lang/invoke/LambdaMetafactory.java
Normal file
476
jdkSrc/jdk8/java/lang/invoke/LambdaMetafactory.java
Normal file
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>Methods to facilitate the creation of simple "function objects" that
|
||||
* implement one or more interfaces by delegation to a provided {@link MethodHandle},
|
||||
* possibly after type adaptation and partial evaluation of arguments. These
|
||||
* methods are typically used as <em>bootstrap methods</em> for {@code invokedynamic}
|
||||
* call sites, to support the <em>lambda expression</em> and <em>method
|
||||
* reference expression</em> features of the Java Programming Language.
|
||||
*
|
||||
* <p>Indirect access to the behavior specified by the provided {@code MethodHandle}
|
||||
* proceeds in order through three phases:
|
||||
* <ul>
|
||||
* <li><em>Linkage</em> occurs when the methods in this class are invoked.
|
||||
* They take as arguments an interface to be implemented (typically a
|
||||
* <em>functional interface</em>, one with a single abstract method), a
|
||||
* name and signature of a method from that interface to be implemented, a
|
||||
* method handle describing the desired implementation behavior
|
||||
* for that method, and possibly other additional metadata, and produce a
|
||||
* {@link CallSite} whose target can be used to create suitable function
|
||||
* objects. Linkage may involve dynamically loading a new class that
|
||||
* implements the target interface. The {@code CallSite} can be considered a
|
||||
* "factory" for function objects and so these linkage methods are referred
|
||||
* to as "metafactories".</li>
|
||||
*
|
||||
* <li><em>Capture</em> occurs when the {@code CallSite}'s target is
|
||||
* invoked, typically through an {@code invokedynamic} call site,
|
||||
* producing a function object. This may occur many times for
|
||||
* a single factory {@code CallSite}. Capture may involve allocation of a
|
||||
* new function object, or may return an existing function object. The
|
||||
* behavior {@code MethodHandle} may have additional parameters beyond those
|
||||
* of the specified interface method; these are referred to as <em>captured
|
||||
* parameters</em>, which must be provided as arguments to the
|
||||
* {@code CallSite} target, and which may be early-bound to the behavior
|
||||
* {@code MethodHandle}. The number of captured parameters and their types
|
||||
* are determined during linkage.</li>
|
||||
*
|
||||
* <li><em>Invocation</em> occurs when an implemented interface method
|
||||
* is invoked on a function object. This may occur many times for a single
|
||||
* function object. The method referenced by the behavior {@code MethodHandle}
|
||||
* is invoked with the captured arguments and any additional arguments
|
||||
* provided on invocation, as if by {@link MethodHandle#invoke(Object...)}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>It is sometimes useful to restrict the set of inputs or results permitted
|
||||
* at invocation. For example, when the generic interface {@code Predicate<T>}
|
||||
* is parameterized as {@code Predicate<String>}, the input must be a
|
||||
* {@code String}, even though the method to implement allows any {@code Object}.
|
||||
* At linkage time, an additional {@link MethodType} parameter describes the
|
||||
* "instantiated" method type; on invocation, the arguments and eventual result
|
||||
* are checked against this {@code MethodType}.
|
||||
*
|
||||
* <p>This class provides two forms of linkage methods: a standard version
|
||||
* ({@link #metafactory(MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType)})
|
||||
* using an optimized protocol, and an alternate version
|
||||
* {@link #altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)}).
|
||||
* The alternate version is a generalization of the standard version, providing
|
||||
* additional control over the behavior of the generated function objects via
|
||||
* flags and additional arguments. The alternate version adds the ability to
|
||||
* manage the following attributes of function objects:
|
||||
*
|
||||
* <ul>
|
||||
* <li><em>Bridging.</em> It is sometimes useful to implement multiple
|
||||
* variations of the method signature, involving argument or return type
|
||||
* adaptation. This occurs when multiple distinct VM signatures for a method
|
||||
* are logically considered to be the same method by the language. The
|
||||
* flag {@code FLAG_BRIDGES} indicates that a list of additional
|
||||
* {@code MethodType}s will be provided, each of which will be implemented
|
||||
* by the resulting function object. These methods will share the same
|
||||
* name and instantiated type.</li>
|
||||
*
|
||||
* <li><em>Multiple interfaces.</em> If needed, more than one interface
|
||||
* can be implemented by the function object. (These additional interfaces
|
||||
* are typically marker interfaces with no methods.) The flag {@code FLAG_MARKERS}
|
||||
* indicates that a list of additional interfaces will be provided, each of
|
||||
* which should be implemented by the resulting function object.</li>
|
||||
*
|
||||
* <li><em>Serializability.</em> The generated function objects do not
|
||||
* generally support serialization. If desired, {@code FLAG_SERIALIZABLE}
|
||||
* can be used to indicate that the function objects should be serializable.
|
||||
* Serializable function objects will use, as their serialized form,
|
||||
* instances of the class {@code SerializedLambda}, which requires additional
|
||||
* assistance from the capturing class (the class described by the
|
||||
* {@link MethodHandles.Lookup} parameter {@code caller}); see
|
||||
* {@link SerializedLambda} for details.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Assume the linkage arguments are as follows:
|
||||
* <ul>
|
||||
* <li>{@code invokedType} (describing the {@code CallSite} signature) has
|
||||
* K parameters of types (D1..Dk) and return type Rd;</li>
|
||||
* <li>{@code samMethodType} (describing the implemented method type) has N
|
||||
* parameters, of types (U1..Un) and return type Ru;</li>
|
||||
* <li>{@code implMethod} (the {@code MethodHandle} providing the
|
||||
* implementation has M parameters, of types (A1..Am) and return type Ra
|
||||
* (if the method describes an instance method, the method type of this
|
||||
* method handle already includes an extra first argument corresponding to
|
||||
* the receiver);</li>
|
||||
* <li>{@code instantiatedMethodType} (allowing restrictions on invocation)
|
||||
* has N parameters, of types (T1..Tn) and return type Rt.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Then the following linkage invariants must hold:
|
||||
* <ul>
|
||||
* <li>Rd is an interface</li>
|
||||
* <li>{@code implMethod} is a <em>direct method handle</em></li>
|
||||
* <li>{@code samMethodType} and {@code instantiatedMethodType} have the same
|
||||
* arity N, and for i=1..N, Ti and Ui are the same type, or Ti and Ui are
|
||||
* both reference types and Ti is a subtype of Ui</li>
|
||||
* <li>Either Rt and Ru are the same type, or both are reference types and
|
||||
* Rt is a subtype of Ru</li>
|
||||
* <li>K + N = M</li>
|
||||
* <li>For i=1..K, Di = Ai</li>
|
||||
* <li>For i=1..N, Ti is adaptable to Aj, where j=i+k</li>
|
||||
* <li>The return type Rt is void, or the return type Ra is not void and is
|
||||
* adaptable to Rt</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Further, at capture time, if {@code implMethod} corresponds to an instance
|
||||
* method, and there are any capture arguments ({@code K > 0}), then the first
|
||||
* capture argument (corresponding to the receiver) must be non-null.
|
||||
*
|
||||
* <p>A type Q is considered adaptable to S as follows:
|
||||
* <table summary="adaptable types">
|
||||
* <tr><th>Q</th><th>S</th><th>Link-time checks</th><th>Invocation-time checks</th></tr>
|
||||
* <tr>
|
||||
* <td>Primitive</td><td>Primitive</td>
|
||||
* <td>Q can be converted to S via a primitive widening conversion</td>
|
||||
* <td>None</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Primitive</td><td>Reference</td>
|
||||
* <td>S is a supertype of the Wrapper(Q)</td>
|
||||
* <td>Cast from Wrapper(Q) to S</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Reference</td><td>Primitive</td>
|
||||
* <td>for parameter types: Q is a primitive wrapper and Primitive(Q)
|
||||
* can be widened to S
|
||||
* <br>for return types: If Q is a primitive wrapper, check that
|
||||
* Primitive(Q) can be widened to S</td>
|
||||
* <td>If Q is not a primitive wrapper, cast Q to the base Wrapper(S);
|
||||
* for example Number for numeric types</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Reference</td><td>Reference</td>
|
||||
* <td>for parameter types: S is a supertype of Q
|
||||
* <br>for return types: none</td>
|
||||
* <td>Cast from Q to S</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @apiNote These linkage methods are designed to support the evaluation
|
||||
* of <em>lambda expressions</em> and <em>method references</em> in the Java
|
||||
* Language. For every lambda expressions or method reference in the source code,
|
||||
* there is a target type which is a functional interface. Evaluating a lambda
|
||||
* expression produces an object of its target type. The recommended mechanism
|
||||
* for evaluating lambda expressions is to desugar the lambda body to a method,
|
||||
* invoke an invokedynamic call site whose static argument list describes the
|
||||
* sole method of the functional interface and the desugared implementation
|
||||
* method, and returns an object (the lambda object) that implements the target
|
||||
* type. (For method references, the implementation method is simply the
|
||||
* referenced method; no desugaring is needed.)
|
||||
*
|
||||
* <p>The argument list of the implementation method and the argument list of
|
||||
* the interface method(s) may differ in several ways. The implementation
|
||||
* methods may have additional arguments to accommodate arguments captured by
|
||||
* the lambda expression; there may also be differences resulting from permitted
|
||||
* adaptations of arguments, such as casting, boxing, unboxing, and primitive
|
||||
* widening. (Varargs adaptations are not handled by the metafactories; these are
|
||||
* expected to be handled by the caller.)
|
||||
*
|
||||
* <p>Invokedynamic call sites have two argument lists: a static argument list
|
||||
* and a dynamic argument list. The static argument list is stored in the
|
||||
* constant pool; the dynamic argument is pushed on the operand stack at capture
|
||||
* time. The bootstrap method has access to the entire static argument list
|
||||
* (which in this case, includes information describing the implementation method,
|
||||
* the target interface, and the target interface method(s)), as well as a
|
||||
* method signature describing the number and static types (but not the values)
|
||||
* of the dynamic arguments and the static return type of the invokedynamic site.
|
||||
*
|
||||
* @implNote The implementation method is described with a method handle. In
|
||||
* theory, any method handle could be used. Currently supported are direct method
|
||||
* handles representing invocation of virtual, interface, constructor and static
|
||||
* methods.
|
||||
*/
|
||||
public class LambdaMetafactory {
|
||||
|
||||
/** Flag for alternate metafactories indicating the lambda object
|
||||
* must be serializable */
|
||||
public static final int FLAG_SERIALIZABLE = 1 << 0;
|
||||
|
||||
/**
|
||||
* Flag for alternate metafactories indicating the lambda object implements
|
||||
* other marker interfaces
|
||||
* besides Serializable
|
||||
*/
|
||||
public static final int FLAG_MARKERS = 1 << 1;
|
||||
|
||||
/**
|
||||
* Flag for alternate metafactories indicating the lambda object requires
|
||||
* additional bridge methods
|
||||
*/
|
||||
public static final int FLAG_BRIDGES = 1 << 2;
|
||||
|
||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
private static final MethodType[] EMPTY_MT_ARRAY = new MethodType[0];
|
||||
|
||||
/**
|
||||
* Facilitates the creation of simple "function objects" that implement one
|
||||
* or more interfaces by delegation to a provided {@link MethodHandle},
|
||||
* after appropriate type adaptation and partial evaluation of arguments.
|
||||
* Typically used as a <em>bootstrap method</em> for {@code invokedynamic}
|
||||
* call sites, to support the <em>lambda expression</em> and <em>method
|
||||
* reference expression</em> features of the Java Programming Language.
|
||||
*
|
||||
* <p>This is the standard, streamlined metafactory; additional flexibility
|
||||
* is provided by {@link #altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)}.
|
||||
* A general description of the behavior of this method is provided
|
||||
* {@link LambdaMetafactory above}.
|
||||
*
|
||||
* <p>When the target of the {@code CallSite} returned from this method is
|
||||
* invoked, the resulting function objects are instances of a class which
|
||||
* implements the interface named by the return type of {@code invokedType},
|
||||
* declares a method with the name given by {@code invokedName} and the
|
||||
* signature given by {@code samMethodType}. It may also override additional
|
||||
* methods from {@code Object}.
|
||||
*
|
||||
* @param caller Represents a lookup context with the accessibility
|
||||
* privileges of the caller. When used with {@code invokedynamic},
|
||||
* this is stacked automatically by the VM.
|
||||
* @param invokedName The name of the method to implement. When used with
|
||||
* {@code invokedynamic}, this is provided by the
|
||||
* {@code NameAndType} of the {@code InvokeDynamic}
|
||||
* structure and is stacked automatically by the VM.
|
||||
* @param invokedType The expected signature of the {@code CallSite}. The
|
||||
* parameter types represent the types of capture variables;
|
||||
* the return type is the interface to implement. When
|
||||
* used with {@code invokedynamic}, this is provided by
|
||||
* the {@code NameAndType} of the {@code InvokeDynamic}
|
||||
* structure and is stacked automatically by the VM.
|
||||
* In the event that the implementation method is an
|
||||
* instance method and this signature has any parameters,
|
||||
* the first parameter in the invocation signature must
|
||||
* correspond to the receiver.
|
||||
* @param samMethodType Signature and return type of method to be implemented
|
||||
* by the function object.
|
||||
* @param implMethod A direct method handle describing the implementation
|
||||
* method which should be called (with suitable adaptation
|
||||
* of argument types, return types, and with captured
|
||||
* arguments prepended to the invocation arguments) at
|
||||
* invocation time.
|
||||
* @param instantiatedMethodType The signature and return type that should
|
||||
* be enforced dynamically at invocation time.
|
||||
* This may be the same as {@code samMethodType},
|
||||
* or may be a specialization of it.
|
||||
* @return a CallSite whose target can be used to perform capture, generating
|
||||
* instances of the interface named by {@code invokedType}
|
||||
* @throws LambdaConversionException If any of the linkage invariants
|
||||
* described {@link LambdaMetafactory above}
|
||||
* are violated
|
||||
*/
|
||||
public static CallSite metafactory(MethodHandles.Lookup caller,
|
||||
String invokedName,
|
||||
MethodType invokedType,
|
||||
MethodType samMethodType,
|
||||
MethodHandle implMethod,
|
||||
MethodType instantiatedMethodType)
|
||||
throws LambdaConversionException {
|
||||
AbstractValidatingLambdaMetafactory mf;
|
||||
mf = new InnerClassLambdaMetafactory(caller, invokedType,
|
||||
invokedName, samMethodType,
|
||||
implMethod, instantiatedMethodType,
|
||||
false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
|
||||
mf.validateMetafactoryArgs();
|
||||
return mf.buildCallSite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Facilitates the creation of simple "function objects" that implement one
|
||||
* or more interfaces by delegation to a provided {@link MethodHandle},
|
||||
* after appropriate type adaptation and partial evaluation of arguments.
|
||||
* Typically used as a <em>bootstrap method</em> for {@code invokedynamic}
|
||||
* call sites, to support the <em>lambda expression</em> and <em>method
|
||||
* reference expression</em> features of the Java Programming Language.
|
||||
*
|
||||
* <p>This is the general, more flexible metafactory; a streamlined version
|
||||
* is provided by {@link #metafactory(java.lang.invoke.MethodHandles.Lookup,
|
||||
* String, MethodType, MethodType, MethodHandle, MethodType)}.
|
||||
* A general description of the behavior of this method is provided
|
||||
* {@link LambdaMetafactory above}.
|
||||
*
|
||||
* <p>The argument list for this method includes three fixed parameters,
|
||||
* corresponding to the parameters automatically stacked by the VM for the
|
||||
* bootstrap method in an {@code invokedynamic} invocation, and an {@code Object[]}
|
||||
* parameter that contains additional parameters. The declared argument
|
||||
* list for this method is:
|
||||
*
|
||||
* <pre>{@code
|
||||
* CallSite altMetafactory(MethodHandles.Lookup caller,
|
||||
* String invokedName,
|
||||
* MethodType invokedType,
|
||||
* Object... args)
|
||||
* }</pre>
|
||||
*
|
||||
* <p>but it behaves as if the argument list is as follows:
|
||||
*
|
||||
* <pre>{@code
|
||||
* CallSite altMetafactory(MethodHandles.Lookup caller,
|
||||
* String invokedName,
|
||||
* MethodType invokedType,
|
||||
* MethodType samMethodType,
|
||||
* MethodHandle implMethod,
|
||||
* MethodType instantiatedMethodType,
|
||||
* int flags,
|
||||
* int markerInterfaceCount, // IF flags has MARKERS set
|
||||
* Class... markerInterfaces, // IF flags has MARKERS set
|
||||
* int bridgeCount, // IF flags has BRIDGES set
|
||||
* MethodType... bridges // IF flags has BRIDGES set
|
||||
* )
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Arguments that appear in the argument list for
|
||||
* {@link #metafactory(MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType)}
|
||||
* have the same specification as in that method. The additional arguments
|
||||
* are interpreted as follows:
|
||||
* <ul>
|
||||
* <li>{@code flags} indicates additional options; this is a bitwise
|
||||
* OR of desired flags. Defined flags are {@link #FLAG_BRIDGES},
|
||||
* {@link #FLAG_MARKERS}, and {@link #FLAG_SERIALIZABLE}.</li>
|
||||
* <li>{@code markerInterfaceCount} is the number of additional interfaces
|
||||
* the function object should implement, and is present if and only if the
|
||||
* {@code FLAG_MARKERS} flag is set.</li>
|
||||
* <li>{@code markerInterfaces} is a variable-length list of additional
|
||||
* interfaces to implement, whose length equals {@code markerInterfaceCount},
|
||||
* and is present if and only if the {@code FLAG_MARKERS} flag is set.</li>
|
||||
* <li>{@code bridgeCount} is the number of additional method signatures
|
||||
* the function object should implement, and is present if and only if
|
||||
* the {@code FLAG_BRIDGES} flag is set.</li>
|
||||
* <li>{@code bridges} is a variable-length list of additional
|
||||
* methods signatures to implement, whose length equals {@code bridgeCount},
|
||||
* and is present if and only if the {@code FLAG_BRIDGES} flag is set.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Each class named by {@code markerInterfaces} is subject to the same
|
||||
* restrictions as {@code Rd}, the return type of {@code invokedType},
|
||||
* as described {@link LambdaMetafactory above}. Each {@code MethodType}
|
||||
* named by {@code bridges} is subject to the same restrictions as
|
||||
* {@code samMethodType}, as described {@link LambdaMetafactory above}.
|
||||
*
|
||||
* <p>When FLAG_SERIALIZABLE is set in {@code flags}, the function objects
|
||||
* will implement {@code Serializable}, and will have a {@code writeReplace}
|
||||
* method that returns an appropriate {@link SerializedLambda}. The
|
||||
* {@code caller} class must have an appropriate {@code $deserializeLambda$}
|
||||
* method, as described in {@link SerializedLambda}.
|
||||
*
|
||||
* <p>When the target of the {@code CallSite} returned from this method is
|
||||
* invoked, the resulting function objects are instances of a class with
|
||||
* the following properties:
|
||||
* <ul>
|
||||
* <li>The class implements the interface named by the return type
|
||||
* of {@code invokedType} and any interfaces named by {@code markerInterfaces}</li>
|
||||
* <li>The class declares methods with the name given by {@code invokedName},
|
||||
* and the signature given by {@code samMethodType} and additional signatures
|
||||
* given by {@code bridges}</li>
|
||||
* <li>The class may override methods from {@code Object}, and may
|
||||
* implement methods related to serialization.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param caller Represents a lookup context with the accessibility
|
||||
* privileges of the caller. When used with {@code invokedynamic},
|
||||
* this is stacked automatically by the VM.
|
||||
* @param invokedName The name of the method to implement. When used with
|
||||
* {@code invokedynamic}, this is provided by the
|
||||
* {@code NameAndType} of the {@code InvokeDynamic}
|
||||
* structure and is stacked automatically by the VM.
|
||||
* @param invokedType The expected signature of the {@code CallSite}. The
|
||||
* parameter types represent the types of capture variables;
|
||||
* the return type is the interface to implement. When
|
||||
* used with {@code invokedynamic}, this is provided by
|
||||
* the {@code NameAndType} of the {@code InvokeDynamic}
|
||||
* structure and is stacked automatically by the VM.
|
||||
* In the event that the implementation method is an
|
||||
* instance method and this signature has any parameters,
|
||||
* the first parameter in the invocation signature must
|
||||
* correspond to the receiver.
|
||||
* @param args An {@code Object[]} array containing the required
|
||||
* arguments {@code samMethodType}, {@code implMethod},
|
||||
* {@code instantiatedMethodType}, {@code flags}, and any
|
||||
* optional arguments, as described
|
||||
* {@link #altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)} above}
|
||||
* @return a CallSite whose target can be used to perform capture, generating
|
||||
* instances of the interface named by {@code invokedType}
|
||||
* @throws LambdaConversionException If any of the linkage invariants
|
||||
* described {@link LambdaMetafactory above}
|
||||
* are violated
|
||||
*/
|
||||
public static CallSite altMetafactory(MethodHandles.Lookup caller,
|
||||
String invokedName,
|
||||
MethodType invokedType,
|
||||
Object... args)
|
||||
throws LambdaConversionException {
|
||||
MethodType samMethodType = (MethodType)args[0];
|
||||
MethodHandle implMethod = (MethodHandle)args[1];
|
||||
MethodType instantiatedMethodType = (MethodType)args[2];
|
||||
int flags = (Integer) args[3];
|
||||
Class<?>[] markerInterfaces;
|
||||
MethodType[] bridges;
|
||||
int argIndex = 4;
|
||||
if ((flags & FLAG_MARKERS) != 0) {
|
||||
int markerCount = (Integer) args[argIndex++];
|
||||
markerInterfaces = new Class<?>[markerCount];
|
||||
System.arraycopy(args, argIndex, markerInterfaces, 0, markerCount);
|
||||
argIndex += markerCount;
|
||||
}
|
||||
else
|
||||
markerInterfaces = EMPTY_CLASS_ARRAY;
|
||||
if ((flags & FLAG_BRIDGES) != 0) {
|
||||
int bridgeCount = (Integer) args[argIndex++];
|
||||
bridges = new MethodType[bridgeCount];
|
||||
System.arraycopy(args, argIndex, bridges, 0, bridgeCount);
|
||||
argIndex += bridgeCount;
|
||||
}
|
||||
else
|
||||
bridges = EMPTY_MT_ARRAY;
|
||||
|
||||
boolean isSerializable = ((flags & FLAG_SERIALIZABLE) != 0);
|
||||
if (isSerializable) {
|
||||
boolean foundSerializableSupertype = Serializable.class.isAssignableFrom(invokedType.returnType());
|
||||
for (Class<?> c : markerInterfaces)
|
||||
foundSerializableSupertype |= Serializable.class.isAssignableFrom(c);
|
||||
if (!foundSerializableSupertype) {
|
||||
markerInterfaces = Arrays.copyOf(markerInterfaces, markerInterfaces.length + 1);
|
||||
markerInterfaces[markerInterfaces.length-1] = Serializable.class;
|
||||
}
|
||||
}
|
||||
|
||||
AbstractValidatingLambdaMetafactory mf
|
||||
= new InnerClassLambdaMetafactory(caller, invokedType,
|
||||
invokedName, samMethodType,
|
||||
implMethod,
|
||||
instantiatedMethodType,
|
||||
isSerializable,
|
||||
markerInterfaces, bridges);
|
||||
mf.validateMetafactoryArgs();
|
||||
return mf.buildCallSite();
|
||||
}
|
||||
}
|
||||
1090
jdkSrc/jdk8/java/lang/invoke/MemberName.java
Normal file
1090
jdkSrc/jdk8/java/lang/invoke/MemberName.java
Normal file
File diff suppressed because it is too large
Load Diff
1456
jdkSrc/jdk8/java/lang/invoke/MethodHandle.java
Normal file
1456
jdkSrc/jdk8/java/lang/invoke/MethodHandle.java
Normal file
File diff suppressed because it is too large
Load Diff
1664
jdkSrc/jdk8/java/lang/invoke/MethodHandleImpl.java
Normal file
1664
jdkSrc/jdk8/java/lang/invoke/MethodHandleImpl.java
Normal file
File diff suppressed because it is too large
Load Diff
284
jdkSrc/jdk8/java/lang/invoke/MethodHandleInfo.java
Normal file
284
jdkSrc/jdk8/java/lang/invoke/MethodHandleInfo.java
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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.reflect.*;
|
||||
import java.util.*;
|
||||
import java.lang.invoke.MethodHandleNatives.Constants;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* A symbolic reference obtained by cracking a direct method handle
|
||||
* into its consitutent symbolic parts.
|
||||
* To crack a direct method handle, call {@link Lookup#revealDirect Lookup.revealDirect}.
|
||||
* <h1><a name="directmh"></a>Direct Method Handles</h1>
|
||||
* A <em>direct method handle</em> represents a method, constructor, or field without
|
||||
* any intervening argument bindings or other transformations.
|
||||
* The method, constructor, or field referred to by a direct method handle is called
|
||||
* its <em>underlying member</em>.
|
||||
* Direct method handles may be obtained in any of these ways:
|
||||
* <ul>
|
||||
* <li>By executing an {@code ldc} instruction on a {@code CONSTANT_MethodHandle} constant.
|
||||
* (See the Java Virtual Machine Specification, sections 4.4.8 and 5.4.3.)
|
||||
* <li>By calling one of the <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>,
|
||||
* such as {@link Lookup#findVirtual Lookup.findVirtual},
|
||||
* to resolve a symbolic reference into a method handle.
|
||||
* A symbolic reference consists of a class, name string, and type.
|
||||
* <li>By calling the factory method {@link Lookup#unreflect Lookup.unreflect}
|
||||
* or {@link Lookup#unreflectSpecial Lookup.unreflectSpecial}
|
||||
* to convert a {@link Method} into a method handle.
|
||||
* <li>By calling the factory method {@link Lookup#unreflectConstructor Lookup.unreflectConstructor}
|
||||
* to convert a {@link Constructor} into a method handle.
|
||||
* <li>By calling the factory method {@link Lookup#unreflectGetter Lookup.unreflectGetter}
|
||||
* or {@link Lookup#unreflectSetter Lookup.unreflectSetter}
|
||||
* to convert a {@link Field} into a method handle.
|
||||
* </ul>
|
||||
*
|
||||
* <h1>Restrictions on Cracking</h1>
|
||||
* Given a suitable {@code Lookup} object, it is possible to crack any direct method handle
|
||||
* to recover a symbolic reference for the underlying method, constructor, or field.
|
||||
* Cracking must be done via a {@code Lookup} object equivalent to that which created
|
||||
* the target method handle, or which has enough access permissions to recreate
|
||||
* an equivalent method handle.
|
||||
* <p>
|
||||
* If the underlying method is <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a>,
|
||||
* the direct method handle will have been "bound" to a particular caller class, the
|
||||
* {@linkplain java.lang.invoke.MethodHandles.Lookup#lookupClass() lookup class}
|
||||
* of the lookup object used to create it.
|
||||
* Cracking this method handle with a different lookup class will fail
|
||||
* even if the underlying method is public (like {@code Class.forName}).
|
||||
* <p>
|
||||
* The requirement of lookup object matching provides a "fast fail" behavior
|
||||
* for programs which may otherwise trust erroneous revelation of a method
|
||||
* handle with symbolic information (or caller binding) from an unexpected scope.
|
||||
* Use {@link java.lang.invoke.MethodHandles#reflectAs} to override this limitation.
|
||||
*
|
||||
* <h1><a name="refkinds"></a>Reference kinds</h1>
|
||||
* The <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>
|
||||
* correspond to all major use cases for methods, constructors, and fields.
|
||||
* These use cases may be distinguished using small integers as follows:
|
||||
* <table border=1 cellpadding=5 summary="reference kinds">
|
||||
* <tr><th>reference kind</th><th>descriptive name</th><th>scope</th><th>member</th><th>behavior</th></tr>
|
||||
* <tr>
|
||||
* <td>{@code 1}</td><td>{@code REF_getField}</td><td>{@code class}</td>
|
||||
* <td>{@code FT f;}</td><td>{@code (T) this.f;}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 2}</td><td>{@code REF_getStatic}</td><td>{@code class} or {@code interface}</td>
|
||||
* <td>{@code static}<br>{@code FT f;}</td><td>{@code (T) C.f;}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 3}</td><td>{@code REF_putField}</td><td>{@code class}</td>
|
||||
* <td>{@code FT f;}</td><td>{@code this.f = x;}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 4}</td><td>{@code REF_putStatic}</td><td>{@code class}</td>
|
||||
* <td>{@code static}<br>{@code FT f;}</td><td>{@code C.f = arg;}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 5}</td><td>{@code REF_invokeVirtual}</td><td>{@code class}</td>
|
||||
* <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 6}</td><td>{@code REF_invokeStatic}</td><td>{@code class} or {@code interface}</td>
|
||||
* <td>{@code static}<br>{@code T m(A*);}</td><td>{@code (T) C.m(arg*);}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 7}</td><td>{@code REF_invokeSpecial}</td><td>{@code class} or {@code interface}</td>
|
||||
* <td>{@code T m(A*);}</td><td>{@code (T) super.m(arg*);}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 8}</td><td>{@code REF_newInvokeSpecial}</td><td>{@code class}</td>
|
||||
* <td>{@code C(A*);}</td><td>{@code new C(arg*);}</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code 9}</td><td>{@code REF_invokeInterface}</td><td>{@code interface}</td>
|
||||
* <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* @since 1.8
|
||||
*/
|
||||
public
|
||||
interface MethodHandleInfo {
|
||||
/**
|
||||
* A direct method handle reference kind,
|
||||
* as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
|
||||
*/
|
||||
public static final int
|
||||
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;
|
||||
|
||||
/**
|
||||
* Returns the reference kind of the cracked method handle, which in turn
|
||||
* determines whether the method handle's underlying member was a constructor, method, or field.
|
||||
* See the <a href="MethodHandleInfo.html#refkinds">table above</a> for definitions.
|
||||
* @return the integer code for the kind of reference used to access the underlying member
|
||||
*/
|
||||
public int getReferenceKind();
|
||||
|
||||
/**
|
||||
* Returns the class in which the cracked method handle's underlying member was defined.
|
||||
* @return the declaring class of the underlying member
|
||||
*/
|
||||
public Class<?> getDeclaringClass();
|
||||
|
||||
/**
|
||||
* Returns the name of the cracked method handle's underlying member.
|
||||
* This is {@code "<init>"} if the underlying member was a constructor,
|
||||
* else it is a simple method name or field name.
|
||||
* @return the simple name of the underlying member
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Returns the nominal type of the cracked symbolic reference, expressed as a method type.
|
||||
* If the reference is to a constructor, the return type will be {@code void}.
|
||||
* If it is to a non-static method, the method type will not mention the {@code this} parameter.
|
||||
* If it is to a field and the requested access is to read the field,
|
||||
* the method type will have no parameters and return the field type.
|
||||
* If it is to a field and the requested access is to write the field,
|
||||
* the method type will have one parameter of the field type and return {@code void}.
|
||||
* <p>
|
||||
* Note that original direct method handle may include a leading {@code this} parameter,
|
||||
* or (in the case of a constructor) will replace the {@code void} return type
|
||||
* with the constructed class.
|
||||
* The nominal type does not include any {@code this} parameter,
|
||||
* and (in the case of a constructor) will return {@code void}.
|
||||
* @return the type of the underlying member, expressed as a method type
|
||||
*/
|
||||
public MethodType getMethodType();
|
||||
|
||||
// Utility methods.
|
||||
// NOTE: class/name/type and reference kind constitute a symbolic reference
|
||||
// member and modifiers are an add-on, derived from Core Reflection (or the equivalent)
|
||||
|
||||
/**
|
||||
* Reflects the underlying member as a method, constructor, or field object.
|
||||
* If the underlying member is public, it is reflected as if by
|
||||
* {@code getMethod}, {@code getConstructor}, or {@code getField}.
|
||||
* Otherwise, it is reflected as if by
|
||||
* {@code getDeclaredMethod}, {@code getDeclaredConstructor}, or {@code getDeclaredField}.
|
||||
* The underlying member must be accessible to the given lookup object.
|
||||
* @param <T> the desired type of the result, either {@link Member} or a subtype
|
||||
* @param expected a class object representing the desired result type {@code T}
|
||||
* @param lookup the lookup object that created this MethodHandleInfo, or one with equivalent access privileges
|
||||
* @return a reference to the method, constructor, or field object
|
||||
* @exception ClassCastException if the member is not of the expected type
|
||||
* @exception NullPointerException if either argument is {@code null}
|
||||
* @exception IllegalArgumentException if the underlying member is not accessible to the given lookup object
|
||||
*/
|
||||
public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup);
|
||||
|
||||
/**
|
||||
* Returns the access modifiers of the underlying member.
|
||||
* @return the Java language modifiers for underlying member,
|
||||
* or -1 if the member cannot be accessed
|
||||
* @see Modifier
|
||||
* @see #reflectAs
|
||||
*/
|
||||
public int getModifiers();
|
||||
|
||||
/**
|
||||
* Determines if the underlying member was a variable arity method or constructor.
|
||||
* Such members are represented by method handles that are varargs collectors.
|
||||
* @implSpec
|
||||
* This produces a result equivalent to:
|
||||
* <pre>{@code
|
||||
* getReferenceKind() >= REF_invokeVirtual && Modifier.isTransient(getModifiers())
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* @return {@code true} if and only if the underlying member was declared with variable arity.
|
||||
*/
|
||||
// spelling derived from java.lang.reflect.Executable, not MethodHandle.isVarargsCollector
|
||||
public default boolean isVarArgs() {
|
||||
// fields are never varargs:
|
||||
if (MethodHandleNatives.refKindIsField((byte) getReferenceKind()))
|
||||
return false;
|
||||
// not in the public API: Modifier.VARARGS
|
||||
final int ACC_VARARGS = 0x00000080; // from JVMS 4.6 (Table 4.20)
|
||||
assert(ACC_VARARGS == Modifier.TRANSIENT);
|
||||
return Modifier.isTransient(getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the descriptive name of the given reference kind,
|
||||
* as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
|
||||
* The conventional prefix "REF_" is omitted.
|
||||
* @param referenceKind an integer code for a kind of reference used to access a class member
|
||||
* @return a mixed-case string such as {@code "getField"}
|
||||
* @exception IllegalArgumentException if the argument is not a valid
|
||||
* <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
|
||||
*/
|
||||
public static String referenceKindToString(int referenceKind) {
|
||||
if (!MethodHandleNatives.refKindIsValid(referenceKind))
|
||||
throw newIllegalArgumentException("invalid reference kind", referenceKind);
|
||||
return MethodHandleNatives.refKindName((byte)referenceKind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation for a {@code MethodHandleInfo},
|
||||
* given the four parts of its symbolic reference.
|
||||
* This is defined to be of the form {@code "RK C.N:MT"}, where {@code RK} is the
|
||||
* {@linkplain #referenceKindToString reference kind string} for {@code kind},
|
||||
* {@code C} is the {@linkplain java.lang.Class#getName name} of {@code defc}
|
||||
* {@code N} is the {@code name}, and
|
||||
* {@code MT} is the {@code type}.
|
||||
* These four values may be obtained from the
|
||||
* {@linkplain #getReferenceKind reference kind},
|
||||
* {@linkplain #getDeclaringClass declaring class},
|
||||
* {@linkplain #getName member name},
|
||||
* and {@linkplain #getMethodType method type}
|
||||
* of a {@code MethodHandleInfo} object.
|
||||
*
|
||||
* @implSpec
|
||||
* This produces a result equivalent to:
|
||||
* <pre>{@code
|
||||
* String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type)
|
||||
* }</pre>
|
||||
*
|
||||
* @param kind the {@linkplain #getReferenceKind reference kind} part of the symbolic reference
|
||||
* @param defc the {@linkplain #getDeclaringClass declaring class} part of the symbolic reference
|
||||
* @param name the {@linkplain #getName member name} part of the symbolic reference
|
||||
* @param type the {@linkplain #getMethodType method type} part of the symbolic reference
|
||||
* @return a string of the form {@code "RK C.N:MT"}
|
||||
* @exception IllegalArgumentException if the first argument is not a valid
|
||||
* <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
|
||||
* @exception NullPointerException if any reference argument is {@code null}
|
||||
*/
|
||||
public static String toString(int kind, Class<?> defc, String name, MethodType type) {
|
||||
Objects.requireNonNull(name); Objects.requireNonNull(type);
|
||||
return String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type);
|
||||
}
|
||||
}
|
||||
540
jdkSrc/jdk8/java/lang/invoke/MethodHandleNatives.java
Normal file
540
jdkSrc/jdk8/java/lang/invoke/MethodHandleNatives.java
Normal file
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, 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.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Field;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
/**
|
||||
* The JVM interface for the method handles package is all here.
|
||||
* This is an interface internal and private to an implementation of JSR 292.
|
||||
* <em>This class is not part of the JSR 292 standard.</em>
|
||||
* @author jrose
|
||||
*/
|
||||
class MethodHandleNatives {
|
||||
|
||||
private MethodHandleNatives() { } // static only
|
||||
|
||||
/// MemberName support
|
||||
|
||||
static native void init(MemberName self, Object ref);
|
||||
static native void expand(MemberName self);
|
||||
static native MemberName resolve(MemberName self, Class<?> caller) throws LinkageError, ClassNotFoundException;
|
||||
static native int getMembers(Class<?> defc, String matchName, String matchSig,
|
||||
int matchFlags, Class<?> caller, int skip, MemberName[] results);
|
||||
|
||||
/// Field layout queries parallel to sun.misc.Unsafe:
|
||||
static native long objectFieldOffset(MemberName self); // e.g., returns vmindex
|
||||
static native long staticFieldOffset(MemberName self); // e.g., returns vmindex
|
||||
static native Object staticFieldBase(MemberName self); // e.g., returns clazz
|
||||
static native Object getMemberVMInfo(MemberName self); // returns {vmindex,vmtarget}
|
||||
|
||||
/// MethodHandle support
|
||||
|
||||
/** Fetch MH-related JVM parameter.
|
||||
* which=0 retrieves MethodHandlePushLimit
|
||||
* which=1 retrieves stack slot push size (in address units)
|
||||
*/
|
||||
static native int getConstant(int which);
|
||||
|
||||
static final boolean COUNT_GWT;
|
||||
|
||||
/// CallSite support
|
||||
|
||||
/** Tell the JVM that we need to change the target of a CallSite. */
|
||||
static native void setCallSiteTargetNormal(CallSite site, MethodHandle target);
|
||||
static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target);
|
||||
|
||||
private static native void registerNatives();
|
||||
static {
|
||||
registerNatives();
|
||||
COUNT_GWT = getConstant(Constants.GC_COUNT_GWT) != 0;
|
||||
|
||||
// The JVM calls MethodHandleNatives.<clinit>. Cascade the <clinit> calls as needed:
|
||||
MethodHandleImpl.initStatics();
|
||||
}
|
||||
|
||||
// All compile-time constants go here.
|
||||
// There is an opportunity to check them against the JVM's idea of them.
|
||||
static class Constants {
|
||||
Constants() { } // static only
|
||||
// MethodHandleImpl
|
||||
static final int // for getConstant
|
||||
GC_COUNT_GWT = 4,
|
||||
GC_LAMBDA_SUPPORT = 5;
|
||||
|
||||
// MemberName
|
||||
// The JVM uses values of -2 and above for vtable indexes.
|
||||
// Field values are simple positive offsets.
|
||||
// Ref: src/share/vm/oops/methodOop.hpp
|
||||
// This value is negative enough to avoid such numbers,
|
||||
// but not too negative.
|
||||
static final int
|
||||
MN_IS_METHOD = 0x00010000, // method (not constructor)
|
||||
MN_IS_CONSTRUCTOR = 0x00020000, // constructor
|
||||
MN_IS_FIELD = 0x00040000, // field
|
||||
MN_IS_TYPE = 0x00080000, // nested type
|
||||
MN_CALLER_SENSITIVE = 0x00100000, // @CallerSensitive annotation detected
|
||||
MN_REFERENCE_KIND_SHIFT = 24, // refKind
|
||||
MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
|
||||
// The SEARCH_* bits are not for MN.flags but for the matchFlags argument of MHN.getMembers:
|
||||
MN_SEARCH_SUPERCLASSES = 0x00100000,
|
||||
MN_SEARCH_INTERFACES = 0x00200000;
|
||||
|
||||
/**
|
||||
* Basic types as encoded in the JVM. These code values are not
|
||||
* intended for use outside this class. They are used as part of
|
||||
* a private interface between the JVM and this class.
|
||||
*/
|
||||
static final int
|
||||
T_BOOLEAN = 4,
|
||||
T_CHAR = 5,
|
||||
T_FLOAT = 6,
|
||||
T_DOUBLE = 7,
|
||||
T_BYTE = 8,
|
||||
T_SHORT = 9,
|
||||
T_INT = 10,
|
||||
T_LONG = 11,
|
||||
T_OBJECT = 12,
|
||||
//T_ARRAY = 13
|
||||
T_VOID = 14,
|
||||
//T_ADDRESS = 15
|
||||
T_ILLEGAL = 99;
|
||||
|
||||
/**
|
||||
* Constant pool entry types.
|
||||
*/
|
||||
static final byte
|
||||
CONSTANT_Utf8 = 1,
|
||||
CONSTANT_Integer = 3,
|
||||
CONSTANT_Float = 4,
|
||||
CONSTANT_Long = 5,
|
||||
CONSTANT_Double = 6,
|
||||
CONSTANT_Class = 7,
|
||||
CONSTANT_String = 8,
|
||||
CONSTANT_Fieldref = 9,
|
||||
CONSTANT_Methodref = 10,
|
||||
CONSTANT_InterfaceMethodref = 11,
|
||||
CONSTANT_NameAndType = 12,
|
||||
CONSTANT_MethodHandle = 15, // JSR 292
|
||||
CONSTANT_MethodType = 16, // JSR 292
|
||||
CONSTANT_InvokeDynamic = 18,
|
||||
CONSTANT_LIMIT = 19; // Limit to tags found in classfiles
|
||||
|
||||
/**
|
||||
* Access modifier flags.
|
||||
*/
|
||||
static final char
|
||||
ACC_PUBLIC = 0x0001,
|
||||
ACC_PRIVATE = 0x0002,
|
||||
ACC_PROTECTED = 0x0004,
|
||||
ACC_STATIC = 0x0008,
|
||||
ACC_FINAL = 0x0010,
|
||||
ACC_SYNCHRONIZED = 0x0020,
|
||||
ACC_VOLATILE = 0x0040,
|
||||
ACC_TRANSIENT = 0x0080,
|
||||
ACC_NATIVE = 0x0100,
|
||||
ACC_INTERFACE = 0x0200,
|
||||
ACC_ABSTRACT = 0x0400,
|
||||
ACC_STRICT = 0x0800,
|
||||
ACC_SYNTHETIC = 0x1000,
|
||||
ACC_ANNOTATION = 0x2000,
|
||||
ACC_ENUM = 0x4000,
|
||||
// aliases:
|
||||
ACC_SUPER = ACC_SYNCHRONIZED,
|
||||
ACC_BRIDGE = ACC_VOLATILE,
|
||||
ACC_VARARGS = ACC_TRANSIENT;
|
||||
|
||||
/**
|
||||
* Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
|
||||
*/
|
||||
static final byte
|
||||
REF_NONE = 0, // null value
|
||||
REF_getField = 1,
|
||||
REF_getStatic = 2,
|
||||
REF_putField = 3,
|
||||
REF_putStatic = 4,
|
||||
REF_invokeVirtual = 5,
|
||||
REF_invokeStatic = 6,
|
||||
REF_invokeSpecial = 7,
|
||||
REF_newInvokeSpecial = 8,
|
||||
REF_invokeInterface = 9,
|
||||
REF_LIMIT = 10;
|
||||
}
|
||||
|
||||
static boolean refKindIsValid(int refKind) {
|
||||
return (refKind > REF_NONE && refKind < REF_LIMIT);
|
||||
}
|
||||
static boolean refKindIsField(byte refKind) {
|
||||
assert(refKindIsValid(refKind));
|
||||
return (refKind <= REF_putStatic);
|
||||
}
|
||||
static boolean refKindIsGetter(byte refKind) {
|
||||
assert(refKindIsValid(refKind));
|
||||
return (refKind <= REF_getStatic);
|
||||
}
|
||||
static boolean refKindIsSetter(byte refKind) {
|
||||
return refKindIsField(refKind) && !refKindIsGetter(refKind);
|
||||
}
|
||||
static boolean refKindIsMethod(byte refKind) {
|
||||
return !refKindIsField(refKind) && (refKind != REF_newInvokeSpecial);
|
||||
}
|
||||
static boolean refKindIsConstructor(byte refKind) {
|
||||
return (refKind == REF_newInvokeSpecial);
|
||||
}
|
||||
static boolean refKindHasReceiver(byte refKind) {
|
||||
assert(refKindIsValid(refKind));
|
||||
return (refKind & 1) != 0;
|
||||
}
|
||||
static boolean refKindIsStatic(byte refKind) {
|
||||
return !refKindHasReceiver(refKind) && (refKind != REF_newInvokeSpecial);
|
||||
}
|
||||
static boolean refKindDoesDispatch(byte refKind) {
|
||||
assert(refKindIsValid(refKind));
|
||||
return (refKind == REF_invokeVirtual ||
|
||||
refKind == REF_invokeInterface);
|
||||
}
|
||||
static {
|
||||
final int HR_MASK = ((1 << REF_getField) |
|
||||
(1 << REF_putField) |
|
||||
(1 << REF_invokeVirtual) |
|
||||
(1 << REF_invokeSpecial) |
|
||||
(1 << REF_invokeInterface)
|
||||
);
|
||||
for (byte refKind = REF_NONE+1; refKind < REF_LIMIT; refKind++) {
|
||||
assert(refKindHasReceiver(refKind) == (((1<<refKind) & HR_MASK) != 0)) : refKind;
|
||||
}
|
||||
}
|
||||
static String refKindName(byte refKind) {
|
||||
assert(refKindIsValid(refKind));
|
||||
switch (refKind) {
|
||||
case REF_getField: return "getField";
|
||||
case REF_getStatic: return "getStatic";
|
||||
case REF_putField: return "putField";
|
||||
case REF_putStatic: return "putStatic";
|
||||
case REF_invokeVirtual: return "invokeVirtual";
|
||||
case REF_invokeStatic: return "invokeStatic";
|
||||
case REF_invokeSpecial: return "invokeSpecial";
|
||||
case REF_newInvokeSpecial: return "newInvokeSpecial";
|
||||
case REF_invokeInterface: return "invokeInterface";
|
||||
default: return "REF_???";
|
||||
}
|
||||
}
|
||||
|
||||
private static native int getNamedCon(int which, Object[] name);
|
||||
static boolean verifyConstants() {
|
||||
Object[] box = { null };
|
||||
for (int i = 0; ; i++) {
|
||||
box[0] = null;
|
||||
int vmval = getNamedCon(i, box);
|
||||
if (box[0] == null) break;
|
||||
String name = (String) box[0];
|
||||
try {
|
||||
Field con = Constants.class.getDeclaredField(name);
|
||||
int jval = con.getInt(null);
|
||||
if (jval == vmval) continue;
|
||||
String err = (name+": JVM has "+vmval+" while Java has "+jval);
|
||||
if (name.equals("CONV_OP_LIMIT")) {
|
||||
System.err.println("warning: "+err);
|
||||
continue;
|
||||
}
|
||||
throw new InternalError(err);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
String err = (name+": JVM has "+vmval+" which Java does not define");
|
||||
// ignore exotic ops the JVM cares about; we just wont issue them
|
||||
//System.err.println("warning: "+err);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static {
|
||||
assert(verifyConstants());
|
||||
}
|
||||
|
||||
// Up-calls from the JVM.
|
||||
// These must NOT be public.
|
||||
|
||||
/**
|
||||
* The JVM is linking an invokedynamic instruction. Create a reified call site for it.
|
||||
*/
|
||||
static MemberName linkCallSite(Object callerObj,
|
||||
Object bootstrapMethodObj,
|
||||
Object nameObj, Object typeObj,
|
||||
Object staticArguments,
|
||||
Object[] appendixResult) {
|
||||
MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
|
||||
Class<?> caller = (Class<?>)callerObj;
|
||||
String name = nameObj.toString().intern();
|
||||
MethodType type = (MethodType)typeObj;
|
||||
if (!TRACE_METHOD_LINKAGE)
|
||||
return linkCallSiteImpl(caller, bootstrapMethod, name, type,
|
||||
staticArguments, appendixResult);
|
||||
return linkCallSiteTracing(caller, bootstrapMethod, name, type,
|
||||
staticArguments, appendixResult);
|
||||
}
|
||||
static MemberName linkCallSiteImpl(Class<?> caller,
|
||||
MethodHandle bootstrapMethod,
|
||||
String name, MethodType type,
|
||||
Object staticArguments,
|
||||
Object[] appendixResult) {
|
||||
CallSite callSite = CallSite.makeSite(bootstrapMethod,
|
||||
name,
|
||||
type,
|
||||
staticArguments,
|
||||
caller);
|
||||
if (callSite instanceof ConstantCallSite) {
|
||||
appendixResult[0] = callSite.dynamicInvoker();
|
||||
return Invokers.linkToTargetMethod(type);
|
||||
} else {
|
||||
appendixResult[0] = callSite;
|
||||
return Invokers.linkToCallSiteMethod(type);
|
||||
}
|
||||
}
|
||||
// Tracing logic:
|
||||
static MemberName linkCallSiteTracing(Class<?> caller,
|
||||
MethodHandle bootstrapMethod,
|
||||
String name, MethodType type,
|
||||
Object staticArguments,
|
||||
Object[] appendixResult) {
|
||||
Object bsmReference = bootstrapMethod.internalMemberName();
|
||||
if (bsmReference == null) bsmReference = bootstrapMethod;
|
||||
Object staticArglist = (staticArguments instanceof Object[] ?
|
||||
java.util.Arrays.asList((Object[]) staticArguments) :
|
||||
staticArguments);
|
||||
System.out.println("linkCallSite "+caller.getName()+" "+
|
||||
bsmReference+" "+
|
||||
name+type+"/"+staticArglist);
|
||||
try {
|
||||
MemberName res = linkCallSiteImpl(caller, bootstrapMethod, name, type,
|
||||
staticArguments, appendixResult);
|
||||
System.out.println("linkCallSite => "+res+" + "+appendixResult[0]);
|
||||
return res;
|
||||
} catch (Throwable ex) {
|
||||
System.out.println("linkCallSite => throw "+ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The JVM wants a pointer to a MethodType. Oblige it by finding or creating one.
|
||||
*/
|
||||
static MethodType findMethodHandleType(Class<?> rtype, Class<?>[] ptypes) {
|
||||
return MethodType.makeImpl(rtype, ptypes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* The JVM wants to link a call site that requires a dynamic type check.
|
||||
* Name is a type-checking invoker, invokeExact or invoke.
|
||||
* Return a JVM method (MemberName) to handle the invoking.
|
||||
* The method assumes the following arguments on the stack:
|
||||
* 0: the method handle being invoked
|
||||
* 1-N: the arguments to the method handle invocation
|
||||
* N+1: an optional, implicitly added argument (typically the given MethodType)
|
||||
* <p>
|
||||
* The nominal method at such a call site is an instance of
|
||||
* a signature-polymorphic method (see @PolymorphicSignature).
|
||||
* Such method instances are user-visible entities which are
|
||||
* "split" from the generic placeholder method in {@code MethodHandle}.
|
||||
* (Note that the placeholder method is not identical with any of
|
||||
* its instances. If invoked reflectively, is guaranteed to throw an
|
||||
* {@code UnsupportedOperationException}.)
|
||||
* If the signature-polymorphic method instance is ever reified,
|
||||
* it appears as a "copy" of the original placeholder
|
||||
* (a native final member of {@code MethodHandle}) except
|
||||
* that its type descriptor has shape required by the instance,
|
||||
* and the method instance is <em>not</em> varargs.
|
||||
* The method instance is also marked synthetic, since the
|
||||
* method (by definition) does not appear in Java source code.
|
||||
* <p>
|
||||
* The JVM is allowed to reify this method as instance metadata.
|
||||
* For example, {@code invokeBasic} is always reified.
|
||||
* But the JVM may instead call {@code linkMethod}.
|
||||
* If the result is an * ordered pair of a {@code (method, appendix)},
|
||||
* the method gets all the arguments (0..N inclusive)
|
||||
* plus the appendix (N+1), and uses the appendix to complete the call.
|
||||
* In this way, one reusable method (called a "linker method")
|
||||
* can perform the function of any number of polymorphic instance
|
||||
* methods.
|
||||
* <p>
|
||||
* Linker methods are allowed to be weakly typed, with any or
|
||||
* all references rewritten to {@code Object} and any primitives
|
||||
* (except {@code long}/{@code float}/{@code double})
|
||||
* rewritten to {@code int}.
|
||||
* A linker method is trusted to return a strongly typed result,
|
||||
* according to the specific method type descriptor of the
|
||||
* signature-polymorphic instance it is emulating.
|
||||
* This can involve (as necessary) a dynamic check using
|
||||
* data extracted from the appendix argument.
|
||||
* <p>
|
||||
* The JVM does not inspect the appendix, other than to pass
|
||||
* it verbatim to the linker method at every call.
|
||||
* This means that the JDK runtime has wide latitude
|
||||
* for choosing the shape of each linker method and its
|
||||
* corresponding appendix.
|
||||
* Linker methods should be generated from {@code LambdaForm}s
|
||||
* so that they do not become visible on stack traces.
|
||||
* <p>
|
||||
* The {@code linkMethod} call is free to omit the appendix
|
||||
* (returning null) and instead emulate the required function
|
||||
* completely in the linker method.
|
||||
* As a corner case, if N==255, no appendix is possible.
|
||||
* In this case, the method returned must be custom-generated to
|
||||
* to perform any needed type checking.
|
||||
* <p>
|
||||
* If the JVM does not reify a method at a call site, but instead
|
||||
* calls {@code linkMethod}, the corresponding call represented
|
||||
* in the bytecodes may mention a valid method which is not
|
||||
* representable with a {@code MemberName}.
|
||||
* Therefore, use cases for {@code linkMethod} tend to correspond to
|
||||
* special cases in reflective code such as {@code findVirtual}
|
||||
* or {@code revealDirect}.
|
||||
*/
|
||||
static MemberName linkMethod(Class<?> callerClass, int refKind,
|
||||
Class<?> defc, String name, Object type,
|
||||
Object[] appendixResult) {
|
||||
if (!TRACE_METHOD_LINKAGE)
|
||||
return linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
|
||||
return linkMethodTracing(callerClass, refKind, defc, name, type, appendixResult);
|
||||
}
|
||||
static MemberName linkMethodImpl(Class<?> callerClass, int refKind,
|
||||
Class<?> defc, String name, Object type,
|
||||
Object[] appendixResult) {
|
||||
try {
|
||||
if (defc == MethodHandle.class && refKind == REF_invokeVirtual) {
|
||||
return Invokers.methodHandleInvokeLinkerMethod(name, fixMethodType(callerClass, type), appendixResult);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
if (ex instanceof LinkageError)
|
||||
throw (LinkageError) ex;
|
||||
else
|
||||
throw new LinkageError(ex.getMessage(), ex);
|
||||
}
|
||||
throw new LinkageError("no such method "+defc.getName()+"."+name+type);
|
||||
}
|
||||
private static MethodType fixMethodType(Class<?> callerClass, Object type) {
|
||||
if (type instanceof MethodType)
|
||||
return (MethodType) type;
|
||||
else
|
||||
return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader());
|
||||
}
|
||||
// Tracing logic:
|
||||
static MemberName linkMethodTracing(Class<?> callerClass, int refKind,
|
||||
Class<?> defc, String name, Object type,
|
||||
Object[] appendixResult) {
|
||||
System.out.println("linkMethod "+defc.getName()+"."+
|
||||
name+type+"/"+Integer.toHexString(refKind));
|
||||
try {
|
||||
MemberName res = linkMethodImpl(callerClass, refKind, defc, name, type, appendixResult);
|
||||
System.out.println("linkMethod => "+res+" + "+appendixResult[0]);
|
||||
return res;
|
||||
} catch (Throwable ex) {
|
||||
System.out.println("linkMethod => throw "+ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help.
|
||||
* It will make an up-call to this method. (Do not change the name or signature.)
|
||||
* The type argument is a Class for field requests and a MethodType for non-fields.
|
||||
* <p>
|
||||
* Recent versions of the JVM may also pass a resolved MemberName for the type.
|
||||
* In that case, the name is ignored and may be null.
|
||||
*/
|
||||
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
|
||||
Class<?> defc, String name, Object type) {
|
||||
try {
|
||||
Lookup lookup = IMPL_LOOKUP.in(callerClass);
|
||||
assert(refKindIsValid(refKind));
|
||||
return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type);
|
||||
} catch (IllegalAccessException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause instanceof AbstractMethodError) {
|
||||
throw (AbstractMethodError) cause;
|
||||
} else {
|
||||
Error err = new IllegalAccessError(ex.getMessage());
|
||||
throw initCauseFrom(err, ex);
|
||||
}
|
||||
} catch (NoSuchMethodException ex) {
|
||||
Error err = new NoSuchMethodError(ex.getMessage());
|
||||
throw initCauseFrom(err, ex);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
Error err = new NoSuchFieldError(ex.getMessage());
|
||||
throw initCauseFrom(err, ex);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
Error err = new IncompatibleClassChangeError();
|
||||
throw initCauseFrom(err, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use best possible cause for err.initCause(), substituting the
|
||||
* cause for err itself if the cause has the same (or better) type.
|
||||
*/
|
||||
static private Error initCauseFrom(Error err, Exception ex) {
|
||||
Throwable th = ex.getCause();
|
||||
if (err.getClass().isInstance(th))
|
||||
return (Error) th;
|
||||
err.initCause(th == null ? ex : th);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this method a caller-sensitive method?
|
||||
* I.e., does it call Reflection.getCallerClass or a similer method
|
||||
* to ask about the identity of its caller?
|
||||
*/
|
||||
static boolean isCallerSensitive(MemberName mem) {
|
||||
if (!mem.isInvocable()) return false; // fields are not caller sensitive
|
||||
|
||||
return mem.isCallerSensitive() || canBeCalledVirtual(mem);
|
||||
}
|
||||
|
||||
static boolean canBeCalledVirtual(MemberName mem) {
|
||||
assert(mem.isInvocable());
|
||||
Class<?> defc = mem.getDeclaringClass();
|
||||
switch (mem.getName()) {
|
||||
case "checkMemberAccess":
|
||||
return canBeCalledVirtual(mem, java.lang.SecurityManager.class);
|
||||
case "getContextClassLoader":
|
||||
return canBeCalledVirtual(mem, java.lang.Thread.class);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean canBeCalledVirtual(MemberName symbolicRef, Class<?> definingClass) {
|
||||
Class<?> symbolicRefClass = symbolicRef.getDeclaringClass();
|
||||
if (symbolicRefClass == definingClass) return true;
|
||||
if (symbolicRef.isStatic() || symbolicRef.isPrivate()) return false;
|
||||
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
|
||||
symbolicRefClass.isInterface()); // Mdef implements Msym
|
||||
}
|
||||
}
|
||||
372
jdkSrc/jdk8/java/lang/invoke/MethodHandleProxies.java
Normal file
372
jdkSrc/jdk8/java/lang/invoke/MethodHandleProxies.java
Normal file
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2018, 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.reflect.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import sun.invoke.WrapperInstance;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.Reflection;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* This class consists exclusively of static methods that help adapt
|
||||
* method handles to other JVM types, such as interfaces.
|
||||
*/
|
||||
public class MethodHandleProxies {
|
||||
|
||||
private MethodHandleProxies() { } // do not instantiate
|
||||
|
||||
/**
|
||||
* Produces an instance of the given single-method interface which redirects
|
||||
* its calls to the given method handle.
|
||||
* <p>
|
||||
* A single-method interface is an interface which declares a uniquely named method.
|
||||
* When determining the uniquely named method of a single-method interface,
|
||||
* the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode})
|
||||
* are disregarded as are any default (non-abstract) methods.
|
||||
* For example, {@link java.util.Comparator} is a single-method interface,
|
||||
* even though it re-declares the {@code Object.equals} method and also
|
||||
* declares default methods, such as {@code Comparator.reverse}.
|
||||
* <p>
|
||||
* The interface must be public. No additional access checks are performed.
|
||||
* <p>
|
||||
* The resulting instance of the required type will respond to
|
||||
* invocation of the type's uniquely named method by calling
|
||||
* the given target on the incoming arguments,
|
||||
* and returning or throwing whatever the target
|
||||
* returns or throws. The invocation will be as if by
|
||||
* {@code target.invoke}.
|
||||
* The target's type will be checked before the
|
||||
* instance is created, as if by a call to {@code asType},
|
||||
* which may result in a {@code WrongMethodTypeException}.
|
||||
* <p>
|
||||
* The uniquely named method is allowed to be multiply declared,
|
||||
* with distinct type descriptors. (E.g., it can be overloaded,
|
||||
* or can possess bridge methods.) All such declarations are
|
||||
* connected directly to the target method handle.
|
||||
* Argument and return types are adjusted by {@code asType}
|
||||
* for each individual declaration.
|
||||
* <p>
|
||||
* The wrapper instance will implement the requested interface
|
||||
* and its super-types, but no other single-method interfaces.
|
||||
* This means that the instance will not unexpectedly
|
||||
* pass an {@code instanceof} test for any unrequested type.
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Implementation Note:</em>
|
||||
* Therefore, each instance must implement a unique single-method interface.
|
||||
* Implementations may not bundle together
|
||||
* multiple single-method interfaces onto single implementation classes
|
||||
* in the style of {@link java.awt.AWTEventMulticaster}.
|
||||
* <p>
|
||||
* The method handle may throw an <em>undeclared exception</em>,
|
||||
* which means any checked exception (or other checked throwable)
|
||||
* not declared by the requested type's single abstract method.
|
||||
* If this happens, the throwable will be wrapped in an instance of
|
||||
* {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException}
|
||||
* and thrown in that wrapped form.
|
||||
* <p>
|
||||
* Like {@link java.lang.Integer#valueOf Integer.valueOf},
|
||||
* {@code asInterfaceInstance} is a factory method whose results are defined
|
||||
* by their behavior.
|
||||
* It is not guaranteed to return a new instance for every call.
|
||||
* <p>
|
||||
* Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods}
|
||||
* and other corner cases, the interface may also have several abstract methods
|
||||
* with the same name but having distinct descriptors (types of returns and parameters).
|
||||
* In this case, all the methods are bound in common to the one given target.
|
||||
* The type check and effective {@code asType} conversion is applied to each
|
||||
* method type descriptor, and all abstract methods are bound to the target in common.
|
||||
* Beyond this type check, no further checks are made to determine that the
|
||||
* abstract methods are related in any way.
|
||||
* <p>
|
||||
* Future versions of this API may accept additional types,
|
||||
* such as abstract classes with single abstract methods.
|
||||
* Future versions of this API may also equip wrapper instances
|
||||
* with one or more additional public "marker" interfaces.
|
||||
* <p>
|
||||
* If a security manager is installed, this method is caller sensitive.
|
||||
* During any invocation of the target method handle via the returned wrapper,
|
||||
* the original creator of the wrapper (the caller) will be visible
|
||||
* to context checks requested by the security manager.
|
||||
*
|
||||
* @param <T> the desired type of the wrapper, a single-method interface
|
||||
* @param intfc a class object representing {@code T}
|
||||
* @param target the method handle to invoke from the wrapper
|
||||
* @return a correctly-typed wrapper for the given target
|
||||
* @throws NullPointerException if either argument is null
|
||||
* @throws IllegalArgumentException if the {@code intfc} is not a
|
||||
* valid argument to this method
|
||||
* @throws WrongMethodTypeException if the target cannot
|
||||
* be converted to the type required by the requested interface
|
||||
*/
|
||||
// Other notes to implementors:
|
||||
// <p>
|
||||
// No stable mapping is promised between the single-method interface and
|
||||
// the implementation class C. Over time, several implementation
|
||||
// classes might be used for the same type.
|
||||
// <p>
|
||||
// If the implementation is able
|
||||
// to prove that a wrapper of the required type
|
||||
// has already been created for a given
|
||||
// method handle, or for another method handle with the
|
||||
// same behavior, the implementation may return that wrapper in place of
|
||||
// a new wrapper.
|
||||
// <p>
|
||||
// This method is designed to apply to common use cases
|
||||
// where a single method handle must interoperate with
|
||||
// an interface that implements a function-like
|
||||
// API. Additional variations, such as single-abstract-method classes with
|
||||
// private constructors, or interfaces with multiple but related
|
||||
// entry points, must be covered by hand-written or automatically
|
||||
// generated adapter classes.
|
||||
//
|
||||
@CallerSensitive
|
||||
public static
|
||||
<T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) {
|
||||
if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers()))
|
||||
throw newIllegalArgumentException("not a public interface", intfc.getName());
|
||||
final MethodHandle mh;
|
||||
if (System.getSecurityManager() != null) {
|
||||
final Class<?> caller = Reflection.getCallerClass();
|
||||
final ClassLoader ccl = caller != null ? caller.getClassLoader() : null;
|
||||
ReflectUtil.checkProxyPackageAccess(ccl, intfc);
|
||||
mh = ccl != null ? bindCaller(target, caller) : target;
|
||||
} else {
|
||||
mh = target;
|
||||
}
|
||||
ClassLoader proxyLoader = intfc.getClassLoader();
|
||||
if (proxyLoader == null) {
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); // avoid use of BCP
|
||||
proxyLoader = cl != null ? cl : ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
final Method[] methods = getSingleNameMethods(intfc);
|
||||
if (methods == null)
|
||||
throw newIllegalArgumentException("not a single-method interface", intfc.getName());
|
||||
final MethodHandle[] vaTargets = new MethodHandle[methods.length];
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
Method sm = methods[i];
|
||||
MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes());
|
||||
MethodHandle checkTarget = mh.asType(smMT); // make throw WMT
|
||||
checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class));
|
||||
vaTargets[i] = checkTarget.asSpreader(Object[].class, smMT.parameterCount());
|
||||
}
|
||||
final ConcurrentHashMap<Method, MethodHandle> defaultMethodMap =
|
||||
hasDefaultMethods(intfc) ? new ConcurrentHashMap<>() : null;
|
||||
final InvocationHandler ih = new InvocationHandler() {
|
||||
private Object getArg(String name) {
|
||||
if ((Object)name == "getWrapperInstanceTarget") return target;
|
||||
if ((Object)name == "getWrapperInstanceType") return intfc;
|
||||
throw new AssertionError();
|
||||
}
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (method.equals(methods[i]))
|
||||
return vaTargets[i].invokeExact(args);
|
||||
}
|
||||
if (method.getDeclaringClass() == WrapperInstance.class)
|
||||
return getArg(method.getName());
|
||||
if (isObjectMethod(method))
|
||||
return callObjectMethod(proxy, method, args);
|
||||
if (isDefaultMethod(method)) {
|
||||
return callDefaultMethod(defaultMethodMap, proxy, intfc, method, args);
|
||||
}
|
||||
throw newInternalError("bad proxy method: "+method);
|
||||
}
|
||||
};
|
||||
|
||||
final Object proxy;
|
||||
if (System.getSecurityManager() != null) {
|
||||
// sun.invoke.WrapperInstance is a restricted interface not accessible
|
||||
// by any non-null class loader.
|
||||
final ClassLoader loader = proxyLoader;
|
||||
proxy = AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
||||
public Object run() {
|
||||
return Proxy.newProxyInstance(
|
||||
loader,
|
||||
new Class<?>[]{ intfc, WrapperInstance.class },
|
||||
ih);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
proxy = Proxy.newProxyInstance(proxyLoader,
|
||||
new Class<?>[]{ intfc, WrapperInstance.class },
|
||||
ih);
|
||||
}
|
||||
return intfc.cast(proxy);
|
||||
}
|
||||
|
||||
private static MethodHandle bindCaller(MethodHandle target, Class<?> hostClass) {
|
||||
MethodHandle cbmh = MethodHandleImpl.bindCaller(target, hostClass);
|
||||
if (target.isVarargsCollector()) {
|
||||
MethodType type = cbmh.type();
|
||||
int arity = type.parameterCount();
|
||||
return cbmh.asVarargsCollector(type.parameterType(arity-1));
|
||||
}
|
||||
return cbmh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given object was produced by a call to {@link #asInterfaceInstance asInterfaceInstance}.
|
||||
* @param x any reference
|
||||
* @return true if the reference is not null and points to an object produced by {@code asInterfaceInstance}
|
||||
*/
|
||||
public static
|
||||
boolean isWrapperInstance(Object x) {
|
||||
return x instanceof WrapperInstance;
|
||||
}
|
||||
|
||||
private static WrapperInstance asWrapperInstance(Object x) {
|
||||
try {
|
||||
if (x != null)
|
||||
return (WrapperInstance) x;
|
||||
} catch (ClassCastException ex) {
|
||||
}
|
||||
throw newIllegalArgumentException("not a wrapper instance");
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces or recovers a target method handle which is behaviorally
|
||||
* equivalent to the unique method of this wrapper instance.
|
||||
* The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}.
|
||||
* This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
|
||||
* @param x any reference
|
||||
* @return a method handle implementing the unique method
|
||||
* @throws IllegalArgumentException if the reference x is not to a wrapper instance
|
||||
*/
|
||||
public static
|
||||
MethodHandle wrapperInstanceTarget(Object x) {
|
||||
return asWrapperInstance(x).getWrapperInstanceTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recovers the unique single-method interface type for which this wrapper instance was created.
|
||||
* The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}.
|
||||
* This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
|
||||
* @param x any reference
|
||||
* @return the single-method interface type for which the wrapper was created
|
||||
* @throws IllegalArgumentException if the reference x is not to a wrapper instance
|
||||
*/
|
||||
public static
|
||||
Class<?> wrapperInstanceType(Object x) {
|
||||
return asWrapperInstance(x).getWrapperInstanceType();
|
||||
}
|
||||
|
||||
private static
|
||||
boolean isObjectMethod(Method m) {
|
||||
switch (m.getName()) {
|
||||
case "toString":
|
||||
return (m.getReturnType() == String.class
|
||||
&& m.getParameterTypes().length == 0);
|
||||
case "hashCode":
|
||||
return (m.getReturnType() == int.class
|
||||
&& m.getParameterTypes().length == 0);
|
||||
case "equals":
|
||||
return (m.getReturnType() == boolean.class
|
||||
&& m.getParameterTypes().length == 1
|
||||
&& m.getParameterTypes()[0] == Object.class);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static
|
||||
Object callObjectMethod(Object self, Method m, Object[] args) {
|
||||
assert(isObjectMethod(m)) : m;
|
||||
switch (m.getName()) {
|
||||
case "toString":
|
||||
return self.getClass().getName() + "@" + Integer.toHexString(self.hashCode());
|
||||
case "hashCode":
|
||||
return System.identityHashCode(self);
|
||||
case "equals":
|
||||
return (self == args[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static
|
||||
Method[] getSingleNameMethods(Class<?> intfc) {
|
||||
ArrayList<Method> methods = new ArrayList<Method>();
|
||||
String uniqueName = null;
|
||||
for (Method m : intfc.getMethods()) {
|
||||
if (isObjectMethod(m)) continue;
|
||||
if (!Modifier.isAbstract(m.getModifiers())) continue;
|
||||
String mname = m.getName();
|
||||
if (uniqueName == null)
|
||||
uniqueName = mname;
|
||||
else if (!uniqueName.equals(mname))
|
||||
return null; // too many abstract methods
|
||||
methods.add(m);
|
||||
}
|
||||
if (uniqueName == null) return null;
|
||||
return methods.toArray(new Method[methods.size()]);
|
||||
}
|
||||
|
||||
private static
|
||||
boolean isDefaultMethod(Method m) {
|
||||
return !Modifier.isAbstract(m.getModifiers());
|
||||
}
|
||||
|
||||
private static
|
||||
boolean hasDefaultMethods(Class<?> intfc) {
|
||||
for (Method m : intfc.getMethods()) {
|
||||
if (!isObjectMethod(m) &&
|
||||
!Modifier.isAbstract(m.getModifiers())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static
|
||||
Object callDefaultMethod(ConcurrentHashMap<Method, MethodHandle> defaultMethodMap,
|
||||
Object self, Class<?> intfc, Method m, Object[] args) throws Throwable {
|
||||
assert(isDefaultMethod(m) && !isObjectMethod(m)) : m;
|
||||
|
||||
// Lazily compute the associated method handle from the method
|
||||
MethodHandle dmh = defaultMethodMap.computeIfAbsent(m, mk -> {
|
||||
try {
|
||||
// Look up the default method for special invocation thereby
|
||||
// avoiding recursive invocation back to the proxy
|
||||
MethodHandle mh = MethodHandles.Lookup.IMPL_LOOKUP.findSpecial(
|
||||
intfc, mk.getName(),
|
||||
MethodType.methodType(mk.getReturnType(), mk.getParameterTypes()),
|
||||
self.getClass());
|
||||
return mh.asSpreader(Object[].class, mk.getParameterCount());
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
// The method is known to exist and should be accessible, this
|
||||
// method would not be called unless the invokeinterface to the
|
||||
// default (public) method passed access control checks
|
||||
throw new InternalError(e);
|
||||
}
|
||||
});
|
||||
return dmh.invoke(self, args);
|
||||
}
|
||||
}
|
||||
164
jdkSrc/jdk8/java/lang/invoke/MethodHandleStatics.java
Normal file
164
jdkSrc/jdk8/java/lang/invoke/MethodHandleStatics.java
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* This class consists exclusively of static names internal to the
|
||||
* method handle implementation.
|
||||
* Usage: {@code import static java.lang.invoke.MethodHandleStatics.*}
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
/*non-public*/ class MethodHandleStatics {
|
||||
|
||||
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 int COMPILE_THRESHOLD;
|
||||
static final int DONT_INLINE_THRESHOLD;
|
||||
static final int PROFILE_LEVEL;
|
||||
static final boolean PROFILE_GWT;
|
||||
static final int CUSTOMIZE_THRESHOLD;
|
||||
|
||||
static {
|
||||
final Object[] values = new Object[9];
|
||||
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", 0);
|
||||
values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.DONT_INLINE_THRESHOLD", 30);
|
||||
values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
|
||||
values[7] = Boolean.parseBoolean(System.getProperty("java.lang.invoke.MethodHandle.PROFILE_GWT", "true"));
|
||||
values[8] = Integer.getInteger("java.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD", 127);
|
||||
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];
|
||||
DONT_INLINE_THRESHOLD = (Integer) values[5];
|
||||
PROFILE_LEVEL = (Integer) values[6];
|
||||
PROFILE_GWT = (Boolean) values[7];
|
||||
CUSTOMIZE_THRESHOLD = (Integer) values[8];
|
||||
|
||||
if (CUSTOMIZE_THRESHOLD < -1 || CUSTOMIZE_THRESHOLD > 127) {
|
||||
throw newInternalError("CUSTOMIZE_THRESHOLD should be in [-1...127] range");
|
||||
}
|
||||
}
|
||||
|
||||
/** Tell if any of the debugging switches are turned on.
|
||||
* If this is the case, it is reasonable to perform extra checks or save extra information.
|
||||
*/
|
||||
/*non-public*/ static boolean debugEnabled() {
|
||||
return (DEBUG_METHOD_HANDLE_NAMES |
|
||||
DUMP_CLASS_FILES |
|
||||
TRACE_INTERPRETER |
|
||||
TRACE_METHOD_LINKAGE);
|
||||
}
|
||||
|
||||
/*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
|
||||
if (type == null)
|
||||
type = target.type();
|
||||
MemberName name = null;
|
||||
if (target != null)
|
||||
name = target.internalMemberName();
|
||||
if (name == null)
|
||||
return "invoke" + type;
|
||||
return name.getName() + type;
|
||||
}
|
||||
|
||||
/*non-public*/ static String getNameString(MethodHandle target, MethodHandle typeHolder) {
|
||||
return getNameString(target, typeHolder == null ? (MethodType) null : typeHolder.type());
|
||||
}
|
||||
|
||||
/*non-public*/ static String getNameString(MethodHandle target) {
|
||||
return getNameString(target, (MethodType) null);
|
||||
}
|
||||
|
||||
/*non-public*/ static String addTypeString(Object obj, MethodHandle target) {
|
||||
String str = String.valueOf(obj);
|
||||
if (target == null) return str;
|
||||
int paren = str.indexOf('(');
|
||||
if (paren >= 0) str = str.substring(0, paren);
|
||||
return str + target.type();
|
||||
}
|
||||
|
||||
// handy shared exception makers (they simplify the common case code)
|
||||
/*non-public*/ static InternalError newInternalError(String message) {
|
||||
return new InternalError(message);
|
||||
}
|
||||
/*non-public*/ static InternalError newInternalError(String message, Throwable cause) {
|
||||
return new InternalError(message, cause);
|
||||
}
|
||||
/*non-public*/ static InternalError newInternalError(Throwable cause) {
|
||||
return new InternalError(cause);
|
||||
}
|
||||
/*non-public*/ static RuntimeException newIllegalStateException(String message) {
|
||||
return new IllegalStateException(message);
|
||||
}
|
||||
/*non-public*/ static RuntimeException newIllegalStateException(String message, Object obj) {
|
||||
return new IllegalStateException(message(message, obj));
|
||||
}
|
||||
/*non-public*/ static RuntimeException newIllegalArgumentException(String message) {
|
||||
return new IllegalArgumentException(message);
|
||||
}
|
||||
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj) {
|
||||
return new IllegalArgumentException(message(message, obj));
|
||||
}
|
||||
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
|
||||
return new IllegalArgumentException(message(message, obj, obj2));
|
||||
}
|
||||
/** Propagate unchecked exceptions and errors, but wrap anything checked and throw that instead. */
|
||||
/*non-public*/ static Error uncaughtException(Throwable ex) {
|
||||
if (ex instanceof Error) throw (Error) ex;
|
||||
if (ex instanceof RuntimeException) throw (RuntimeException) ex;
|
||||
throw newInternalError("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;
|
||||
}
|
||||
private static String message(String message, Object obj, Object obj2) {
|
||||
if (obj != null || obj2 != null) message = message + ": " + obj + ", " + obj2;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
3113
jdkSrc/jdk8/java/lang/invoke/MethodHandles.java
Normal file
3113
jdkSrc/jdk8/java/lang/invoke/MethodHandles.java
Normal file
File diff suppressed because it is too large
Load Diff
1291
jdkSrc/jdk8/java/lang/invoke/MethodType.java
Normal file
1291
jdkSrc/jdk8/java/lang/invoke/MethodType.java
Normal file
File diff suppressed because it is too large
Load Diff
426
jdkSrc/jdk8/java/lang/invoke/MethodTypeForm.java
Normal file
426
jdkSrc/jdk8/java/lang/invoke/MethodTypeForm.java
Normal file
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, 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 sun.invoke.util.Wrapper;
|
||||
import java.lang.ref.SoftReference;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* Shared information for a group of method types, which differ
|
||||
* only by reference types, and therefore share a common erasure
|
||||
* and wrapping.
|
||||
* <p>
|
||||
* For an empirical discussion of the structure of method types,
|
||||
* see <a href="http://groups.google.com/group/jvm-languages/browse_thread/thread/ac9308ae74da9b7e/">
|
||||
* the thread "Avoiding Boxing" on jvm-languages</a>.
|
||||
* There are approximately 2000 distinct erased method types in the JDK.
|
||||
* There are a little over 10 times that number of unerased types.
|
||||
* No more than half of these are likely to be loaded at once.
|
||||
* @author John Rose
|
||||
*/
|
||||
final class MethodTypeForm {
|
||||
final int[] argToSlotTable, slotToArgTable;
|
||||
final long argCounts; // packed slot & value counts
|
||||
final long primCounts; // packed prim & double counts
|
||||
final MethodType erasedType; // the canonical erasure
|
||||
final MethodType basicType; // the canonical erasure, with primitives simplified
|
||||
|
||||
// Cached adapter information:
|
||||
@Stable final SoftReference<MethodHandle>[] methodHandles;
|
||||
// Indexes into methodHandles:
|
||||
static final int
|
||||
MH_BASIC_INV = 0, // cached instance of MH.invokeBasic
|
||||
MH_NF_INV = 1, // cached helper for LF.NamedFunction
|
||||
MH_UNINIT_CS = 2, // uninitialized call site
|
||||
MH_LIMIT = 3;
|
||||
|
||||
// Cached lambda form information, for basic types only:
|
||||
final @Stable SoftReference<LambdaForm>[] lambdaForms;
|
||||
// Indexes into lambdaForms:
|
||||
static final int
|
||||
LF_INVVIRTUAL = 0, // DMH invokeVirtual
|
||||
LF_INVSTATIC = 1,
|
||||
LF_INVSPECIAL = 2,
|
||||
LF_NEWINVSPECIAL = 3,
|
||||
LF_INVINTERFACE = 4,
|
||||
LF_INVSTATIC_INIT = 5, // DMH invokeStatic with <clinit> barrier
|
||||
LF_INTERPRET = 6, // LF interpreter
|
||||
LF_REBIND = 7, // BoundMethodHandle
|
||||
LF_DELEGATE = 8, // DelegatingMethodHandle
|
||||
LF_DELEGATE_BLOCK_INLINING = 9, // Counting DelegatingMethodHandle w/ @DontInline
|
||||
LF_EX_LINKER = 10, // invokeExact_MT (for invokehandle)
|
||||
LF_EX_INVOKER = 11, // MHs.invokeExact
|
||||
LF_GEN_LINKER = 12, // generic invoke_MT (for invokehandle)
|
||||
LF_GEN_INVOKER = 13, // generic MHs.invoke
|
||||
LF_CS_LINKER = 14, // linkToCallSite_CS
|
||||
LF_MH_LINKER = 15, // linkToCallSite_MH
|
||||
LF_GWC = 16, // guardWithCatch (catchException)
|
||||
LF_GWT = 17, // guardWithTest
|
||||
LF_LIMIT = 18;
|
||||
|
||||
/** Return the type corresponding uniquely (1-1) to this MT-form.
|
||||
* It might have any primitive returns or arguments, but will have no references except Object.
|
||||
*/
|
||||
public MethodType erasedType() {
|
||||
return erasedType;
|
||||
}
|
||||
|
||||
/** Return the basic type derived from the erased type of this MT-form.
|
||||
* A basic type is erased (all references Object) and also has all primitive
|
||||
* types (except int, long, float, double, void) normalized to int.
|
||||
* Such basic types correspond to low-level JVM calling sequences.
|
||||
*/
|
||||
public MethodType basicType() {
|
||||
return basicType;
|
||||
}
|
||||
|
||||
private boolean assertIsBasicType() {
|
||||
// primitives must be flattened also
|
||||
assert(erasedType == basicType)
|
||||
: "erasedType: " + erasedType + " != basicType: " + basicType;
|
||||
return true;
|
||||
}
|
||||
|
||||
public MethodHandle cachedMethodHandle(int which) {
|
||||
assert(assertIsBasicType());
|
||||
SoftReference<MethodHandle> entry = methodHandles[which];
|
||||
return (entry != null) ? entry.get() : null;
|
||||
}
|
||||
|
||||
synchronized public MethodHandle setCachedMethodHandle(int which, MethodHandle mh) {
|
||||
// Simulate a CAS, to avoid racy duplication of results.
|
||||
SoftReference<MethodHandle> entry = methodHandles[which];
|
||||
if (entry != null) {
|
||||
MethodHandle prev = entry.get();
|
||||
if (prev != null) {
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
methodHandles[which] = new SoftReference<>(mh);
|
||||
return mh;
|
||||
}
|
||||
|
||||
public LambdaForm cachedLambdaForm(int which) {
|
||||
assert(assertIsBasicType());
|
||||
SoftReference<LambdaForm> entry = lambdaForms[which];
|
||||
return (entry != null) ? entry.get() : null;
|
||||
}
|
||||
|
||||
synchronized public LambdaForm setCachedLambdaForm(int which, LambdaForm form) {
|
||||
// Simulate a CAS, to avoid racy duplication of results.
|
||||
SoftReference<LambdaForm> entry = lambdaForms[which];
|
||||
if (entry != null) {
|
||||
LambdaForm prev = entry.get();
|
||||
if (prev != null) {
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
lambdaForms[which] = new SoftReference<>(form);
|
||||
return form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an MTF for a given type, which must have all references erased to Object.
|
||||
* This MTF will stand for that type and all un-erased variations.
|
||||
* Eagerly compute some basic properties of the type, common to all variations.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected MethodTypeForm(MethodType erasedType) {
|
||||
this.erasedType = erasedType;
|
||||
|
||||
Class<?>[] ptypes = erasedType.ptypes();
|
||||
int ptypeCount = ptypes.length;
|
||||
int pslotCount = ptypeCount; // temp. estimate
|
||||
int rtypeCount = 1; // temp. estimate
|
||||
int rslotCount = 1; // temp. estimate
|
||||
|
||||
int[] argToSlotTab = null, slotToArgTab = null;
|
||||
|
||||
// Walk the argument types, looking for primitives.
|
||||
int pac = 0, lac = 0, prc = 0, lrc = 0;
|
||||
Class<?>[] epts = ptypes;
|
||||
Class<?>[] bpts = epts;
|
||||
for (int i = 0; i < epts.length; i++) {
|
||||
Class<?> pt = epts[i];
|
||||
if (pt != Object.class) {
|
||||
++pac;
|
||||
Wrapper w = Wrapper.forPrimitiveType(pt);
|
||||
if (w.isDoubleWord()) ++lac;
|
||||
if (w.isSubwordOrInt() && pt != int.class) {
|
||||
if (bpts == epts)
|
||||
bpts = bpts.clone();
|
||||
bpts[i] = int.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
pslotCount += lac; // #slots = #args + #longs
|
||||
Class<?> rt = erasedType.returnType();
|
||||
Class<?> bt = rt;
|
||||
if (rt != Object.class) {
|
||||
++prc; // even void.class counts as a prim here
|
||||
Wrapper w = Wrapper.forPrimitiveType(rt);
|
||||
if (w.isDoubleWord()) ++lrc;
|
||||
if (w.isSubwordOrInt() && rt != int.class)
|
||||
bt = int.class;
|
||||
// adjust #slots, #args
|
||||
if (rt == void.class)
|
||||
rtypeCount = rslotCount = 0;
|
||||
else
|
||||
rslotCount += lrc;
|
||||
}
|
||||
if (epts == bpts && bt == rt) {
|
||||
this.basicType = erasedType;
|
||||
} else {
|
||||
this.basicType = MethodType.makeImpl(bt, bpts, true);
|
||||
// fill in rest of data from the basic type:
|
||||
MethodTypeForm that = this.basicType.form();
|
||||
assert(this != that);
|
||||
this.primCounts = that.primCounts;
|
||||
this.argCounts = that.argCounts;
|
||||
this.argToSlotTable = that.argToSlotTable;
|
||||
this.slotToArgTable = that.slotToArgTable;
|
||||
this.methodHandles = null;
|
||||
this.lambdaForms = null;
|
||||
return;
|
||||
}
|
||||
if (lac != 0) {
|
||||
int slot = ptypeCount + lac;
|
||||
slotToArgTab = new int[slot+1];
|
||||
argToSlotTab = new int[1+ptypeCount];
|
||||
argToSlotTab[0] = slot; // argument "-1" is past end of slots
|
||||
for (int i = 0; i < epts.length; i++) {
|
||||
Class<?> pt = epts[i];
|
||||
Wrapper w = Wrapper.forBasicType(pt);
|
||||
if (w.isDoubleWord()) --slot;
|
||||
--slot;
|
||||
slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
|
||||
argToSlotTab[1+i] = slot;
|
||||
}
|
||||
assert(slot == 0); // filled the table
|
||||
} else if (pac != 0) {
|
||||
// have primitives but no long primitives; share slot counts with generic
|
||||
assert(ptypeCount == pslotCount);
|
||||
MethodTypeForm that = MethodType.genericMethodType(ptypeCount).form();
|
||||
assert(this != that);
|
||||
slotToArgTab = that.slotToArgTable;
|
||||
argToSlotTab = that.argToSlotTable;
|
||||
} else {
|
||||
int slot = ptypeCount; // first arg is deepest in stack
|
||||
slotToArgTab = new int[slot+1];
|
||||
argToSlotTab = new int[1+ptypeCount];
|
||||
argToSlotTab[0] = slot; // argument "-1" is past end of slots
|
||||
for (int i = 0; i < ptypeCount; i++) {
|
||||
--slot;
|
||||
slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
|
||||
argToSlotTab[1+i] = slot;
|
||||
}
|
||||
}
|
||||
this.primCounts = pack(lrc, prc, lac, pac);
|
||||
this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
|
||||
this.argToSlotTable = argToSlotTab;
|
||||
this.slotToArgTable = slotToArgTab;
|
||||
|
||||
if (pslotCount >= 256) throw newIllegalArgumentException("too many arguments");
|
||||
|
||||
// Initialize caches, but only for basic types
|
||||
assert(basicType == erasedType);
|
||||
this.lambdaForms = new SoftReference[LF_LIMIT];
|
||||
this.methodHandles = new SoftReference[MH_LIMIT];
|
||||
}
|
||||
|
||||
private static long pack(int a, int b, int c, int d) {
|
||||
assert(((a|b|c|d) & ~0xFFFF) == 0);
|
||||
long hw = ((a << 16) | b), lw = ((c << 16) | d);
|
||||
return (hw << 32) | lw;
|
||||
}
|
||||
private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d
|
||||
assert(word <= 3);
|
||||
return (char)(packed >> ((3-word) * 16));
|
||||
}
|
||||
|
||||
public int parameterCount() { // # outgoing values
|
||||
return unpack(argCounts, 3);
|
||||
}
|
||||
public int parameterSlotCount() { // # outgoing interpreter slots
|
||||
return unpack(argCounts, 2);
|
||||
}
|
||||
public int returnCount() { // = 0 (V), or 1
|
||||
return unpack(argCounts, 1);
|
||||
}
|
||||
public int returnSlotCount() { // = 0 (V), 2 (J/D), or 1
|
||||
return unpack(argCounts, 0);
|
||||
}
|
||||
public int primitiveParameterCount() {
|
||||
return unpack(primCounts, 3);
|
||||
}
|
||||
public int longPrimitiveParameterCount() {
|
||||
return unpack(primCounts, 2);
|
||||
}
|
||||
public int primitiveReturnCount() { // = 0 (obj), or 1
|
||||
return unpack(primCounts, 1);
|
||||
}
|
||||
public int longPrimitiveReturnCount() { // = 1 (J/D), or 0
|
||||
return unpack(primCounts, 0);
|
||||
}
|
||||
public boolean hasPrimitives() {
|
||||
return primCounts != 0;
|
||||
}
|
||||
public boolean hasNonVoidPrimitives() {
|
||||
if (primCounts == 0) return false;
|
||||
if (primitiveParameterCount() != 0) return true;
|
||||
return (primitiveReturnCount() != 0 && returnCount() != 0);
|
||||
}
|
||||
public boolean hasLongPrimitives() {
|
||||
return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
|
||||
}
|
||||
public int parameterToArgSlot(int i) {
|
||||
return argToSlotTable[1+i];
|
||||
}
|
||||
public int argSlotToParameter(int argSlot) {
|
||||
// Note: Empty slots are represented by zero in this table.
|
||||
// Valid arguments slots contain incremented entries, so as to be non-zero.
|
||||
// We return -1 the caller to mean an empty slot.
|
||||
return slotToArgTable[argSlot] - 1;
|
||||
}
|
||||
|
||||
static MethodTypeForm findForm(MethodType mt) {
|
||||
MethodType erased = canonicalize(mt, ERASE, ERASE);
|
||||
if (erased == null) {
|
||||
// It is already erased. Make a new MethodTypeForm.
|
||||
return new MethodTypeForm(mt);
|
||||
} else {
|
||||
// Share the MethodTypeForm with the erased version.
|
||||
return erased.form();
|
||||
}
|
||||
}
|
||||
|
||||
/** Codes for {@link #canonicalize(java.lang.Class, int)}.
|
||||
* ERASE means change every reference to {@code Object}.
|
||||
* WRAP means convert primitives (including {@code void} to their
|
||||
* corresponding wrapper types. UNWRAP means the reverse of WRAP.
|
||||
* INTS means convert all non-void primitive types to int or long,
|
||||
* according to size. LONGS means convert all non-void primitives
|
||||
* to long, regardless of size. RAW_RETURN means convert a type
|
||||
* (assumed to be a return type) to int if it is smaller than an int,
|
||||
* or if it is void.
|
||||
*/
|
||||
public static final int NO_CHANGE = 0, ERASE = 1, WRAP = 2, UNWRAP = 3, INTS = 4, LONGS = 5, RAW_RETURN = 6;
|
||||
|
||||
/** Canonicalize the types in the given method type.
|
||||
* If any types change, intern the new type, and return it.
|
||||
* Otherwise return null.
|
||||
*/
|
||||
public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
|
||||
Class<?>[] ptypes = mt.ptypes();
|
||||
Class<?>[] ptc = MethodTypeForm.canonicalizeAll(ptypes, howArgs);
|
||||
Class<?> rtype = mt.returnType();
|
||||
Class<?> rtc = MethodTypeForm.canonicalize(rtype, howRet);
|
||||
if (ptc == null && rtc == null) {
|
||||
// It is already canonical.
|
||||
return null;
|
||||
}
|
||||
// Find the erased version of the method type:
|
||||
if (rtc == null) rtc = rtype;
|
||||
if (ptc == null) ptc = ptypes;
|
||||
return MethodType.makeImpl(rtc, ptc, true);
|
||||
}
|
||||
|
||||
/** Canonicalize the given return or param type.
|
||||
* Return null if the type is already canonicalized.
|
||||
*/
|
||||
static Class<?> canonicalize(Class<?> t, int how) {
|
||||
Class<?> ct;
|
||||
if (t == Object.class) {
|
||||
// no change, ever
|
||||
} else if (!t.isPrimitive()) {
|
||||
switch (how) {
|
||||
case UNWRAP:
|
||||
ct = Wrapper.asPrimitiveType(t);
|
||||
if (ct != t) return ct;
|
||||
break;
|
||||
case RAW_RETURN:
|
||||
case ERASE:
|
||||
return Object.class;
|
||||
}
|
||||
} else if (t == void.class) {
|
||||
// no change, usually
|
||||
switch (how) {
|
||||
case RAW_RETURN:
|
||||
return int.class;
|
||||
case WRAP:
|
||||
return Void.class;
|
||||
}
|
||||
} else {
|
||||
// non-void primitive
|
||||
switch (how) {
|
||||
case WRAP:
|
||||
return Wrapper.asWrapperType(t);
|
||||
case INTS:
|
||||
if (t == int.class || t == long.class)
|
||||
return null; // no change
|
||||
if (t == double.class)
|
||||
return long.class;
|
||||
return int.class;
|
||||
case LONGS:
|
||||
if (t == long.class)
|
||||
return null; // no change
|
||||
return long.class;
|
||||
case RAW_RETURN:
|
||||
if (t == int.class || t == long.class ||
|
||||
t == float.class || t == double.class)
|
||||
return null; // no change
|
||||
// everything else returns as an int
|
||||
return int.class;
|
||||
}
|
||||
}
|
||||
// no change; return null to signify
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Canonicalize each param type in the given array.
|
||||
* Return null if all types are already canonicalized.
|
||||
*/
|
||||
static Class<?>[] canonicalizeAll(Class<?>[] ts, int how) {
|
||||
Class<?>[] cs = null;
|
||||
for (int imax = ts.length, i = 0; i < imax; i++) {
|
||||
Class<?> c = canonicalize(ts[i], how);
|
||||
if (c == void.class)
|
||||
c = null; // a Void parameter was unwrapped to void; ignore
|
||||
if (c != null) {
|
||||
if (cs == null)
|
||||
cs = ts.clone();
|
||||
cs[i] = c;
|
||||
}
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Form"+erasedType;
|
||||
}
|
||||
}
|
||||
283
jdkSrc/jdk8/java/lang/invoke/MutableCallSite.java
Normal file
283
jdkSrc/jdk8/java/lang/invoke/MutableCallSite.java
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, 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.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A {@code MutableCallSite} is a {@link CallSite} whose target variable
|
||||
* behaves like an ordinary field.
|
||||
* An {@code invokedynamic} instruction linked to a {@code MutableCallSite} delegates
|
||||
* all calls to the site's current target.
|
||||
* The {@linkplain CallSite#dynamicInvoker dynamic invoker} of a mutable call site
|
||||
* also delegates each call to the site's current target.
|
||||
* <p>
|
||||
* Here is an example of a mutable call site which introduces a
|
||||
* state variable into a method handle chain.
|
||||
* <!-- JavaDocExamplesTest.testMutableCallSite -->
|
||||
* <blockquote><pre>{@code
|
||||
MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
|
||||
MethodHandle MH_name = name.dynamicInvoker();
|
||||
MethodType MT_str1 = MethodType.methodType(String.class);
|
||||
MethodHandle MH_upcase = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "toUpperCase", MT_str1);
|
||||
MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
|
||||
name.setTarget(MethodHandles.constant(String.class, "Rocky"));
|
||||
assertEquals("ROCKY", (String) worker1.invokeExact());
|
||||
name.setTarget(MethodHandles.constant(String.class, "Fred"));
|
||||
assertEquals("FRED", (String) worker1.invokeExact());
|
||||
// (mutation can be continued indefinitely)
|
||||
* }</pre></blockquote>
|
||||
* <p>
|
||||
* The same call site may be used in several places at once.
|
||||
* <blockquote><pre>{@code
|
||||
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
|
||||
MethodHandle MH_cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
|
||||
MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
|
||||
assertEquals("Fred, dear?", (String) worker2.invokeExact());
|
||||
name.setTarget(MethodHandles.constant(String.class, "Wilma"));
|
||||
assertEquals("WILMA", (String) worker1.invokeExact());
|
||||
assertEquals("Wilma, dear?", (String) worker2.invokeExact());
|
||||
* }</pre></blockquote>
|
||||
* <p>
|
||||
* <em>Non-synchronization of target values:</em>
|
||||
* A write to a mutable call site's target does not force other threads
|
||||
* to become aware of the updated value. Threads which do not perform
|
||||
* suitable synchronization actions relative to the updated call site
|
||||
* may cache the old target value and delay their use of the new target
|
||||
* value indefinitely.
|
||||
* (This is a normal consequence of the Java Memory Model as applied
|
||||
* to object fields.)
|
||||
* <p>
|
||||
* The {@link #syncAll syncAll} operation provides a way to force threads
|
||||
* to accept a new target value, even if there is no other synchronization.
|
||||
* <p>
|
||||
* For target values which will be frequently updated, consider using
|
||||
* a {@linkplain VolatileCallSite volatile call site} instead.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class MutableCallSite extends CallSite {
|
||||
/**
|
||||
* Creates a blank call site object with the given method type.
|
||||
* The initial target is set to a method handle of the given type
|
||||
* which will throw an {@link IllegalStateException} if called.
|
||||
* <p>
|
||||
* The type of the call site is permanently set to the given type.
|
||||
* <p>
|
||||
* Before this {@code CallSite} object is returned from a bootstrap method,
|
||||
* or invoked in some other manner,
|
||||
* it is usually provided with a more useful target method,
|
||||
* via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
|
||||
* @param type the method type that this call site will have
|
||||
* @throws NullPointerException if the proposed type is null
|
||||
*/
|
||||
public MutableCallSite(MethodType type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a call site object with an initial target method handle.
|
||||
* The type of the call site is permanently set to the initial target's type.
|
||||
* @param target the method handle that will be the initial target of the call site
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public MutableCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target method of the call site, which behaves
|
||||
* like a normal field of the {@code MutableCallSite}.
|
||||
* <p>
|
||||
* The interactions of {@code getTarget} with memory are the same
|
||||
* as of a read from an ordinary variable, such as an array element or a
|
||||
* non-volatile, non-final field.
|
||||
* <p>
|
||||
* In particular, the current thread may choose to reuse the result
|
||||
* of a previous read of the target from memory, and may fail to see
|
||||
* a recent update to the target by another thread.
|
||||
*
|
||||
* @return the linkage state of this call site, a method handle which can change over time
|
||||
* @see #setTarget
|
||||
*/
|
||||
@Override public final MethodHandle getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the target method of this call site, as a normal variable.
|
||||
* The type of the new target must agree with the type of the old target.
|
||||
* <p>
|
||||
* The interactions with memory are the same
|
||||
* as of a write to an ordinary variable, such as an array element or a
|
||||
* non-volatile, non-final field.
|
||||
* <p>
|
||||
* In particular, unrelated threads may fail to see the updated target
|
||||
* until they perform a read from memory.
|
||||
* Stronger guarantees can be created by putting appropriate operations
|
||||
* into the bootstrap method and/or the target methods used
|
||||
* at any given call site.
|
||||
*
|
||||
* @param newTarget the new target
|
||||
* @throws NullPointerException if the proposed new target is null
|
||||
* @throws WrongMethodTypeException if the proposed new target
|
||||
* has a method type that differs from the previous target
|
||||
* @see #getTarget
|
||||
*/
|
||||
@Override public void setTarget(MethodHandle newTarget) {
|
||||
checkTargetChange(this.target, newTarget);
|
||||
setTargetNormal(newTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public final MethodHandle dynamicInvoker() {
|
||||
return makeDynamicInvoker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a synchronization operation on each call site in the given array,
|
||||
* forcing all other threads to throw away any cached values previously
|
||||
* loaded from the target of any of the call sites.
|
||||
* <p>
|
||||
* This operation does not reverse any calls that have already started
|
||||
* on an old target value.
|
||||
* (Java supports {@linkplain java.lang.Object#wait() forward time travel} only.)
|
||||
* <p>
|
||||
* The overall effect is to force all future readers of each call site's target
|
||||
* to accept the most recently stored value.
|
||||
* ("Most recently" is reckoned relative to the {@code syncAll} itself.)
|
||||
* Conversely, the {@code syncAll} call may block until all readers have
|
||||
* (somehow) decached all previous versions of each call site's target.
|
||||
* <p>
|
||||
* To avoid race conditions, calls to {@code setTarget} and {@code syncAll}
|
||||
* should generally be performed under some sort of mutual exclusion.
|
||||
* Note that reader threads may observe an updated target as early
|
||||
* as the {@code setTarget} call that install the value
|
||||
* (and before the {@code syncAll} that confirms the value).
|
||||
* On the other hand, reader threads may observe previous versions of
|
||||
* the target until the {@code syncAll} call returns
|
||||
* (and after the {@code setTarget} that attempts to convey the updated version).
|
||||
* <p>
|
||||
* This operation is likely to be expensive and should be used sparingly.
|
||||
* If possible, it should be buffered for batch processing on sets of call sites.
|
||||
* <p>
|
||||
* If {@code sites} contains a null element,
|
||||
* a {@code NullPointerException} will be raised.
|
||||
* In this case, some non-null elements in the array may be
|
||||
* processed before the method returns abnormally.
|
||||
* Which elements these are (if any) is implementation-dependent.
|
||||
*
|
||||
* <h1>Java Memory Model details</h1>
|
||||
* In terms of the Java Memory Model, this operation performs a synchronization
|
||||
* action which is comparable in effect to the writing of a volatile variable
|
||||
* by the current thread, and an eventual volatile read by every other thread
|
||||
* that may access one of the affected call sites.
|
||||
* <p>
|
||||
* The following effects are apparent, for each individual call site {@code S}:
|
||||
* <ul>
|
||||
* <li>A new volatile variable {@code V} is created, and written by the current thread.
|
||||
* As defined by the JMM, this write is a global synchronization event.
|
||||
* <li>As is normal with thread-local ordering of write events,
|
||||
* every action already performed by the current thread is
|
||||
* taken to happen before the volatile write to {@code V}.
|
||||
* (In some implementations, this means that the current thread
|
||||
* performs a global release operation.)
|
||||
* <li>Specifically, the write to the current target of {@code S} is
|
||||
* taken to happen before the volatile write to {@code V}.
|
||||
* <li>The volatile write to {@code V} is placed
|
||||
* (in an implementation specific manner)
|
||||
* in the global synchronization order.
|
||||
* <li>Consider an arbitrary thread {@code T} (other than the current thread).
|
||||
* If {@code T} executes a synchronization action {@code A}
|
||||
* after the volatile write to {@code V} (in the global synchronization order),
|
||||
* it is therefore required to see either the current target
|
||||
* of {@code S}, or a later write to that target,
|
||||
* if it executes a read on the target of {@code S}.
|
||||
* (This constraint is called "synchronization-order consistency".)
|
||||
* <li>The JMM specifically allows optimizing compilers to elide
|
||||
* reads or writes of variables that are known to be useless.
|
||||
* Such elided reads and writes have no effect on the happens-before
|
||||
* relation. Regardless of this fact, the volatile {@code V}
|
||||
* will not be elided, even though its written value is
|
||||
* indeterminate and its read value is not used.
|
||||
* </ul>
|
||||
* Because of the last point, the implementation behaves as if a
|
||||
* volatile read of {@code V} were performed by {@code T}
|
||||
* immediately after its action {@code A}. In the local ordering
|
||||
* of actions in {@code T}, this read happens before any future
|
||||
* read of the target of {@code S}. It is as if the
|
||||
* implementation arbitrarily picked a read of {@code S}'s target
|
||||
* by {@code T}, and forced a read of {@code V} to precede it,
|
||||
* thereby ensuring communication of the new target value.
|
||||
* <p>
|
||||
* As long as the constraints of the Java Memory Model are obeyed,
|
||||
* implementations may delay the completion of a {@code syncAll}
|
||||
* operation while other threads ({@code T} above) continue to
|
||||
* use previous values of {@code S}'s target.
|
||||
* However, implementations are (as always) encouraged to avoid
|
||||
* livelock, and to eventually require all threads to take account
|
||||
* of the updated target.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* For performance reasons, {@code syncAll} is not a virtual method
|
||||
* on a single call site, but rather applies to a set of call sites.
|
||||
* Some implementations may incur a large fixed overhead cost
|
||||
* for processing one or more synchronization operations,
|
||||
* but a small incremental cost for each additional call site.
|
||||
* In any case, this operation is likely to be costly, since
|
||||
* other threads may have to be somehow interrupted
|
||||
* in order to make them notice the updated target value.
|
||||
* However, it may be observed that a single call to synchronize
|
||||
* several sites has the same formal effect as many calls,
|
||||
* each on just one of the sites.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Implementation Note:</em>
|
||||
* Simple implementations of {@code MutableCallSite} may use
|
||||
* a volatile variable for the target of a mutable call site.
|
||||
* In such an implementation, the {@code syncAll} method can be a no-op,
|
||||
* and yet it will conform to the JMM behavior documented above.
|
||||
*
|
||||
* @param sites an array of call sites to be synchronized
|
||||
* @throws NullPointerException if the {@code sites} array reference is null
|
||||
* or the array contains a null
|
||||
*/
|
||||
public static void syncAll(MutableCallSite[] sites) {
|
||||
if (sites.length == 0) return;
|
||||
STORE_BARRIER.lazySet(0);
|
||||
for (int i = 0; i < sites.length; i++) {
|
||||
sites[i].getClass(); // trigger NPE on first null
|
||||
}
|
||||
// FIXME: NYI
|
||||
}
|
||||
private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
|
||||
}
|
||||
147
jdkSrc/jdk8/java/lang/invoke/ProxyClassesDumper.java
Normal file
147
jdkSrc/jdk8/java/lang/invoke/ProxyClassesDumper.java
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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 sun.util.logging.PlatformLogger;
|
||||
|
||||
import java.io.FilePermission;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Helper class used by InnerClassLambdaMetafactory to log generated classes
|
||||
*
|
||||
* @implNote
|
||||
* <p> Because this class is called by LambdaMetafactory, make use
|
||||
* of lambda lead to recursive calls cause stack overflow.
|
||||
*/
|
||||
final class ProxyClassesDumper {
|
||||
private static final char[] HEX = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||
};
|
||||
private static final char[] BAD_CHARS = {
|
||||
'\\', ':', '*', '?', '"', '<', '>', '|'
|
||||
};
|
||||
private static final String[] REPLACEMENT = {
|
||||
"%5C", "%3A", "%2A", "%3F", "%22", "%3C", "%3E", "%7C"
|
||||
};
|
||||
|
||||
private final Path dumpDir;
|
||||
|
||||
public static ProxyClassesDumper getInstance(String path) {
|
||||
if (null == path) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
path = path.trim();
|
||||
final Path dir = Paths.get(path.length() == 0 ? "." : path);
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
validateDumpDir(dir);
|
||||
return null;
|
||||
}
|
||||
}, null, new FilePermission("<<ALL FILES>>", "read, write"));
|
||||
return new ProxyClassesDumper(dir);
|
||||
} catch (InvalidPathException ex) {
|
||||
PlatformLogger.getLogger(ProxyClassesDumper.class.getName())
|
||||
.warning("Path " + path + " is not valid - dumping disabled", ex);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
PlatformLogger.getLogger(ProxyClassesDumper.class.getName())
|
||||
.warning(iae.getMessage() + " - dumping disabled");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ProxyClassesDumper(Path path) {
|
||||
dumpDir = Objects.requireNonNull(path);
|
||||
}
|
||||
|
||||
private static void validateDumpDir(Path path) {
|
||||
if (!Files.exists(path)) {
|
||||
throw new IllegalArgumentException("Directory " + path + " does not exist");
|
||||
} else if (!Files.isDirectory(path)) {
|
||||
throw new IllegalArgumentException("Path " + path + " is not a directory");
|
||||
} else if (!Files.isWritable(path)) {
|
||||
throw new IllegalArgumentException("Directory " + path + " is not writable");
|
||||
}
|
||||
}
|
||||
|
||||
public static String encodeForFilename(String className) {
|
||||
final int len = className.length();
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = className.charAt(i);
|
||||
// control characters
|
||||
if (c <= 31) {
|
||||
sb.append('%');
|
||||
sb.append(HEX[c >> 4 & 0x0F]);
|
||||
sb.append(HEX[c & 0x0F]);
|
||||
} else {
|
||||
int j = 0;
|
||||
for (; j < BAD_CHARS.length; j++) {
|
||||
if (c == BAD_CHARS[j]) {
|
||||
sb.append(REPLACEMENT[j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j >= BAD_CHARS.length) {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void dumpClass(String className, final byte[] classBytes) {
|
||||
Path file;
|
||||
try {
|
||||
file = dumpDir.resolve(encodeForFilename(className) + ".class");
|
||||
} catch (InvalidPathException ex) {
|
||||
PlatformLogger.getLogger(ProxyClassesDumper.class.getName())
|
||||
.warning("Invalid path for class " + className);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Path dir = file.getParent();
|
||||
Files.createDirectories(dir);
|
||||
Files.write(file, classBytes);
|
||||
} catch (Exception ignore) {
|
||||
PlatformLogger.getLogger(ProxyClassesDumper.class.getName())
|
||||
.warning("Exception writing to path at " + file.toString());
|
||||
// simply don't care if this operation failed
|
||||
}
|
||||
}
|
||||
}
|
||||
258
jdkSrc/jdk8/java/lang/invoke/SerializedLambda.java
Normal file
258
jdkSrc/jdk8/java/lang/invoke/SerializedLambda.java
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Serialized form of a lambda expression. The properties of this class
|
||||
* represent the information that is present at the lambda factory site, including
|
||||
* static metafactory arguments such as the identity of the primary functional
|
||||
* interface method and the identity of the implementation method, as well as
|
||||
* dynamic metafactory arguments such as values captured from the lexical scope
|
||||
* at the time of lambda capture.
|
||||
*
|
||||
* <p>Implementors of serializable lambdas, such as compilers or language
|
||||
* runtime libraries, are expected to ensure that instances deserialize properly.
|
||||
* One means to do so is to ensure that the {@code writeReplace} method returns
|
||||
* an instance of {@code SerializedLambda}, rather than allowing default
|
||||
* serialization to proceed.
|
||||
*
|
||||
* <p>{@code SerializedLambda} has a {@code readResolve} method that looks for
|
||||
* a (possibly private) static method called
|
||||
* {@code $deserializeLambda$(SerializedLambda)} in the capturing class, invokes
|
||||
* that with itself as the first argument, and returns the result. Lambda classes
|
||||
* implementing {@code $deserializeLambda$} are responsible for validating
|
||||
* that the properties of the {@code SerializedLambda} are consistent with a
|
||||
* lambda actually captured by that class.
|
||||
*
|
||||
* @see LambdaMetafactory
|
||||
*/
|
||||
public final class SerializedLambda implements Serializable {
|
||||
private static final long serialVersionUID = 8025925345765570181L;
|
||||
private final Class<?> capturingClass;
|
||||
private final String functionalInterfaceClass;
|
||||
private final String functionalInterfaceMethodName;
|
||||
private final String functionalInterfaceMethodSignature;
|
||||
private final String implClass;
|
||||
private final String implMethodName;
|
||||
private final String implMethodSignature;
|
||||
private final int implMethodKind;
|
||||
private final String instantiatedMethodType;
|
||||
private final Object[] capturedArgs;
|
||||
|
||||
/**
|
||||
* Create a {@code SerializedLambda} from the low-level information present
|
||||
* at the lambda factory site.
|
||||
*
|
||||
* @param capturingClass The class in which the lambda expression appears
|
||||
* @param functionalInterfaceClass Name, in slash-delimited form, of static
|
||||
* type of the returned lambda object
|
||||
* @param functionalInterfaceMethodName Name of the functional interface
|
||||
* method for the present at the
|
||||
* lambda factory site
|
||||
* @param functionalInterfaceMethodSignature Signature of the functional
|
||||
* interface method present at
|
||||
* the lambda factory site
|
||||
* @param implMethodKind Method handle kind for the implementation method
|
||||
* @param implClass Name, in slash-delimited form, for the class holding
|
||||
* the implementation method
|
||||
* @param implMethodName Name of the implementation method
|
||||
* @param implMethodSignature Signature of the implementation method
|
||||
* @param instantiatedMethodType The signature of the primary functional
|
||||
* interface method after type variables
|
||||
* are substituted with their instantiation
|
||||
* from the capture site
|
||||
* @param capturedArgs The dynamic arguments to the lambda factory site,
|
||||
* which represent variables captured by
|
||||
* the lambda
|
||||
*/
|
||||
public SerializedLambda(Class<?> capturingClass,
|
||||
String functionalInterfaceClass,
|
||||
String functionalInterfaceMethodName,
|
||||
String functionalInterfaceMethodSignature,
|
||||
int implMethodKind,
|
||||
String implClass,
|
||||
String implMethodName,
|
||||
String implMethodSignature,
|
||||
String instantiatedMethodType,
|
||||
Object[] capturedArgs) {
|
||||
this.capturingClass = capturingClass;
|
||||
this.functionalInterfaceClass = functionalInterfaceClass;
|
||||
this.functionalInterfaceMethodName = functionalInterfaceMethodName;
|
||||
this.functionalInterfaceMethodSignature = functionalInterfaceMethodSignature;
|
||||
this.implMethodKind = implMethodKind;
|
||||
this.implClass = implClass;
|
||||
this.implMethodName = implMethodName;
|
||||
this.implMethodSignature = implMethodSignature;
|
||||
this.instantiatedMethodType = instantiatedMethodType;
|
||||
this.capturedArgs = Objects.requireNonNull(capturedArgs).clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the class that captured this lambda.
|
||||
* @return the name of the class that captured this lambda
|
||||
*/
|
||||
public String getCapturingClass() {
|
||||
return capturingClass.getName().replace('.', '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the invoked type to which this
|
||||
* lambda has been converted
|
||||
* @return the name of the functional interface class to which
|
||||
* this lambda has been converted
|
||||
*/
|
||||
public String getFunctionalInterfaceClass() {
|
||||
return functionalInterfaceClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the primary method for the functional interface
|
||||
* to which this lambda has been converted.
|
||||
* @return the name of the primary methods of the functional interface
|
||||
*/
|
||||
public String getFunctionalInterfaceMethodName() {
|
||||
return functionalInterfaceMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the signature of the primary method for the functional
|
||||
* interface to which this lambda has been converted.
|
||||
* @return the signature of the primary method of the functional
|
||||
* interface
|
||||
*/
|
||||
public String getFunctionalInterfaceMethodSignature() {
|
||||
return functionalInterfaceMethodSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the class containing the implementation
|
||||
* method.
|
||||
* @return the name of the class containing the implementation
|
||||
* method
|
||||
*/
|
||||
public String getImplClass() {
|
||||
return implClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the implementation method.
|
||||
* @return the name of the implementation method
|
||||
*/
|
||||
public String getImplMethodName() {
|
||||
return implMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the signature of the implementation method.
|
||||
* @return the signature of the implementation method
|
||||
*/
|
||||
public String getImplMethodSignature() {
|
||||
return implMethodSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the method handle kind (see {@link MethodHandleInfo}) of
|
||||
* the implementation method.
|
||||
* @return the method handle kind of the implementation method
|
||||
*/
|
||||
public int getImplMethodKind() {
|
||||
return implMethodKind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the signature of the primary functional interface method
|
||||
* after type variables are substituted with their instantiation
|
||||
* from the capture site.
|
||||
* @return the signature of the primary functional interface method
|
||||
* after type variable processing
|
||||
*/
|
||||
public final String getInstantiatedMethodType() {
|
||||
return instantiatedMethodType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of dynamic arguments to the lambda capture site.
|
||||
* @return the count of dynamic arguments to the lambda capture site
|
||||
*/
|
||||
public int getCapturedArgCount() {
|
||||
return capturedArgs.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a dynamic argument to the lambda capture site.
|
||||
* @param i the argument to capture
|
||||
* @return a dynamic argument to the lambda capture site
|
||||
*/
|
||||
public Object getCapturedArg(int i) {
|
||||
return capturedArgs[i];
|
||||
}
|
||||
|
||||
private Object readResolve() throws ReflectiveOperationException {
|
||||
try {
|
||||
Method deserialize = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
|
||||
@Override
|
||||
public Method run() throws Exception {
|
||||
Method m = capturingClass.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class);
|
||||
m.setAccessible(true);
|
||||
return m;
|
||||
}
|
||||
});
|
||||
|
||||
return deserialize.invoke(null, this);
|
||||
}
|
||||
catch (PrivilegedActionException e) {
|
||||
Exception cause = e.getException();
|
||||
if (cause instanceof ReflectiveOperationException)
|
||||
throw (ReflectiveOperationException) cause;
|
||||
else if (cause instanceof RuntimeException)
|
||||
throw (RuntimeException) cause;
|
||||
else
|
||||
throw new RuntimeException("Exception in SerializedLambda.readResolve", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String implKind=MethodHandleInfo.referenceKindToString(implMethodKind);
|
||||
return String.format("SerializedLambda[%s=%s, %s=%s.%s:%s, " +
|
||||
"%s=%s %s.%s:%s, %s=%s, %s=%d]",
|
||||
"capturingClass", capturingClass,
|
||||
"functionalInterfaceMethod", functionalInterfaceClass,
|
||||
functionalInterfaceMethodName,
|
||||
functionalInterfaceMethodSignature,
|
||||
"implementation",
|
||||
implKind,
|
||||
implClass, implMethodName, implMethodSignature,
|
||||
"instantiatedMethodType", instantiatedMethodType,
|
||||
"numCaptured", capturedArgs.length);
|
||||
}
|
||||
}
|
||||
101
jdkSrc/jdk8/java/lang/invoke/SimpleMethodHandle.java
Normal file
101
jdkSrc/jdk8/java/lang/invoke/SimpleMethodHandle.java
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 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 static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
* A method handle whose behavior is determined only by its LambdaForm.
|
||||
* @author jrose
|
||||
*/
|
||||
final class SimpleMethodHandle extends BoundMethodHandle {
|
||||
private SimpleMethodHandle(MethodType type, LambdaForm form) {
|
||||
super(type, form);
|
||||
}
|
||||
|
||||
/*non-public*/ static BoundMethodHandle make(MethodType type, LambdaForm form) {
|
||||
return new SimpleMethodHandle(type, form);
|
||||
}
|
||||
|
||||
/*non-public*/ static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
|
||||
|
||||
/*non-public*/ public SpeciesData speciesData() {
|
||||
return SPECIES_DATA;
|
||||
}
|
||||
|
||||
@Override
|
||||
/*non-public*/ BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||
return make(mt, lf);
|
||||
}
|
||||
|
||||
@Override
|
||||
String internalProperties() {
|
||||
return "\n& Class="+getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
/*non-public*/ public int fieldCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
|
||||
return BoundMethodHandle.bindSingle(mt, lf, narg); // Use known fast path.
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
/*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
|
||||
try {
|
||||
return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, narg);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
jdkSrc/jdk8/java/lang/invoke/Stable.java
Normal file
73
jdkSrc/jdk8/java/lang/invoke/Stable.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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.*;
|
||||
|
||||
/**
|
||||
* A field may be annotated as stable if all of its component variables
|
||||
* changes value at most once.
|
||||
* A field's value counts as its component value.
|
||||
* If the field is typed as an array, then all the non-null components
|
||||
* of the array, of depth up to the rank of the field's array type,
|
||||
* also count as component values.
|
||||
* By extension, any variable (either array or field) which has annotated
|
||||
* as stable is called a stable variable, and its non-null or non-zero
|
||||
* value is called a stable value.
|
||||
* <p>
|
||||
* Since all fields begin with a default value of null for references
|
||||
* (resp., zero for primitives), it follows that this annotation indicates
|
||||
* that the first non-null (resp., non-zero) value stored in the field
|
||||
* will never be changed.
|
||||
* <p>
|
||||
* If the field is not of an array type, there are no array elements,
|
||||
* then the value indicated as stable is simply the value of the field.
|
||||
* If the dynamic type of the field value is an array but the static type
|
||||
* is not, the components of the array are <em>not</em> regarded as stable.
|
||||
* <p>
|
||||
* If the field is an array type, then both the field value and
|
||||
* all the components of the field value (if the field value is non-null)
|
||||
* are indicated to be stable.
|
||||
* If the field type is an array type with rank {@code N > 1},
|
||||
* then each component of the field value (if the field value is non-null),
|
||||
* is regarded as a stable array of rank {@code N-1}.
|
||||
* <p>
|
||||
* Fields which are declared {@code final} may also be annotated as stable.
|
||||
* Since final fields already behave as stable values, such an annotation
|
||||
* indicates no additional information, unless the type of the field is
|
||||
* an array type.
|
||||
* <p>
|
||||
* It is (currently) undefined what happens if a field annotated as stable
|
||||
* is given a third value. In practice, if the JVM relies on this annotation
|
||||
* to promote a field reference to a constant, it may be that the Java memory
|
||||
* model would appear to be broken, if such a constant (the second value of the field)
|
||||
* is used as the value of the field even after the field value has changed.
|
||||
*/
|
||||
/* package-private */
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Stable {
|
||||
}
|
||||
228
jdkSrc/jdk8/java/lang/invoke/SwitchPoint.java
Normal file
228
jdkSrc/jdk8/java/lang/invoke/SwitchPoint.java
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, 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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A {@code SwitchPoint} is an object which can publish state transitions to other threads.
|
||||
* A switch point is initially in the <em>valid</em> state, but may at any time be
|
||||
* changed to the <em>invalid</em> state. Invalidation cannot be reversed.
|
||||
* A switch point can combine a <em>guarded pair</em> of method handles into a
|
||||
* <em>guarded delegator</em>.
|
||||
* The guarded delegator is a method handle which delegates to one of the old method handles.
|
||||
* The state of the switch point determines which of the two gets the delegation.
|
||||
* <p>
|
||||
* A single switch point may be used to control any number of method handles.
|
||||
* (Indirectly, therefore, it can control any number of call sites.)
|
||||
* This is done by using the single switch point as a factory for combining
|
||||
* any number of guarded method handle pairs into guarded delegators.
|
||||
* <p>
|
||||
* When a guarded delegator is created from a guarded pair, the pair
|
||||
* is wrapped in a new method handle {@code M},
|
||||
* which is permanently associated with the switch point that created it.
|
||||
* Each pair consists of a target {@code T} and a fallback {@code F}.
|
||||
* While the switch point is valid, invocations to {@code M} are delegated to {@code T}.
|
||||
* After it is invalidated, invocations are delegated to {@code F}.
|
||||
* <p>
|
||||
* Invalidation is global and immediate, as if the switch point contained a
|
||||
* volatile boolean variable consulted on every call to {@code M}.
|
||||
* The invalidation is also permanent, which means the switch point
|
||||
* can change state only once.
|
||||
* The switch point will always delegate to {@code F} after being invalidated.
|
||||
* At that point {@code guardWithTest} may ignore {@code T} and return {@code F}.
|
||||
* <p>
|
||||
* Here is an example of a switch point in action:
|
||||
* <pre>{@code
|
||||
* MethodHandle MH_strcat = MethodHandles.lookup()
|
||||
* .findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
|
||||
* SwitchPoint spt = new SwitchPoint();
|
||||
* assert(!spt.hasBeenInvalidated());
|
||||
* // the following steps may be repeated to re-use the same switch point:
|
||||
* MethodHandle worker1 = MH_strcat;
|
||||
* MethodHandle worker2 = MethodHandles.permuteArguments(MH_strcat, MH_strcat.type(), 1, 0);
|
||||
* MethodHandle worker = spt.guardWithTest(worker1, worker2);
|
||||
* assertEquals("method", (String) worker.invokeExact("met", "hod"));
|
||||
* SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
|
||||
* assert(spt.hasBeenInvalidated());
|
||||
* assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
|
||||
* }</pre>
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* Switch points are useful without subclassing. They may also be subclassed.
|
||||
* This may be useful in order to associate application-specific invalidation logic
|
||||
* with the switch point.
|
||||
* Notice that there is no permanent association between a switch point and
|
||||
* the method handles it produces and consumes.
|
||||
* The garbage collector may collect method handles produced or consumed
|
||||
* by a switch point independently of the lifetime of the switch point itself.
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Implementation Note:</em>
|
||||
* A switch point behaves as if implemented on top of {@link MutableCallSite},
|
||||
* approximately as follows:
|
||||
* <pre>{@code
|
||||
* public class SwitchPoint {
|
||||
* private static final MethodHandle
|
||||
* K_true = MethodHandles.constant(boolean.class, true),
|
||||
* K_false = MethodHandles.constant(boolean.class, false);
|
||||
* private final MutableCallSite mcs;
|
||||
* private final MethodHandle mcsInvoker;
|
||||
* public SwitchPoint() {
|
||||
* this.mcs = new MutableCallSite(K_true);
|
||||
* this.mcsInvoker = mcs.dynamicInvoker();
|
||||
* }
|
||||
* public MethodHandle guardWithTest(
|
||||
* MethodHandle target, MethodHandle fallback) {
|
||||
* // Note: mcsInvoker is of type ()boolean.
|
||||
* // Target and fallback may take any arguments, but must have the same type.
|
||||
* return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback);
|
||||
* }
|
||||
* public static void invalidateAll(SwitchPoint[] spts) {
|
||||
* List<MutableCallSite> mcss = new ArrayList<>();
|
||||
* for (SwitchPoint spt : spts) mcss.add(spt.mcs);
|
||||
* for (MutableCallSite mcs : mcss) mcs.setTarget(K_false);
|
||||
* MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0]));
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
* @author Remi Forax, JSR 292 EG
|
||||
*/
|
||||
public class SwitchPoint {
|
||||
private static final MethodHandle
|
||||
K_true = MethodHandles.constant(boolean.class, true),
|
||||
K_false = MethodHandles.constant(boolean.class, false);
|
||||
|
||||
private final MutableCallSite mcs;
|
||||
private final MethodHandle mcsInvoker;
|
||||
|
||||
/**
|
||||
* Creates a new switch point.
|
||||
*/
|
||||
public SwitchPoint() {
|
||||
this.mcs = new MutableCallSite(K_true);
|
||||
this.mcsInvoker = mcs.dynamicInvoker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this switch point has been invalidated yet.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* Because of the one-way nature of invalidation, once a switch point begins
|
||||
* to return true for {@code hasBeenInvalidated},
|
||||
* it will always do so in the future.
|
||||
* On the other hand, a valid switch point visible to other threads may
|
||||
* be invalidated at any moment, due to a request by another thread.
|
||||
* <p style="font-size:smaller;">
|
||||
* Since invalidation is a global and immediate operation,
|
||||
* the execution of this query, on a valid switchpoint,
|
||||
* must be internally sequenced with any
|
||||
* other threads that could cause invalidation.
|
||||
* This query may therefore be expensive.
|
||||
* The recommended way to build a boolean-valued method handle
|
||||
* which queries the invalidation state of a switch point {@code s} is
|
||||
* to call {@code s.guardWithTest} on
|
||||
* {@link MethodHandles#constant constant} true and false method handles.
|
||||
*
|
||||
* @return true if this switch point has been invalidated
|
||||
*/
|
||||
public boolean hasBeenInvalidated() {
|
||||
return (mcs.getTarget() != K_true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a method handle which always delegates either to the target or the fallback.
|
||||
* The method handle will delegate to the target exactly as long as the switch point is valid.
|
||||
* After that, it will permanently delegate to the fallback.
|
||||
* <p>
|
||||
* The target and fallback must be of exactly the same method type,
|
||||
* and the resulting combined method handle will also be of this type.
|
||||
*
|
||||
* @param target the method handle selected by the switch point as long as it is valid
|
||||
* @param fallback the method handle selected by the switch point after it is invalidated
|
||||
* @return a combined method handle which always calls either the target or fallback
|
||||
* @throws NullPointerException if either argument is null
|
||||
* @throws IllegalArgumentException if the two method types do not match
|
||||
* @see MethodHandles#guardWithTest
|
||||
*/
|
||||
public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) {
|
||||
if (mcs.getTarget() == K_false)
|
||||
return fallback; // already invalid
|
||||
return MethodHandles.guardWithTest(mcsInvoker, target, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all of the given switch points into the invalid state.
|
||||
* After this call executes, no thread will observe any of the
|
||||
* switch points to be in a valid state.
|
||||
* <p>
|
||||
* This operation is likely to be expensive and should be used sparingly.
|
||||
* If possible, it should be buffered for batch processing on sets of switch points.
|
||||
* <p>
|
||||
* If {@code switchPoints} contains a null element,
|
||||
* a {@code NullPointerException} will be raised.
|
||||
* In this case, some non-null elements in the array may be
|
||||
* processed before the method returns abnormally.
|
||||
* Which elements these are (if any) is implementation-dependent.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* For performance reasons, {@code invalidateAll} is not a virtual method
|
||||
* on a single switch point, but rather applies to a set of switch points.
|
||||
* Some implementations may incur a large fixed overhead cost
|
||||
* for processing one or more invalidation operations,
|
||||
* but a small incremental cost for each additional invalidation.
|
||||
* In any case, this operation is likely to be costly, since
|
||||
* other threads may have to be somehow interrupted
|
||||
* in order to make them notice the updated switch point state.
|
||||
* However, it may be observed that a single call to invalidate
|
||||
* several switch points has the same formal effect as many calls,
|
||||
* each on just one of the switch points.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Implementation Note:</em>
|
||||
* Simple implementations of {@code SwitchPoint} may use
|
||||
* a private {@link MutableCallSite} to publish the state of a switch point.
|
||||
* In such an implementation, the {@code invalidateAll} method can
|
||||
* simply change the call site's target, and issue one call to
|
||||
* {@linkplain MutableCallSite#syncAll synchronize} all the
|
||||
* private call sites.
|
||||
*
|
||||
* @param switchPoints an array of call sites to be synchronized
|
||||
* @throws NullPointerException if the {@code switchPoints} array reference is null
|
||||
* or the array contains a null
|
||||
*/
|
||||
public static void invalidateAll(SwitchPoint[] switchPoints) {
|
||||
if (switchPoints.length == 0) return;
|
||||
MutableCallSite[] sites = new MutableCallSite[switchPoints.length];
|
||||
for (int i = 0; i < switchPoints.length; i++) {
|
||||
SwitchPoint spt = switchPoints[i];
|
||||
if (spt == null) break; // MSC.syncAll will trigger a NPE
|
||||
sites[i] = spt.mcs;
|
||||
spt.mcs.setTarget(K_false);
|
||||
}
|
||||
MutableCallSite.syncAll(sites);
|
||||
}
|
||||
}
|
||||
302
jdkSrc/jdk8/java/lang/invoke/TypeConvertingMethodAdapter.java
Normal file
302
jdkSrc/jdk8/java/lang/invoke/TypeConvertingMethodAdapter.java
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2013, 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 jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import sun.invoke.util.BytecodeDescriptor;
|
||||
import sun.invoke.util.Wrapper;
|
||||
import static sun.invoke.util.Wrapper.*;
|
||||
|
||||
class TypeConvertingMethodAdapter extends MethodVisitor {
|
||||
|
||||
TypeConvertingMethodAdapter(MethodVisitor mv) {
|
||||
super(Opcodes.ASM5, mv);
|
||||
}
|
||||
|
||||
private static final int NUM_WRAPPERS = Wrapper.values().length;
|
||||
|
||||
private static final String NAME_OBJECT = "java/lang/Object";
|
||||
private static final String WRAPPER_PREFIX = "Ljava/lang/";
|
||||
|
||||
// Same for all primitives; name of the boxing method
|
||||
private static final String NAME_BOX_METHOD = "valueOf";
|
||||
|
||||
// Table of opcodes for widening primitive conversions; NOP = no conversion
|
||||
private static final int[][] wideningOpcodes = new int[NUM_WRAPPERS][NUM_WRAPPERS];
|
||||
|
||||
private static final Wrapper[] FROM_WRAPPER_NAME = new Wrapper[16];
|
||||
|
||||
// Table of wrappers for primitives, indexed by ASM type sorts
|
||||
private static final Wrapper[] FROM_TYPE_SORT = new Wrapper[16];
|
||||
|
||||
static {
|
||||
for (Wrapper w : Wrapper.values()) {
|
||||
if (w.basicTypeChar() != 'L') {
|
||||
int wi = hashWrapperName(w.wrapperSimpleName());
|
||||
assert (FROM_WRAPPER_NAME[wi] == null);
|
||||
FROM_WRAPPER_NAME[wi] = w;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_WRAPPERS; i++) {
|
||||
for (int j = 0; j < NUM_WRAPPERS; j++) {
|
||||
wideningOpcodes[i][j] = Opcodes.NOP;
|
||||
}
|
||||
}
|
||||
|
||||
initWidening(LONG, Opcodes.I2L, BYTE, SHORT, INT, CHAR);
|
||||
initWidening(LONG, Opcodes.F2L, FLOAT);
|
||||
initWidening(FLOAT, Opcodes.I2F, BYTE, SHORT, INT, CHAR);
|
||||
initWidening(FLOAT, Opcodes.L2F, LONG);
|
||||
initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR);
|
||||
initWidening(DOUBLE, Opcodes.F2D, FLOAT);
|
||||
initWidening(DOUBLE, Opcodes.L2D, LONG);
|
||||
|
||||
FROM_TYPE_SORT[Type.BYTE] = Wrapper.BYTE;
|
||||
FROM_TYPE_SORT[Type.SHORT] = Wrapper.SHORT;
|
||||
FROM_TYPE_SORT[Type.INT] = Wrapper.INT;
|
||||
FROM_TYPE_SORT[Type.LONG] = Wrapper.LONG;
|
||||
FROM_TYPE_SORT[Type.CHAR] = Wrapper.CHAR;
|
||||
FROM_TYPE_SORT[Type.FLOAT] = Wrapper.FLOAT;
|
||||
FROM_TYPE_SORT[Type.DOUBLE] = Wrapper.DOUBLE;
|
||||
FROM_TYPE_SORT[Type.BOOLEAN] = Wrapper.BOOLEAN;
|
||||
}
|
||||
|
||||
private static void initWidening(Wrapper to, int opcode, Wrapper... from) {
|
||||
for (Wrapper f : from) {
|
||||
wideningOpcodes[f.ordinal()][to.ordinal()] = opcode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class name to Wrapper hash, derived from Wrapper.hashWrap()
|
||||
* @param xn
|
||||
* @return The hash code 0-15
|
||||
*/
|
||||
private static int hashWrapperName(String xn) {
|
||||
if (xn.length() < 3) {
|
||||
return 0;
|
||||
}
|
||||
return (3 * xn.charAt(1) + xn.charAt(2)) % 16;
|
||||
}
|
||||
|
||||
private Wrapper wrapperOrNullFromDescriptor(String desc) {
|
||||
if (!desc.startsWith(WRAPPER_PREFIX)) {
|
||||
// Not a class type (array or method), so not a boxed type
|
||||
// or not in the right package
|
||||
return null;
|
||||
}
|
||||
// Pare it down to the simple class name
|
||||
String cname = desc.substring(WRAPPER_PREFIX.length(), desc.length() - 1);
|
||||
// Hash to a Wrapper
|
||||
Wrapper w = FROM_WRAPPER_NAME[hashWrapperName(cname)];
|
||||
if (w == null || w.wrapperSimpleName().equals(cname)) {
|
||||
return w;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String wrapperName(Wrapper w) {
|
||||
return "java/lang/" + w.wrapperSimpleName();
|
||||
}
|
||||
|
||||
private static String unboxMethod(Wrapper w) {
|
||||
return w.primitiveSimpleName() + "Value";
|
||||
}
|
||||
|
||||
private static String boxingDescriptor(Wrapper w) {
|
||||
return String.format("(%s)L%s;", w.basicTypeChar(), wrapperName(w));
|
||||
}
|
||||
|
||||
private static String unboxingDescriptor(Wrapper w) {
|
||||
return "()" + w.basicTypeChar();
|
||||
}
|
||||
|
||||
void boxIfTypePrimitive(Type t) {
|
||||
Wrapper w = FROM_TYPE_SORT[t.getSort()];
|
||||
if (w != null) {
|
||||
box(w);
|
||||
}
|
||||
}
|
||||
|
||||
void widen(Wrapper ws, Wrapper wt) {
|
||||
if (ws != wt) {
|
||||
int opcode = wideningOpcodes[ws.ordinal()][wt.ordinal()];
|
||||
if (opcode != Opcodes.NOP) {
|
||||
visitInsn(opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void box(Wrapper w) {
|
||||
visitMethodInsn(Opcodes.INVOKESTATIC,
|
||||
wrapperName(w),
|
||||
NAME_BOX_METHOD,
|
||||
boxingDescriptor(w), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert types by unboxing. The source type is known to be a primitive wrapper.
|
||||
* @param sname A primitive wrapper corresponding to wrapped reference source type
|
||||
* @param wt A primitive wrapper being converted to
|
||||
*/
|
||||
void unbox(String sname, Wrapper wt) {
|
||||
visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
||||
sname,
|
||||
unboxMethod(wt),
|
||||
unboxingDescriptor(wt), false);
|
||||
}
|
||||
|
||||
private String descriptorToName(String desc) {
|
||||
int last = desc.length() - 1;
|
||||
if (desc.charAt(0) == 'L' && desc.charAt(last) == ';') {
|
||||
// In descriptor form
|
||||
return desc.substring(1, last);
|
||||
} else {
|
||||
// Already in internal name form
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
void cast(String ds, String dt) {
|
||||
String ns = descriptorToName(ds);
|
||||
String nt = descriptorToName(dt);
|
||||
if (!nt.equals(ns) && !nt.equals(NAME_OBJECT)) {
|
||||
visitTypeInsn(Opcodes.CHECKCAST, nt);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPrimitive(Wrapper w) {
|
||||
return w != OBJECT;
|
||||
}
|
||||
|
||||
private Wrapper toWrapper(String desc) {
|
||||
char first = desc.charAt(0);
|
||||
if (first == '[' || first == '(') {
|
||||
first = 'L';
|
||||
}
|
||||
return Wrapper.forBasicType(first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an argument of type 'arg' to be passed to 'target' assuring that it is 'functional'.
|
||||
* Insert the needed conversion instructions in the method code.
|
||||
* @param arg
|
||||
* @param target
|
||||
* @param functional
|
||||
*/
|
||||
void convertType(Class<?> arg, Class<?> target, Class<?> functional) {
|
||||
if (arg.equals(target) && arg.equals(functional)) {
|
||||
return;
|
||||
}
|
||||
if (arg == Void.TYPE || target == Void.TYPE) {
|
||||
return;
|
||||
}
|
||||
if (arg.isPrimitive()) {
|
||||
Wrapper wArg = Wrapper.forPrimitiveType(arg);
|
||||
if (target.isPrimitive()) {
|
||||
// Both primitives: widening
|
||||
widen(wArg, Wrapper.forPrimitiveType(target));
|
||||
} else {
|
||||
// Primitive argument to reference target
|
||||
String dTarget = BytecodeDescriptor.unparse(target);
|
||||
Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget);
|
||||
if (wPrimTarget != null) {
|
||||
// The target is a boxed primitive type, widen to get there before boxing
|
||||
widen(wArg, wPrimTarget);
|
||||
box(wPrimTarget);
|
||||
} else {
|
||||
// Otherwise, box and cast
|
||||
box(wArg);
|
||||
cast(wrapperName(wArg), dTarget);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String dArg = BytecodeDescriptor.unparse(arg);
|
||||
String dSrc;
|
||||
if (functional.isPrimitive()) {
|
||||
dSrc = dArg;
|
||||
} else {
|
||||
// Cast to convert to possibly more specific type, and generate CCE for invalid arg
|
||||
dSrc = BytecodeDescriptor.unparse(functional);
|
||||
cast(dArg, dSrc);
|
||||
}
|
||||
String dTarget = BytecodeDescriptor.unparse(target);
|
||||
if (target.isPrimitive()) {
|
||||
Wrapper wTarget = toWrapper(dTarget);
|
||||
// Reference argument to primitive target
|
||||
Wrapper wps = wrapperOrNullFromDescriptor(dSrc);
|
||||
if (wps != null) {
|
||||
if (wps.isSigned() || wps.isFloating()) {
|
||||
// Boxed number to primitive
|
||||
unbox(wrapperName(wps), wTarget);
|
||||
} else {
|
||||
// Character or Boolean
|
||||
unbox(wrapperName(wps), wps);
|
||||
widen(wps, wTarget);
|
||||
}
|
||||
} else {
|
||||
// Source type is reference type, but not boxed type,
|
||||
// assume it is super type of target type
|
||||
String intermediate;
|
||||
if (wTarget.isSigned() || wTarget.isFloating()) {
|
||||
// Boxed number to primitive
|
||||
intermediate = "java/lang/Number";
|
||||
} else {
|
||||
// Character or Boolean
|
||||
intermediate = wrapperName(wTarget);
|
||||
}
|
||||
cast(dSrc, intermediate);
|
||||
unbox(intermediate, wTarget);
|
||||
}
|
||||
} else {
|
||||
// Both reference types: just case to target type
|
||||
cast(dSrc, dTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The following method is copied from
|
||||
* org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small
|
||||
* and fast Java bytecode manipulation framework.
|
||||
* Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
|
||||
*/
|
||||
void iconst(final int cst) {
|
||||
if (cst >= -1 && cst <= 5) {
|
||||
mv.visitInsn(Opcodes.ICONST_0 + cst);
|
||||
} else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.BIPUSH, cst);
|
||||
} else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.SIPUSH, cst);
|
||||
} else {
|
||||
mv.visitLdcInsn(cst);
|
||||
}
|
||||
}
|
||||
}
|
||||
109
jdkSrc/jdk8/java/lang/invoke/VolatileCallSite.java
Normal file
109
jdkSrc/jdk8/java/lang/invoke/VolatileCallSite.java
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 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;
|
||||
|
||||
/**
|
||||
* A {@code VolatileCallSite} is a {@link CallSite} whose target acts like a volatile variable.
|
||||
* An {@code invokedynamic} instruction linked to a {@code VolatileCallSite} sees updates
|
||||
* to its call site target immediately, even if the update occurs in another thread.
|
||||
* There may be a performance penalty for such tight coupling between threads.
|
||||
* <p>
|
||||
* Unlike {@code MutableCallSite}, there is no
|
||||
* {@linkplain MutableCallSite#syncAll syncAll operation} on volatile
|
||||
* call sites, since every write to a volatile variable is implicitly
|
||||
* synchronized with reader threads.
|
||||
* <p>
|
||||
* In other respects, a {@code VolatileCallSite} is interchangeable
|
||||
* with {@code MutableCallSite}.
|
||||
* @see MutableCallSite
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class VolatileCallSite extends CallSite {
|
||||
/**
|
||||
* Creates a call site with a volatile binding to its target.
|
||||
* The initial target is set to a method handle
|
||||
* of the given type which will throw an {@code IllegalStateException} if called.
|
||||
* @param type the method type that this call site will have
|
||||
* @throws NullPointerException if the proposed type is null
|
||||
*/
|
||||
public VolatileCallSite(MethodType type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a call site with a volatile binding to its target.
|
||||
* The target is set to the given value.
|
||||
* @param target the method handle that will be the initial target of the call site
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public VolatileCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target method of the call site, which behaves
|
||||
* like a {@code volatile} field of the {@code VolatileCallSite}.
|
||||
* <p>
|
||||
* The interactions of {@code getTarget} with memory are the same
|
||||
* as of a read from a {@code volatile} field.
|
||||
* <p>
|
||||
* In particular, the current thread is required to issue a fresh
|
||||
* read of the target from memory, and must not fail to see
|
||||
* a recent update to the target by another thread.
|
||||
*
|
||||
* @return the linkage state of this call site, a method handle which can change over time
|
||||
* @see #setTarget
|
||||
*/
|
||||
@Override public final MethodHandle getTarget() {
|
||||
return getTargetVolatile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the target method of this call site, as a volatile variable.
|
||||
* The type of the new target must agree with the type of the old target.
|
||||
* <p>
|
||||
* The interactions with memory are the same as of a write to a volatile field.
|
||||
* In particular, any threads is guaranteed to see the updated target
|
||||
* the next time it calls {@code getTarget}.
|
||||
* @param newTarget the new target
|
||||
* @throws NullPointerException if the proposed new target is null
|
||||
* @throws WrongMethodTypeException if the proposed new target
|
||||
* has a method type that differs from the previous target
|
||||
* @see #getTarget
|
||||
*/
|
||||
@Override public void setTarget(MethodHandle newTarget) {
|
||||
checkTargetChange(getTargetVolatile(), newTarget);
|
||||
setTargetVolatile(newTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public final MethodHandle dynamicInvoker() {
|
||||
return makeDynamicInvoker();
|
||||
}
|
||||
}
|
||||
85
jdkSrc/jdk8/java/lang/invoke/WrongMethodTypeException.java
Normal file
85
jdkSrc/jdk8/java/lang/invoke/WrongMethodTypeException.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 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;
|
||||
|
||||
/**
|
||||
* Thrown to indicate that code has attempted to call a method handle
|
||||
* via the wrong method type. As with the bytecode representation of
|
||||
* normal Java method calls, method handle calls are strongly typed
|
||||
* to a specific type descriptor associated with a call site.
|
||||
* <p>
|
||||
* This exception may also be thrown when two method handles are
|
||||
* composed, and the system detects that their types cannot be
|
||||
* matched up correctly. This amounts to an early evaluation
|
||||
* of the type mismatch, at method handle construction time,
|
||||
* instead of when the mismatched method handle is called.
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
*/
|
||||
public class WrongMethodTypeException extends RuntimeException {
|
||||
private static final long serialVersionUID = 292L;
|
||||
|
||||
/**
|
||||
* Constructs a {@code WrongMethodTypeException} with no detail message.
|
||||
*/
|
||||
public WrongMethodTypeException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code WrongMethodTypeException} with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public WrongMethodTypeException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code WrongMethodTypeException} with the specified
|
||||
* detail message and cause.
|
||||
*
|
||||
* @param s the detail message.
|
||||
* @param cause the cause of the exception, or null.
|
||||
*/
|
||||
//FIXME: make this public in MR1
|
||||
/*non-public*/ WrongMethodTypeException(String s, Throwable cause) {
|
||||
super(s, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code WrongMethodTypeException} with the specified
|
||||
* cause.
|
||||
*
|
||||
* @param cause the cause of the exception, or null.
|
||||
*/
|
||||
//FIXME: make this public in MR1
|
||||
/*non-public*/ WrongMethodTypeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
211
jdkSrc/jdk8/java/lang/invoke/package-info.java
Normal file
211
jdkSrc/jdk8/java/lang/invoke/package-info.java
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2013, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The {@code java.lang.invoke} package contains dynamic language support provided directly by
|
||||
* the Java core class libraries and virtual machine.
|
||||
*
|
||||
* <p>
|
||||
* As described in the Java Virtual Machine Specification,
|
||||
* certain types in this package have special relations to dynamic
|
||||
* language support in the virtual machine:
|
||||
* <ul>
|
||||
* <li>The class {@link java.lang.invoke.MethodHandle MethodHandle} contains
|
||||
* <a href="MethodHandle.html#sigpoly">signature polymorphic methods</a>
|
||||
* which can be linked regardless of their type descriptor.
|
||||
* Normally, method linkage requires exact matching of type descriptors.
|
||||
* </li>
|
||||
*
|
||||
* <li>The JVM bytecode format supports immediate constants of
|
||||
* the classes {@link java.lang.invoke.MethodHandle MethodHandle} and {@link java.lang.invoke.MethodType MethodType}.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h1><a name="jvm_mods"></a>Summary of relevant Java Virtual Machine changes</h1>
|
||||
* The following low-level information summarizes relevant parts of the
|
||||
* Java Virtual Machine specification. For full details, please see the
|
||||
* current version of that specification.
|
||||
*
|
||||
* Each occurrence of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>.
|
||||
* <h2><a name="indyinsn"></a>{@code invokedynamic} instructions</h2>
|
||||
* A dynamic call site is originally in an unlinked state. In this state, there is
|
||||
* no target method for the call site to invoke.
|
||||
* <p>
|
||||
* Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
|
||||
* the call site must first be <em>linked</em>.
|
||||
* Linking is accomplished by calling a <em>bootstrap method</em>
|
||||
* which is given the static information content of the call site,
|
||||
* and which must produce a {@link java.lang.invoke.MethodHandle method handle}
|
||||
* that gives the behavior of the call site.
|
||||
* <p>
|
||||
* Each {@code invokedynamic} instruction statically specifies its own
|
||||
* bootstrap method as a constant pool reference.
|
||||
* The constant pool reference also specifies the call site's name and type descriptor,
|
||||
* just like {@code invokevirtual} and the other invoke instructions.
|
||||
* <p>
|
||||
* Linking starts with resolving the constant pool entry for the
|
||||
* bootstrap method, and resolving a {@link java.lang.invoke.MethodType MethodType} object for
|
||||
* the type descriptor of the dynamic call site.
|
||||
* This resolution process may trigger class loading.
|
||||
* It may therefore throw an error if a class fails to load.
|
||||
* This error becomes the abnormal termination of the dynamic
|
||||
* call site execution.
|
||||
* Linkage does not trigger class initialization.
|
||||
* <p>
|
||||
* The bootstrap method is invoked on at least three values:
|
||||
* <ul>
|
||||
* <li>a {@code MethodHandles.Lookup}, a lookup object on the <em>caller class</em> in which dynamic call site occurs </li>
|
||||
* <li>a {@code String}, the method name mentioned in the call site </li>
|
||||
* <li>a {@code MethodType}, the resolved type descriptor of the call </li>
|
||||
* <li>optionally, between 1 and 251 additional static arguments taken from the constant pool </li>
|
||||
* </ul>
|
||||
* Invocation is as if by
|
||||
* {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}.
|
||||
* The returned result must be a {@link java.lang.invoke.CallSite CallSite} (or a subclass).
|
||||
* The type of the call site's target must be exactly equal to the type
|
||||
* derived from the dynamic call site's type descriptor and passed to
|
||||
* the bootstrap method.
|
||||
* The call site then becomes permanently linked to the dynamic call site.
|
||||
* <p>
|
||||
* As documented in the JVM specification, all failures arising from
|
||||
* the linkage of a dynamic call site are reported
|
||||
* by a {@link java.lang.BootstrapMethodError BootstrapMethodError},
|
||||
* which is thrown as the abnormal termination of the dynamic call
|
||||
* site execution.
|
||||
* If this happens, the same error will the thrown for all subsequent
|
||||
* attempts to execute the dynamic call site.
|
||||
*
|
||||
* <h2>timing of linkage</h2>
|
||||
* A dynamic call site is linked just before its first execution.
|
||||
* The bootstrap method call implementing the linkage occurs within
|
||||
* a thread that is attempting a first execution.
|
||||
* <p>
|
||||
* If there are several such threads, the bootstrap method may be
|
||||
* invoked in several threads concurrently.
|
||||
* Therefore, bootstrap methods which access global application
|
||||
* data must take the usual precautions against race conditions.
|
||||
* In any case, every {@code invokedynamic} instruction is either
|
||||
* unlinked or linked to a unique {@code CallSite} object.
|
||||
* <p>
|
||||
* In an application which requires dynamic call sites with individually
|
||||
* mutable behaviors, their bootstrap methods should produce distinct
|
||||
* {@link java.lang.invoke.CallSite CallSite} objects, one for each linkage request.
|
||||
* Alternatively, an application can link a single {@code CallSite} object
|
||||
* to several {@code invokedynamic} instructions, in which case
|
||||
* a change to the target method will become visible at each of
|
||||
* the instructions.
|
||||
* <p>
|
||||
* If several threads simultaneously execute a bootstrap method for a single dynamic
|
||||
* call site, the JVM must choose one {@code CallSite} object and install it visibly to
|
||||
* all threads. Any other bootstrap method calls are allowed to complete, but their
|
||||
* results are ignored, and their dynamic call site invocations proceed with the originally
|
||||
* chosen target object.
|
||||
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* These rules do not enable the JVM to duplicate dynamic call sites,
|
||||
* or to issue “causeless” bootstrap method calls.
|
||||
* Every dynamic call site transitions at most once from unlinked to linked,
|
||||
* just before its first invocation.
|
||||
* There is no way to undo the effect of a completed bootstrap method call.
|
||||
*
|
||||
* <h2>types of bootstrap methods</h2>
|
||||
* As long as each bootstrap method can be correctly invoked
|
||||
* by {@code MethodHandle.invoke}, its detailed type is arbitrary.
|
||||
* For example, the first argument could be {@code Object}
|
||||
* instead of {@code MethodHandles.Lookup}, and the return type
|
||||
* could also be {@code Object} instead of {@code CallSite}.
|
||||
* (Note that the types and number of the stacked arguments limit
|
||||
* the legal kinds of bootstrap methods to appropriately typed
|
||||
* static methods and constructors of {@code CallSite} subclasses.)
|
||||
* <p>
|
||||
* If a given {@code invokedynamic} instruction specifies no static arguments,
|
||||
* the instruction's bootstrap method will be invoked on three arguments,
|
||||
* conveying the instruction's caller class, name, and method type.
|
||||
* If the {@code invokedynamic} instruction specifies one or more static arguments,
|
||||
* those values will be passed as additional arguments to the method handle.
|
||||
* (Note that because there is a limit of 255 arguments to any method,
|
||||
* at most 251 extra arguments can be supplied, since the bootstrap method
|
||||
* handle itself and its first three arguments must also be stacked.)
|
||||
* The bootstrap method will be invoked as if by either {@code MethodHandle.invoke}
|
||||
* or {@code invokeWithArguments}. (There is no way to tell the difference.)
|
||||
* <p>
|
||||
* The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments.
|
||||
* For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
|
||||
* If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
|
||||
* then some or all of the arguments specified here may be collected into a trailing array parameter.
|
||||
* (This is not a special rule, but rather a useful consequence of the interaction
|
||||
* between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods,
|
||||
* and the {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.)
|
||||
* <p>
|
||||
* Given these rules, here are examples of legal bootstrap method declarations,
|
||||
* given various numbers {@code N} of extra arguments.
|
||||
* The first rows (marked {@code *}) will work for any number of extra arguments.
|
||||
* <table border=1 cellpadding=5 summary="Static argument types">
|
||||
* <tr><th>N</th><th>sample bootstrap method</th></tr>
|
||||
* <tr><td>*</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)</code></td></tr>
|
||||
* <tr><td>*</td><td><code>CallSite bootstrap(Object... args)</code></td></tr>
|
||||
* <tr><td>*</td><td><code>CallSite bootstrap(Object caller, Object... nameAndTypeWithArgs)</code></td></tr>
|
||||
* <tr><td>0</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type)</code></td></tr>
|
||||
* <tr><td>0</td><td><code>CallSite bootstrap(Lookup caller, Object... nameAndType)</code></td></tr>
|
||||
* <tr><td>1</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)</code></td></tr>
|
||||
* <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)</code></td></tr>
|
||||
* <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, String... args)</code></td></tr>
|
||||
* <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)</code></td></tr>
|
||||
* </table>
|
||||
* The last example assumes that the extra arguments are of type
|
||||
* {@code CONSTANT_String} and {@code CONSTANT_Integer}, respectively.
|
||||
* The second-to-last example assumes that all extra arguments are of type
|
||||
* {@code CONSTANT_String}.
|
||||
* The other examples work with all types of extra arguments.
|
||||
* <p>
|
||||
* As noted above, the actual method type of the bootstrap method can vary.
|
||||
* For example, the fourth argument could be {@code MethodHandle},
|
||||
* if that is the type of the corresponding constant in
|
||||
* the {@code CONSTANT_InvokeDynamic} entry.
|
||||
* In that case, the {@code MethodHandle.invoke} call will pass the extra method handle
|
||||
* constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke}
|
||||
* will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
|
||||
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
|
||||
* resulting in a {@code BootstrapMethodError}.)
|
||||
* <p>
|
||||
* Note that, as a consequence of the above rules, the bootstrap method may accept a primitive
|
||||
* argument, if it can be represented by a constant pool entry.
|
||||
* However, arguments of type {@code boolean}, {@code byte}, {@code short}, or {@code char}
|
||||
* cannot be created for bootstrap methods, since such constants cannot be directly
|
||||
* represented in the constant pool, and the invocation of the bootstrap method will
|
||||
* not perform the necessary narrowing primitive conversions.
|
||||
* <p>
|
||||
* Extra bootstrap method arguments are intended to allow language implementors
|
||||
* to safely and compactly encode metadata.
|
||||
* In principle, the name and extra arguments are redundant,
|
||||
* since each call site could be given its own unique bootstrap method.
|
||||
* Such a practice is likely to produce large class files and constant pools.
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
*/
|
||||
|
||||
package java.lang.invoke;
|
||||
Reference in New Issue
Block a user