This commit is contained in:
Erik Trimble 2011-02-25 12:48:18 -08:00
commit 41fcd26eb6
37 changed files with 3387 additions and 1492 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -78,7 +78,7 @@ static {
}
private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
// ignore caller and name, but match the type:
return new ConstantCallSite(MethodHandles.collectArguments(printArgs, type));
return new ConstantCallSite(printArgs.asType(type));
}
</pre></blockquote>
* @author John Rose, JSR 292 EG
@ -86,6 +86,7 @@ private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String nam
abstract
public class CallSite {
private static final Access IMPL_TOKEN = Access.getToken();
static { MethodHandleImpl.initStatics(); }
// Fields used only by the JVM. Do not use or change.
private MemberName vmmethod; // supplied by the JVM (ref. to calling method)
@ -125,8 +126,8 @@ public class CallSite {
}
/**
* Report the type of this call site's target.
* Although targets may change, the call site's type can never change.
* 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
@ -154,73 +155,40 @@ public class CallSite {
}
/**
* Report the current linkage state of the call site, a value which may change over time.
* <p>
* If a {@code CallSite} object is returned
* from the bootstrap method of the {@code invokedynamic} instruction,
* the {@code CallSite} is permanently bound to that instruction.
* When the {@code invokedynamic} instruction is executed, the target method
* of its associated call site object is invoked directly.
* It is as if the instruction calls {@code getTarget} and then
* calls {@link MethodHandle#invokeExact invokeExact} on the result.
* <p>
* Unless specified differently by a subclass,
* 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.
* <p>
* In a {@linkplain ConstantCallSite constant call site}, the {@code getTarget} method behaves
* like a read from a {@code final} field of the {@code CallSite}.
* <p>
* In a {@linkplain VolatileCallSite volatile call site}, the {@code getTarget} method behaves
* like a read from a {@code volatile} field of the {@code CallSite}.
* <p>
* This method may not be overridden by application code.
* 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 final MethodHandle getTarget() {
return getTarget0();
}
public abstract MethodHandle getTarget();
/**
* Privileged implementations can override this to force final or volatile semantics on getTarget.
*/
/*package-private*/
MethodHandle getTarget0() {
return target;
}
/**
* Set the target method of this call site.
* 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>
* Unless a subclass of CallSite documents otherwise,
* the interactions of {@code setTarget} 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.
* 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
* @throws UnsupportedOperationException if the call site is
* in fact a {@link ConstantCallSite}
* @see CallSite#getTarget
* @see ConstantCallSite#setTarget
* @see MutableCallSite#setTarget
* @see VolatileCallSite#setTarget
*/
public void setTarget(MethodHandle newTarget) {
checkTargetChange(this.target, newTarget);
setTargetNormal(newTarget);
}
public abstract void setTarget(MethodHandle newTarget);
void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
MethodType oldType = oldTarget.type();
@ -236,31 +204,31 @@ public class CallSite {
/**
* Produce a method handle equivalent to an invokedynamic instruction
* which has been linked to this call site.
* <p>If this call site is a {@linkplain ConstantCallSite constant call site},
* this method simply returns the call site's target, since that will never change.
* <p>Otherwise, this method is equivalent to the following code:
* <p><blockquote><pre>
* <p>
* This method is equivalent to the following code:
* <blockquote><pre>
* MethodHandle getTarget, invoker, result;
* getTarget = MethodHandles.lookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
* 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 final MethodHandle dynamicInvoker() {
if (this instanceof ConstantCallSite) {
return getTarget0(); // will not change dynamically
}
public abstract MethodHandle dynamicInvoker();
/*non-public*/ MethodHandle makeDynamicInvoker() {
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget);
}
private static final MethodHandle GET_TARGET;
static {
try {
GET_TARGET = MethodHandles.Lookup.IMPL_LOOKUP.
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
} catch (NoAccessException ignore) {
} catch (ReflectiveOperationException ignore) {
throw new InternalError();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -31,10 +31,14 @@ import java.util.concurrent.atomic.AtomicReference;
import java.lang.reflect.UndeclaredThrowableException;
/**
* Lazily associate a computed value with (potentially) every class.
* Lazily associate a computed value with (potentially) every type.
* For example, if a dynamic language needs to construct a message dispatch
* table for each class encountered at a message send call site,
* it can use a {@code ClassValue} to cache information needed to
* perform the message send quickly, for each class encountered.
* @author John Rose, JSR 292 EG
*/
public class ClassValue<T> {
public abstract class ClassValue<T> {
/**
* Compute the given class's derived value for this {@code ClassValue}.
* <p>
@ -45,61 +49,41 @@ public class ClassValue<T> {
* but it may be invoked again if there has been a call to
* {@link #remove remove}.
* <p>
* If there is no override from a subclass, this method returns
* the result of applying the {@code ClassValue}'s {@code computeValue}
* method handle, which was supplied at construction time.
* If this method throws an exception, the corresponding call to {@code get}
* will terminate abnormally with that exception, and no class value will be recorded.
*
* @param type the type whose class value must be computed
* @return the newly computed value associated with this {@code ClassValue}, for the given class or interface
* @throws UndeclaredThrowableException if the {@code computeValue} method handle invocation throws something other than a {@code RuntimeException} or {@code Error}
* @throws UnsupportedOperationException if the {@code computeValue} method handle is null (subclasses must override)
* @see #get
* @see #remove
*/
protected T computeValue(Class<?> type) {
if (computeValue == null)
return null;
try {
return (T) (Object) computeValue.invokeGeneric(type);
} catch (Throwable ex) {
if (ex instanceof Error) throw (Error) ex;
if (ex instanceof RuntimeException) throw (RuntimeException) ex;
throw new UndeclaredThrowableException(ex);
}
}
private final MethodHandle computeValue;
/**
* Creates a new class value.
* Subclasses which use this constructor must override
* the {@link #computeValue computeValue} method,
* since the default {@code computeValue} method requires a method handle,
* which this constructor does not provide.
*/
protected ClassValue() {
this.computeValue = null;
}
/**
* Creates a new class value, whose {@link #computeValue computeValue} method
* will return the result of {@code computeValue.invokeGeneric(type)}.
* @throws NullPointerException if the method handle parameter is null
*/
public ClassValue(MethodHandle computeValue) {
computeValue.getClass(); // trigger NPE if null
this.computeValue = computeValue;
}
protected abstract T computeValue(Class<?> type);
/**
* Returns the value for the given class.
* If no value has yet been computed, it is obtained by
* by an invocation of the {@link #computeValue computeValue} method.
* an invocation of the {@link #computeValue computeValue} method.
* <p>
* The actual installation of the value on the class
* is performed atomically.
* At that point, if racing threads have
* At that point, if several racing threads have
* computed values, one is chosen, and returned to
* all the racing threads.
* <p>
* The {@code type} parameter is typically a class, but it may be any type,
* such as an interface, a primitive type (like {@code int.class}), or {@code void.class}.
* <p>
* In the absence of {@code remove} calls, a class value has a simple
* state diagram: uninitialized and initialized.
* When {@code remove} calls are made,
* the rules for value observation are more complex.
* See the documentation for {@link #remove remove} for more information.
*
* @param type the type whose class value must be computed or retrieved
* @return the current value associated with this {@code ClassValue}, for the given class or interface
* @throws NullPointerException if the argument is null
* @see #remove
* @see #computeValue
*/
public T get(Class<?> type) {
ClassValueMap map = getMap(type);
@ -119,12 +103,51 @@ public class ClassValue<T> {
* This may result in an additional invocation of the
* {@code computeValue computeValue} method for the given class.
* <p>
* If racing threads perform a combination of {@code get} and {@code remove} calls,
* the calls are serialized.
* A value produced by a call to {@code computeValue} will be discarded, if
* the corresponding {@code get} call was followed by a {@code remove} call
* before the {@code computeValue} could complete.
* In such a case, the {@code get} call will re-invoke {@code computeValue}.
* In order to explain the interaction between {@code get} and {@code remove} calls,
* we must model the state transitions of a class value to take into account
* the alternation between uninitialized and initialized states.
* To do this, number these states sequentially from zero, and note that
* uninitialized (or removed) states are numbered with even numbers,
* while initialized (or re-initialized) states have odd numbers.
* <p>
* When a thread {@code T} removes a class value in state {@code 2N},
* nothing happens, since the class value is already uninitialized.
* Otherwise, the state is advanced atomically to {@code 2N+1}.
* <p>
* When a thread {@code T} queries a class value in state {@code 2N},
* the thread first attempts to initialize the class value to state {@code 2N+1}
* by invoking {@code computeValue} and installing the resulting value.
* <p>
* When {@code T} attempts to install the newly computed value,
* if the state is still at {@code 2N}, the class value will be initialized
* with the computed value, advancing it to state {@code 2N+1}.
* <p>
* Otherwise, whether the new state is even or odd,
* {@code T} will discard the newly computed value
* and retry the {@code get} operation.
* <p>
* Discarding and retrying is an important proviso,
* since otherwise {@code T} could potentially install
* a disastrously stale value. For example:
* <ul>
* <li>{@code T} calls {@code CV.get(C)} and sees state {@code 2N}
* <li>{@code T} quickly computes a time-dependent value {@code V0} and gets ready to install it
* <li>{@code T} is hit by an unlucky paging or scheduling event, and goes to sleep for a long time
* <li>...meanwhile, {@code T2} also calls {@code CV.get(C)} and sees state {@code 2N}
* <li>{@code T2} quickly computes a similar time-dependent value {@code V1} and installs it on {@code CV.get(C)}
* <li>{@code T2} (or a third thread) then calls {@code CV.remove(C)}, undoing {@code T2}'s work
* <li> the previous actions of {@code T2} are repeated several times
* <li> also, the relevant computed values change over time: {@code V1}, {@code V2}, ...
* <li>...meanwhile, {@code T} wakes up and attempts to install {@code V0}; <em>this must fail</em>
* </ul>
* We can assume in the above scenario that {@code CV.computeValue} uses locks to properly
* observe the time-dependent states as it computes {@code V1}, etc.
* This does not remove the threat of a stale value, since there is a window of time
* between the return of {@code computeValue} in {@code T} and the installation
* of the the new value. No user synchronization is possible during this time.
*
* @param type the type whose class value must be removed
* @throws NullPointerException if the argument is null
*/
public void remove(Class<?> type) {
ClassValueMap map = getMap(type);
@ -137,9 +160,9 @@ public class ClassValue<T> {
/// Implementation...
/** The hash code for this type is based on the identity of the object,
* and is well-dispersed for power-of-two tables.
*/
// The hash code for this type is based on the identity of the object,
// and is well-dispersed for power-of-two tables.
/** @deprecated This override, which is implementation-specific, will be removed for PFD. */
public final int hashCode() { return hashCode; }
private final int hashCode = HASH_CODES.getAndAdd(0x61c88647);
private static final AtomicInteger HASH_CODES = new AtomicInteger();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -32,16 +32,46 @@ package java.dyn;
* @author John Rose, JSR 292 EG
*/
public class ConstantCallSite extends CallSite {
/** Create a call site with a permanent target.
/**
* 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);
}
/**
* Throw an {@link UnsupportedOperationException}, because this kind of call site cannot change its target.
* Returns the target method of the call site, which behaves
* like a {@code final} field of the {@code ConstantCallSite}.
* That is, the 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 UnsupportedOperationException because this kind of call site cannot change its target
*/
@Override public final MethodHandle getTarget() {
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("ConstantCallSite");
}
/**
* 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
*/
@Override
public final MethodHandle dynamicInvoker() {
return getTarget();
}
}

View File

@ -31,8 +31,8 @@ package java.dyn;
* {@linkplain BootstrapMethod bootstrap method},
* or the bootstrap method has
* failed to provide a
* {@linkplain CallSite} call site with a non-null {@linkplain MethodHandle target}
* of the correct {@linkplain MethodType method type}.
* {@linkplain CallSite call site} with a {@linkplain CallSite#getTarget target}
* of the correct {@linkplain MethodHandle#type method type}.
*
* @author John Rose, JSR 292 EG
* @since 1.7

View File

@ -88,7 +88,7 @@ public class Linkage {
MethodHandle bootstrapMethod;
try {
bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex);
}
MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod);
@ -101,8 +101,9 @@ public class Linkage {
/**
* <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Invalidate all <code>invokedynamic</code> call sites everywhere.
* @deprecated Use {@linkplain CallSite#setTarget call site target setting}
* and {@link VolatileCallSite#invalidateAll call site invalidation} instead.
* @deprecated Use {@linkplain MutableCallSite#setTarget call site target setting},
* {@link MutableCallSite#syncAll call site update pushing},
* and {@link SwitchPoint#guardWithTest target switching} instead.
*/
public static
Object invalidateAll() {
@ -113,8 +114,9 @@ public class Linkage {
* <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Invalidate all {@code invokedynamic} call sites in the bytecodes
* of any methods of the given class.
* @deprecated Use {@linkplain CallSite#setTarget call site target setting}
* and {@link VolatileCallSite#invalidateAll call site invalidation} instead.
* @deprecated Use {@linkplain MutableCallSite#setTarget call site target setting},
* {@link MutableCallSite#syncAll call site update pushing},
* and {@link SwitchPoint#guardWithTest target switching} instead.
*/
public static
Object invalidateCallerClass(Class<?> callerClass) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -31,6 +31,7 @@ import java.util.HashMap;
import java.util.List;
import sun.dyn.Access;
import sun.dyn.Invokers;
import sun.dyn.MethodHandleImpl;
import sun.dyn.MethodTypeImpl;
import sun.dyn.util.BytecodeDescriptor;
import static sun.dyn.MemberName.newIllegalArgumentException;
@ -41,8 +42,8 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
* and expected by a method handle caller. Method types must be properly
* matched between a method handle and all its callers,
* and the JVM's operations enforce this matching at, specifically
* during calls to {@link MethodHandle#invokeExact}
* and {@link MethodHandle#invokeGeneric}, and during execution
* during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact}
* and {@link MethodHandle#invokeGeneric MethodHandle.invokeGeneric}, and during execution
* of {@code invokedynamic} instructions.
* <p>
* The structure is a return type accompanied by any number of parameter types.
@ -70,8 +71,9 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
* with the instructions in a class file's constant pool.
* <p>
* Like classes and strings, method types can also be represented directly
* in a class file's constant pool as constants. The may be loaded by an {@code ldc}
* instruction which refers to a suitable {@code CONSTANT_MethodType} constant pool entry.
* in a class file's constant pool as constants.
* A method type may be loaded by an {@code ldc} instruction which refers
* to a suitable {@code CONSTANT_MethodType} constant pool entry.
* The entry refers to a {@code CONSTANT_Utf8} spelling for the descriptor string.
* For more details, see the <a href="package-summary.html#mtcon">package summary</a>.
* <p>
@ -82,9 +84,14 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
* @author John Rose, JSR 292 EG
*/
public final
class MethodType {
class MethodType implements java.io.Serializable {
private static final long serialVersionUID = 292L; // {rtype, {ptype...}}
// The rtype and ptypes fields define the structural identity of the method type:
private final Class<?> rtype;
private final Class<?>[] ptypes;
// The remaining fields are caches of various sorts:
private MethodTypeForm form; // erased form, plus cached data about primitives
private MethodType wrapAlt; // alternative wrapped/unwrapped version
private Invokers invokers; // cache of handy higher-order adapters
@ -117,6 +124,9 @@ class MethodType {
});
}
/**
* Check the given parameters for validity and store them into the final fields.
*/
private MethodType(Class<?> rtype, Class<?>[] ptypes) {
checkRtype(rtype);
checkPtypes(ptypes);
@ -124,15 +134,32 @@ class MethodType {
this.ptypes = ptypes;
}
private void checkRtype(Class<?> rtype) {
private static void checkRtype(Class<?> rtype) {
rtype.equals(rtype); // null check
}
private void checkPtypes(Class<?>[] ptypes) {
private static int checkPtype(Class<?> ptype) {
ptype.getClass(); //NPE
if (ptype == void.class)
throw newIllegalArgumentException("parameter type cannot be void");
if (ptype == double.class || ptype == long.class) return 1;
return 0;
}
/** Return number of extra slots (count of long/double args). */
private static int checkPtypes(Class<?>[] ptypes) {
int slots = 0;
for (Class<?> ptype : ptypes) {
ptype.equals(ptype); // null check
if (ptype == void.class)
throw newIllegalArgumentException("parameter type cannot be void");
slots += checkPtype(ptype);
}
checkSlotCount(ptypes.length + slots);
return slots;
}
private static void checkSlotCount(int count) {
if ((count & 0xFF) != count)
throw newIllegalArgumentException("bad parameter count "+count);
}
private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num) {
if (num instanceof Integer) num = "bad index: "+num;
return new IndexOutOfBoundsException(num.toString());
}
static final HashMap<MethodType,MethodType> internTable
@ -140,27 +167,39 @@ class MethodType {
static final Class<?>[] NO_PTYPES = {};
/** Find or create an instance of the given method type.
/**
* Find or create an instance of the given method type.
* @param rtype the return type
* @param ptypes the parameter types
* @return a method type with the given parts
* @throws NullPointerException if rtype or any ptype is null
* @throws IllegalArgumentException if any of the ptypes is void
* @return a method type with the given components
* @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
* @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
*/
public static
MethodType methodType(Class<?> rtype, Class<?>[] ptypes) {
return makeImpl(rtype, ptypes, false);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. */
/**
* Finds or creates a method type with the given components.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @return a method type with the given components
* @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
* @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
*/
public static
MethodType methodType(Class<?> rtype, List<? extends Class<?>> ptypes) {
MethodType methodType(Class<?> rtype, List<Class<?>> ptypes) {
boolean notrust = false; // random List impl. could return evil ptypes array
return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* The leading parameter type is prepended to the remaining array.
/**
* Finds or creates a method type with the given components.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* The leading parameter type is prepended to the remaining array.
* @return a method type with the given components
* @throws NullPointerException if {@code rtype} or {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is null
* @throws IllegalArgumentException if {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is {@code void.class}
*/
public static
MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
@ -170,25 +209,37 @@ class MethodType {
return makeImpl(rtype, ptypes1, true);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* The resulting method has no parameter types.
/**
* Finds or creates a method type with the given components.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* The resulting method has no parameter types.
* @return a method type with the given return value
* @throws NullPointerException if {@code rtype} is null
*/
public static
MethodType methodType(Class<?> rtype) {
return makeImpl(rtype, NO_PTYPES, true);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* The resulting method has the single given parameter type.
/**
* Finds or creates a method type with the given components.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* The resulting method has the single given parameter type.
* @return a method type with the given return value and parameter type
* @throws NullPointerException if {@code rtype} or {@code ptype0} is null
* @throws IllegalArgumentException if {@code ptype0} is {@code void.class}
*/
public static
MethodType methodType(Class<?> rtype, Class<?> ptype0) {
return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* The resulting method has the same parameter types as {@code ptypes},
* and the specified return type.
/**
* Finds or creates a method type with the given components.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* The resulting method has the same parameter types as {@code ptypes},
* and the specified return type.
* @throws NullPointerException if {@code rtype} or {@code ptypes} is null
*/
public static
MethodType methodType(Class<?> rtype, MethodType ptypes) {
@ -237,17 +288,20 @@ class MethodType {
private static final MethodType[] objectOnlyTypes = new MethodType[20];
/**
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All parameters and the return type will be {@code Object},
* except the final varargs parameter if any, which will be {@code Object[]}.
* @param objectArgCount number of parameters (excluding the varargs parameter if any)
* @param varargs whether there will be a varargs parameter, of type {@code Object[]}
* @return a totally generic method type, given only its count of parameters and varargs
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
* @see #genericMethodType(int)
*/
public static
MethodType genericMethodType(int objectArgCount, boolean varargs) {
MethodType mt;
checkSlotCount(objectArgCount);
int ivarargs = (!varargs ? 0 : 1);
int ootIndex = objectArgCount*2 + ivarargs;
if (ootIndex < objectOnlyTypes.length) {
@ -265,9 +319,12 @@ class MethodType {
}
/**
* Finds or creates a method type whose components are all {@code Object}.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All parameters and the return type will be Object.
* @param objectArgCount number of parameters
* @return a totally generic method type, given only its count of parameters
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
* @see #genericMethodType(int, boolean)
*/
public static
@ -275,27 +332,41 @@ class MethodType {
return genericMethodType(objectArgCount, false);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
/**
* Finds or creates a method type with a single different parameter type.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param num the index (zero-based) of the parameter type to change
* @param nptype a new parameter type to replace the old one with
* @return the same type, except with the selected parameter changed
* @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
* @throws IllegalArgumentException if {@code nptype} is {@code void.class}
* @throws NullPointerException if {@code nptype} is null
*/
public MethodType changeParameterType(int num, Class<?> nptype) {
if (parameterType(num) == nptype) return this;
checkPtype(nptype);
Class<?>[] nptypes = ptypes.clone();
nptypes[num] = nptype;
return makeImpl(rtype, nptypes, true);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
/**
* Finds or creates a method type with additional parameter types.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param num the position (zero-based) of the inserted parameter type(s)
* @param ptypesToInsert zero or more a new parameter types to insert into the parameter list
* @param ptypesToInsert zero or more new parameter types to insert into the parameter list
* @return the same type, except with the selected parameter(s) inserted
* @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
* @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
* or if the resulting method type would have more than 255 parameter slots
* @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
*/
public MethodType insertParameterTypes(int num, Class<?>... ptypesToInsert) {
int len = ptypes.length;
if (num < 0 || num > len)
throw newIllegalArgumentException("num="+num); //SPECME
throw newIndexOutOfBoundsException(num);
int ins = checkPtypes(ptypesToInsert);
checkSlotCount(parameterSlotCount() + ptypesToInsert.length + ins);
int ilen = ptypesToInsert.length;
if (ilen == 0) return this;
Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+ilen);
@ -304,40 +375,61 @@ class MethodType {
return makeImpl(rtype, nptypes, true);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list
/**
* Finds or creates a method type with additional parameter types.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
* @return the same type, except with the selected parameter(s) appended
* @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
* or if the resulting method type would have more than 255 parameter slots
* @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
*/
public MethodType appendParameterTypes(Class<?>... ptypesToInsert) {
return insertParameterTypes(parameterCount(), ptypesToInsert);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list
* @return the same type, except with the selected parameter(s) appended
*/
public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) {
return insertParameterTypes(parameterCount(), ptypesToInsert);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
/**
* Finds or creates a method type with additional parameter types.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param num the position (zero-based) of the inserted parameter type(s)
* @param ptypesToInsert zero or more a new parameter types to insert into the parameter list
* @param ptypesToInsert zero or more new parameter types to insert into the parameter list
* @return the same type, except with the selected parameter(s) inserted
* @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
* @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
* or if the resulting method type would have more than 255 parameter slots
* @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
*/
public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) {
return insertParameterTypes(num, ptypesToInsert.toArray(NO_PTYPES));
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
/**
* Finds or creates a method type with additional parameter types.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
* @return the same type, except with the selected parameter(s) appended
* @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
* or if the resulting method type would have more than 255 parameter slots
* @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
*/
public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) {
return insertParameterTypes(parameterCount(), ptypesToInsert);
}
/**
* Finds or creates a method type with some parameter types omitted.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param start the index (zero-based) of the first parameter type to remove
* @param end the index (greater than {@code start}) of the first parameter type after not to remove
* @return the same type, except with the selected parameter(s) removed
* @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
* or if {@code end} is negative or greater than {@code parameterCount()}
* or if {@code start} is greater than {@code end}
*/
public MethodType dropParameterTypes(int start, int end) {
int len = ptypes.length;
if (!(0 <= start && start <= end && end <= len))
throw newIllegalArgumentException("start="+start+" end="+end); //SPECME
throw newIndexOutOfBoundsException("start="+start+" end="+end);
if (start == end) return this;
Class<?>[] nptypes;
if (start == 0) {
@ -361,17 +453,20 @@ class MethodType {
return makeImpl(rtype, nptypes, true);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
/**
* Finds or creates a method type with a different return type.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param nrtype a return parameter type to replace the old one with
* @return the same type, except with the return type change
* @throws NullPointerException if {@code nrtype} is null
*/
public MethodType changeReturnType(Class<?> nrtype) {
if (returnType() == nrtype) return this;
return makeImpl(nrtype, ptypes, true);
}
/** Convenience method.
* Report if this type contains a primitive argument or return value.
/**
* Reports if this type contains a primitive argument or return value.
* The return type {@code void} counts as a primitive.
* @return true if any of the types are primitives
*/
@ -379,8 +474,8 @@ class MethodType {
return form.hasPrimitives();
}
/** Convenience method.
* Report if this type contains a wrapper argument or return value.
/**
* Reports if this type contains a wrapper argument or return value.
* Wrappers are types which box primitive values, such as {@link Integer}.
* The reference type {@code java.lang.Void} counts as a wrapper.
* @return true if any of the types are wrappers
@ -389,8 +484,9 @@ class MethodType {
return unwrap() != this;
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Erase all reference types to {@code Object}.
/**
* Erases all reference types to {@code Object}.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All primitive types (including {@code void}) will remain unchanged.
* @return a version of the original type with all reference types replaced
*/
@ -398,8 +494,9 @@ class MethodType {
return form.erasedType();
}
/** Convenience method for {@link #genericMethodType(int)}.
* Convert all types, both reference and primitive, to {@code Object}.
/**
* Converts all types, both reference and primitive, to {@code Object}.
* Convenience method for {@link #genericMethodType(int) genericMethodType}.
* The expression {@code type.wrap().erase()} produces the same value
* as {@code type.generic()}.
* @return a version of the original type with all types replaced
@ -408,8 +505,9 @@ class MethodType {
return genericMethodType(parameterCount());
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Convert all primitive types to their corresponding wrapper types.
/**
* Converts all primitive types to their corresponding wrapper types.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All reference types (including wrapper types) will remain unchanged.
* A {@code void} return type is changed to the type {@code java.lang.Void}.
* The expression {@code type.wrap().erase()} produces the same value
@ -420,8 +518,9 @@ class MethodType {
return hasPrimitives() ? wrapWithPrims(this) : this;
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
/**
* Convert all wrapper types to their corresponding primitive types.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All primitive types (including {@code void}) will remain unchanged.
* A return type of {@code java.lang.Void} is changed to {@code void}.
* @return a version of the original type with all wrapper types replaced
@ -456,23 +555,33 @@ class MethodType {
return uwt;
}
/** @param num the index (zero-based) of the desired parameter type
* @return the selected parameter type
/**
* Returns the parameter type at the specified index, within this method type.
* @param num the index (zero-based) of the desired parameter type
* @return the selected parameter type
* @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
*/
public Class<?> parameterType(int num) {
return ptypes[num];
}
/** @return the number of parameter types */
/**
* Returns the number of parameter types in this method type.
* @return the number of parameter types
*/
public int parameterCount() {
return ptypes.length;
}
/** @return the return type */
/**
* Returns the return type of this method type.
* @return the return type
*/
public Class<?> returnType() {
return rtype;
}
/**
* Convenience method to present the arguments as a list.
* Presents the parameter types as a list (a convenience method).
* The list will be immutable.
* @return the parameter types (as an immutable list)
*/
public List<Class<?>> parameterList() {
@ -480,7 +589,7 @@ class MethodType {
}
/**
* Convenience method to present the arguments as an array.
* Presents the parameter types as an array (a convenience method).
* Changes to the array will not result in changes to the type.
* @return the parameter types (as a fresh copy if necessary)
*/
@ -524,14 +633,14 @@ class MethodType {
}
/**
* Returns a string representation of the method type,
* of the form {@code "(PT0,PT1...)RT"}.
* The string representation of a method type is a
* parenthesis enclosed, comma separated list of type names,
* followed immediately by the return type.
* <p>
* Each type is represented by its
* {@link java.lang.Class#getSimpleName simple name}.
* If a type name name is array, it the base type followed
* by [], rather than the Class.getName of the array type.
*/
@Override
public String toString() {
@ -548,21 +657,22 @@ class MethodType {
/// Queries which have to do with the bytecode architecture
/** The number of JVM stack slots required to invoke a method
/** Reports the number of JVM stack slots required to invoke a method
* of this type. Note that (for historic reasons) the JVM requires
* a second stack slot to pass long and double arguments.
* So this method returns {@link #parameterCount()} plus the
* So this method returns {@link #parameterCount() parameterCount} plus the
* number of long and double parameters (if any).
* <p>
* This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic.
* @return the number of JVM stack slots for this type's parameters
* @deprecated Will be removed for PFD.
*/
public int parameterSlotCount() {
return form.parameterSlotCount();
}
/** Number of JVM stack slots which carry all parameters including and after
/** Reports the number of JVM stack slots which carry all parameters including and after
* the given position, which must be in the range of 0 to
* {@code parameterCount} inclusive. Successive parameters are
* more shallowly stacked, and parameters are indexed in the bytecodes
@ -583,6 +693,8 @@ class MethodType {
* @param num an index (zero-based, inclusive) within the parameter types
* @return the index of the (shallowest) JVM stack slot transmitting the
* given parameter
* @throws IllegalArgumentException if {@code num} is negative or greater than {@code parameterCount()}
* @deprecated Will be removed for PFD.
*/
public int parameterSlotDepth(int num) {
if (num < 0 || num > ptypes.length)
@ -590,7 +702,7 @@ class MethodType {
return form.parameterToArgSlot(num-1);
}
/** The number of JVM stack slots required to receive a return value
/** Reports the number of JVM stack slots required to receive a return value
* from a method of this type.
* If the {@link #returnType() return type} is void, it will be zero,
* else if the return type is long or double, it will be two, else one.
@ -598,13 +710,15 @@ class MethodType {
* This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic.
* @return the number of JVM stack slots (0, 1, or 2) for this type's return value
* @deprecated Will be removed for PFD.
*/
public int returnSlotCount() {
return form.returnSlotCount();
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Find or create an instance of the given method type.
/**
* Find or create an instance of a method type, given the spelling of its bytecode descriptor.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* Any class or interface name embedded in the descriptor string
* will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
* on the given loader (or if it is null, on the system class loader).
@ -614,10 +728,10 @@ class MethodType {
* not all reachable from a common class loader.
* <p>
* This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic.
* @param descriptor a bytecode-level signature string "(T...)T"
* generate bytecodes that process method handles and {@code invokedynamic}.
* @param descriptor a bytecode-level type descriptor string "(T...)T"
* @param loader the class loader in which to look up the types
* @return a method type matching the bytecode-level signature
* @return a method type matching the bytecode-level type descriptor
* @throws IllegalArgumentException if the string is not well-formed
* @throws TypeNotPresentException if a named type cannot be found
*/
@ -631,19 +745,121 @@ class MethodType {
}
/**
* Create a bytecode descriptor representation of the method type.
* Produces a bytecode descriptor representation of the method type.
* <p>
* Note that this is not a strict inverse of {@link #fromMethodDescriptorString}.
* Note that this is not a strict inverse of {@link #fromMethodDescriptorString fromMethodDescriptorString}.
* Two distinct classes which share a common name but have different class loaders
* will appear identical when viewed within descriptor strings.
* <p>
* This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic.
* {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader)},
* generate bytecodes that process method handles and {@code invokedynamic}.
* {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) fromMethodDescriptorString},
* because the latter requires a suitable class loader argument.
* @return the bytecode signature representation
* @return the bytecode type descriptor representation
*/
public String toMethodDescriptorString() {
return BytecodeDescriptor.unparse(this);
}
/// Serialization.
/**
* There are no serializable fields for {@code MethodType}.
*/
private static final java.io.ObjectStreamField[] serialPersistentFields = { };
/**
* Save the {@code MethodType} instance to a stream.
*
* @serialData
* For portability, the serialized format does not refer to named fields.
* Instead, the return type and parameter type arrays are written directly
* from the {@code writeObject} method, using two calls to {@code s.writeObject}
* as follows:
* <blockquote><pre>
s.writeObject(this.returnType());
s.writeObject(this.parameterArray());
* </pre></blockquote>
* <p>
* The deserialized field values are checked as if they were
* provided to the factory method {@link #methodType(Class,Class[]) methodType}.
* For example, null values, or {@code void} parameter types,
* will lead to exceptions during deserialization.
* @param the stream to write the object to
*/
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
s.defaultWriteObject(); // requires serialPersistentFields to be an empty array
s.writeObject(returnType());
s.writeObject(parameterArray());
}
/**
* Reconstitute the {@code MethodType} instance from a stream (that is,
* deserialize it).
* This instance is a scratch object with bogus final fields.
* It provides the parameters to the factory method called by
* {@link #readResolve readResolve}.
* After that call it is discarded.
* @param the stream to read the object from
* @see #MethodType()
* @see #readResolve
* @see #writeObject
*/
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject(); // requires serialPersistentFields to be an empty array
Class<?> returnType = (Class<?>) s.readObject();
Class<?>[] parameterArray = (Class<?>[]) s.readObject();
// Probably this object will never escape, but let's check
// the field values now, just to be sure.
checkRtype(returnType);
checkPtypes(parameterArray);
parameterArray = parameterArray.clone(); // make sure it is unshared
MethodType_init(returnType, parameterArray);
}
/**
* For serialization only.
* Sets the final fields to null, pending {@code Unsafe.putObject}.
*/
private MethodType() {
this.rtype = null;
this.ptypes = null;
}
private void MethodType_init(Class<?> rtype, Class<?>[] ptypes) {
// In order to communicate these values to readResolve, we must
// store them into the implementation-specific final fields.
checkRtype(rtype);
checkPtypes(ptypes);
unsafe.putObject(this, rtypeOffset, rtype);
unsafe.putObject(this, ptypesOffset, ptypes);
}
// Support for resetting final fields while deserializing
private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
private static final long rtypeOffset, ptypesOffset;
static {
try {
rtypeOffset = unsafe.objectFieldOffset
(MethodType.class.getDeclaredField("rtype"));
ptypesOffset = unsafe.objectFieldOffset
(MethodType.class.getDeclaredField("ptypes"));
} catch (Exception ex) {
throw new Error(ex);
}
}
/**
* Resolves and initializes a {@code MethodType} object
* after serialization.
* @return the fully initialized {@code MethodType} object
*/
private Object readResolve() {
// Do not use a trusted path for deserialization:
//return makeImpl(rtype, ptypes, true);
// Verify all operands, and make sure ptypes is unshared:
return methodType(rtype, ptypes);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -73,7 +73,7 @@ assertEquals("Wilma, dear?", (String) worker2.invokeExact());
* (This is a normal consequence of the Java Memory Model as applied
* to object fields.)
* <p>
* The {@link #sync sync} operation provides a way to force threads
* 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
@ -82,13 +82,17 @@ assertEquals("Wilma, dear?", (String) worker2.invokeExact());
*/
public class MutableCallSite extends CallSite {
/**
* 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.
* 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) {
@ -96,8 +100,9 @@ public class MutableCallSite extends CallSite {
}
/**
* Make a blank call site object, possibly equipped with an initial target method handle.
* @param target the method handle which will be the initial target of the call site
* 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) {
@ -105,7 +110,59 @@ public class MutableCallSite extends CallSite {
}
/**
* Perform a synchronization operation on each call site in the given array,
* 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>
@ -115,19 +172,29 @@ public class MutableCallSite extends CallSite {
* <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 sync} itself.)
* Conversely, the {@code sync} call may block until all readers have
* ("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 sync}
* 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 sync} that confirms 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 sync} call returns
* 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.
*
* <h3>Java Memory Model details</h3>
* 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
@ -171,18 +238,17 @@ public class MutableCallSite extends CallSite {
* 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 sync}
* 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>
* 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 style="font-size:smaller;">
* (This is a static method on a set of call sites, not a
* virtual method on a single call site, for performance reasons.
* <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.
@ -191,15 +257,25 @@ public class MutableCallSite extends CallSite {
* 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>
* 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 sync} method can be a no-op,
* 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 sync(MutableCallSite[] sites) {
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();

View File

@ -1,79 +0,0 @@
/*
* Copyright (c) 2008, 2010, 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.dyn;
/**
* Thrown to indicate that a caller has attempted to create a method handle
* which accesses a field, method, or class to which the caller does not have access.
* This unchecked exception is analogous to {@link IllegalAccessException},
* which is a checked exception thrown when reflective invocation fails
* because of an access check. With method handles, this same access
* checking is performed by the {@link MethodHandles.Lookup lookup object}
* on behalf of the method handle creator,
* at the time of creation.
* @author John Rose, JSR 292 EG
* @since 1.7
*/
public class NoAccessException extends ReflectiveOperationException {
private static final long serialVersionUID = 292L;
/**
* Constructs a {@code NoAccessException} with no detail message.
*/
public NoAccessException() {
super();
}
/**
* Constructs a {@code NoAccessException} with the specified
* detail message.
*
* @param s the detail message
*/
public NoAccessException(String s) {
super(s);
}
/**
* Constructs a {@code NoAccessException} with the specified cause.
*
* @param cause the underlying cause of the exception
*/
public NoAccessException(Throwable cause) {
super(cause);
}
/**
* Constructs a {@code NoAccessException} with the specified
* detail message and cause.
*
* @param s the detail message
* @param cause the underlying cause of the exception
*/
public NoAccessException(String s, Throwable cause) {
super(s, cause);
}
}

View File

@ -0,0 +1,195 @@
/*
* 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.dyn;
/**
* <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:
* <blockquote><pre>
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
MethodHandle MH_strcat = MethodHandles.lookup()
.findVirtual(String.class, "concat", MT_str2);
SwitchPoint spt = new SwitchPoint();
// the following steps may be repeated to re-use the same switch point:
MethodHandle worker1 = strcat;
MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0);
MethodHandle worker = spt.guardWithTest(worker1, worker2);
assertEquals("method", (String) worker.invokeExact("met", "hod"));
SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
* </pre></blockquote>
* <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.
* <p style="font-size:smaller;">
* <em>Implementation Note:</em>
* A switch point behaves as if implemented on top of {@link MutableCallSite},
* approximately as follows:
* <blockquote><pre>
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&lt;MutableCallSite&gt; mcss = new ArrayList&lt;&gt;();
for (SwitchPoint spt : spts) mcss.add(spt.mcs);
for (MutableCallSite mcs : mcss) mcs.setTarget(K_false);
MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0]));
}
}
* </pre></blockquote>
* @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();
}
/**
* 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
* @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);
}
}

View File

@ -1,130 +0,0 @@
/*
* Copyright (c) 2010, 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.dyn;
/**
* <p>
* A {@code Switcher} is an object which can publish state transitions to other threads.
* A switcher 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.
* <p>
* A single switcher may be used to create any number of guarded method handle pairs.
* Each guarded pair is wrapped in a new method handle {@code M},
* which is permanently associated with the switcher that created it.
* Each pair consists of a target {@code T} and a fallback {@code F}.
* While the switcher 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 switcher contained a
* volatile boolean variable consulted on every call to {@code M}.
* The invalidation is also permanent, which means the switcher
* can change state only once.
* <p>
* Here is an example of a switcher in action:
* <blockquote><pre>
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
MethodHandle MH_strcat = MethodHandles.lookup()
.findVirtual(String.class, "concat", MT_str2);
Switcher switcher = new Switcher();
// the following steps may be repeated to re-use the same switcher:
MethodHandle worker1 = strcat;
MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0);
MethodHandle worker = switcher.guardWithTest(worker1, worker2);
assertEquals("method", (String) worker.invokeExact("met", "hod"));
switcher.invalidate();
assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
* </pre></blockquote>
* <p>
* <em>Implementation Note:</em>
* A switcher behaves as if implemented on top of {@link MutableCallSite},
* approximately as follows:
* <blockquote><pre>
public class Switcher {
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 Switcher() {
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(Switcher[] switchers) {
List<MutableCallSite> mcss = new ArrayList<>();
for (Switcher s : switchers) mcss.add(s.mcs);
for (MutableCallSite mcs : mcss) mcs.setTarget(K_false);
MutableCallSite.sync(mcss.toArray(new MutableCallSite[0]));
}
}
* </pre></blockquote>
* @author Remi Forax, JSR 292 EG
*/
public class Switcher {
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;
/** Create a switcher. */
public Switcher() {
this.mcs = new MutableCallSite(K_true);
this.mcsInvoker = mcs.dynamicInvoker();
}
/**
* Return 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 switcher 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.
* @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);
}
/** Set all of the given switchers into the invalid state. */
public static void invalidateAll(Switcher[] switchers) {
MutableCallSite[] sites = new MutableCallSite[switchers.length];
int fillp = 0;
for (Switcher switcher : switchers) {
sites[fillp++] = switcher.mcs;
switcher.mcs.setTarget(K_false);
}
MutableCallSite.sync(sites);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -34,7 +34,7 @@ import java.util.List;
* There may be a performance penalty for such tight coupling between threads.
* <p>
* Unlike {@code MutableCallSite}, there is no
* {@linkplain MutableCallSite#sync sync operation} on volatile
* {@linkplain MutableCallSite#syncAll syncAll operation} on volatile
* call sites, since every write to a volatile variable is implicitly
* synchronized with reader threads.
* <p>
@ -44,36 +44,68 @@ import java.util.List;
* @author John Rose, JSR 292 EG
*/
public class VolatileCallSite extends CallSite {
/** Create a call site with a volatile target.
* The initial target is set to a method handle
* of the given type which will throw {@code IllegalStateException}.
/**
* 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);
}
/** Create a call site with a volatile target.
* The target is set to the given value.
/**
* 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);
}
/** Internal override to nominally final getTarget. */
@Override
MethodHandle getTarget0() {
/**
* 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();
}
/**
* Set the target method of this call site, as a volatile variable.
* Has the same effect as {@link CallSite#setTarget CallSite.setTarget}, with the additional
* effects associated with volatiles, in the Java Memory Model.
* 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();
}
}

View File

@ -29,7 +29,7 @@ package java.dyn;
* 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 signature associated with a call site.
* 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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -24,21 +24,20 @@
*/
/**
* This package contains dynamic language support provided directly by
* The {@code java.lang.invoke} package contains dynamic language support provided directly by
* the Java core class libraries and virtual machine.
*
* <p style="font-size:smaller;">
* <em>Historic Note:</em> In some early versions of Java SE 7,
* the name of this package is {@code java.dyn}.
* <p>
* Certain types in this package have special relations to dynamic
* language support in the virtual machine:
* <ul>
* <li>In source code, a call to
* {@link java.dyn.MethodHandle#invokeExact MethodHandle.invokeExact} or
* {@link java.dyn.MethodHandle#invokeGeneric MethodHandle.invokeGeneric}
* will compile and link, regardless of the requested type signature.
* As usual, the Java compiler emits an {@code invokevirtual}
* instruction with the given signature against the named method.
* The JVM links any such call (regardless of signature) to a dynamically
* typed method handle invocation. In the case of {@code invokeGeneric},
* argument and return value conversions are applied.
* <li>The class {@link java.dyn.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
@ -58,12 +57,11 @@
* The final two bytes are reserved for future use and required to be zero.
* The constant pool reference of an {@code invokedynamic} instruction is to a entry
* with tag {@code CONSTANT_InvokeDynamic} (decimal 18). See below for its format.
* (The tag value 17 is also temporarily allowed. See below.)
* The entry specifies the following information:
* <ul>
* <li>a bootstrap method (a {@link java.dyn.MethodHandle MethodHandle} constant)</li>
* <li>the dynamic invocation name (a UTF8 string)</li>
* <li>the argument and return types of the call (encoded as a signature in a UTF8 string)</li>
* <li>the argument and return types of the call (encoded as a type descriptor in a UTF8 string)</li>
* <li>optionally, a sequence of additional <em>static arguments</em> to the bootstrap method ({@code ldc}-type constants)</li>
* </ul>
* <p>
@ -78,9 +76,9 @@
* as <a href="#bsm">described below</a>.
*
* <p style="font-size:smaller;">
* (Historic Note: Some older JVMs may allow the index of a {@code CONSTANT_NameAndType}
* <em>Historic Note:</em> Some older JVMs may allow the index of a {@code CONSTANT_NameAndType}
* instead of a {@code CONSTANT_InvokeDynamic}. In earlier, obsolete versions of this API, the
* bootstrap method was specified dynamically, in a per-class basis, during class initialization.)
* bootstrap method was specified dynamically, in a per-class basis, during class initialization.
*
* <h3><a name="indycon"></a>constant pool entries for {@code invokedynamic} instructions</h3>
* If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 18),
@ -90,33 +88,21 @@
* <em>bootstrap method table</em>, which is stored in the {@code BootstrapMethods}
* attribute as <a href="#bsmattr">described below</a>.
* The second pair of bytes must be an index to a {@code CONSTANT_NameAndType}.
* This table is not part of the constant pool. Instead, it is stored
* in a class attribute named {@code BootstrapMethods}, described below.
* <p>
* The first index specifies a bootstrap method used by the associated dynamic call sites.
* The second index specifies the method name, argument types, and return type of the dynamic call site.
* The structure of such an entry is therefore analogous to a {@code CONSTANT_Methodref},
* except that the bootstrap method specifier reference replaces
* the {@code CONSTANT_Class} reference of a {@code CONSTANT_Methodref} entry.
* <p>
* Some older JVMs may allow an older constant pool entry tag of decimal 17.
* The format and behavior of a constant pool entry with this tag is identical to
* an entry with a tag of decimal 18, except that the first index refers directly
* to a {@code CONSTANT_MethodHandle} to use as the bootstrap method.
* This format does not require the bootstrap method table.
*
* <p style="font-size:smaller;">
* <em>(Note: The Proposed Final Draft of this specification is likely to support
* only the tag 18, not the tag 17.)</em>
*
* <h3><a name="mtcon"></a>constant pool entries for {@linkplain java.dyn.MethodType method types}</h3>
* If a constant pool entry has the tag {@code CONSTANT_MethodType} (decimal 16),
* it must contain exactly two more bytes, which must be an index to a {@code CONSTANT_Utf8}
* entry which represents a method type signature.
* entry which represents a method type descriptor.
* <p>
* The JVM will ensure that on first
* execution of an {@code ldc} instruction for this entry, a {@link java.dyn.MethodType MethodType}
* will be created which represents the signature.
* will be created which represents the type descriptor.
* Any classes mentioned in the {@code MethodType} will be loaded if necessary,
* but not initialized.
* Access checking and error reporting is performed exactly as it is for
@ -141,38 +127,75 @@
* type is created. Any classes mentioned in this reification will be loaded if necessary,
* but not initialized, and access checking and error reporting performed as usual.
* <p>
* Unlike the reflective {@code Lookup} API, there are no security manager calls made
* when these constants are resolved.
* <p>
* The method handle itself will have a type and behavior determined by the subtag as follows:
* <code>
* <table border=1 cellpadding=5 summary="CONSTANT_MethodHandle subtypes">
* <tr><th>N</th><th>subtag name</th><th>member</th><th>MH type</th><th>MH behavior</th></tr>
* <tr><td>1</td><td>REF_getField</td><td>C.f:T</td><td>(C)T</td><td>getfield C.f:T</td></tr>
* <tr><td>2</td><td>REF_getStatic</td><td>C.f:T</td><td>(&nbsp;)T</td><td>getstatic C.f:T</td></tr>
* <tr><td>3</td><td>REF_putField</td><td>C.f:T</td><td>(C,T)void</td><td>putfield C.f:T</td></tr>
* <tr><td>4</td><td>REF_putStatic</td><td>C.f:T</td><td>(T)void</td><td>putstatic C.f:T</td></tr>
* <tr><td>5</td><td>REF_invokeVirtual</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokevirtual C.m(A*)T</td></tr>
* <tr><td>6</td><td>REF_invokeStatic</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokestatic C.m(A*)T</td></tr>
* <tr><td>7</td><td>REF_invokeSpecial</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokespecial C.m(A*)T</td></tr>
* <tr><td>8</td><td>REF_newInvokeSpecial</td><td>C.&lt;init&gt;(A*)void</td><td>(A*)C</td><td>new C; dup; invokespecial C.&lt;init&gt;(A*)void</td></tr>
* <tr><td>9</td><td>REF_invokeInterface</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokeinterface C.m(A*)T</td></tr>
* <tr><th>N</th><th>subtag name</th><th>member</th><th>MH type</th><th>bytecode behavior</th><th>lookup expression</th></tr>
* <tr><td>1</td><td>REF_getField</td><td>C.f:T</td><td>(C)T</td><td>getfield C.f:T</td>
* <td>{@linkplain java.dyn.MethodHandles.Lookup#findGetter findGetter(C.class,"f",T.class)}</td></tr>
* <tr><td>2</td><td>REF_getStatic</td><td>C.f:T</td><td>(&nbsp;)T</td><td>getstatic C.f:T</td>
* <td>{@linkplain java.dyn.MethodHandles.Lookup#findStaticGetter findStaticGetter(C.class,"f",T.class)}</td></tr>
* <tr><td>3</td><td>REF_putField</td><td>C.f:T</td><td>(C,T)void</td><td>putfield C.f:T</td>
* <td>{@linkplain java.dyn.MethodHandles.Lookup#findSetter findSetter(C.class,"f",T.class)}</td></tr>
* <tr><td>4</td><td>REF_putStatic</td><td>C.f:T</td><td>(T)void</td><td>putstatic C.f:T</td>
* <td>{@linkplain java.dyn.MethodHandles.Lookup#findStaticSetter findStaticSetter(C.class,"f",T.class)}</td></tr>
* <tr><td>5</td><td>REF_invokeVirtual</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokevirtual C.m(A*)T</td>
* <td>{@linkplain java.dyn.MethodHandles.Lookup#findVirtual findVirtual(C.class,"m",MT)}</td></tr>
* <tr><td>6</td><td>REF_invokeStatic</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokestatic C.m(A*)T</td>
* <td>{@linkplain java.dyn.MethodHandles.Lookup#findStatic findStatic(C.class,"m",MT)}</td></tr>
* <tr><td>7</td><td>REF_invokeSpecial</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokespecial C.m(A*)T</td>
* <td>{@linkplain java.dyn.MethodHandles.Lookup#findSpecial findSpecial(C.class,"m",MT,this.class)}</td></tr>
* <tr><td>8</td><td>REF_newInvokeSpecial</td><td>C.&lt;init&gt;(A*)void</td><td>(A*)C</td><td>new C; dup; invokespecial C.&lt;init&gt;(A*)void</td>
* <td>{@linkplain java.dyn.MethodHandles.Lookup#findConstructor findConstructor(C.class,MT)}</td></tr>
* <tr><td>9</td><td>REF_invokeInterface</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokeinterface C.m(A*)T</td>
* <td>{@linkplain java.dyn.MethodHandles.Lookup#findVirtual findVirtual(C.class,"m",MT)}</td></tr>
* </table>
* </code>
* Here, the type {@code C} is taken from the {@code CONSTANT_Class} reference associated
* with the {@code CONSTANT_NameAndType} descriptor.
* The field name {@code f} or method name {@code m} is taken from the {@code CONSTANT_NameAndType}
* as is the result type {@code T} and (in the case of a method or constructor) the argument type sequence
* {@code A*}.
* <p>
* Each method handle constant has an equivalent instruction sequence called its <em>bytecode behavior</em>.
* In general, creating a method handle constant can be done in exactly the same circumstances that
* the JVM would successfully resolve the symbolic references in the bytecode behavior.
* Also, the type of a method handle constant is such that a valid {@code invokeExact} call
* on the method handle has exactly the same JVM stack effects as the <em>bytecode behavior</em>.
* Finally, calling a method handle constant on a valid set of arguments has exactly the same effect
* and returns the same result (if any) as the corresponding <em>bytecode behavior</em>.
* <p>
* Each method handle constant also has an equivalent reflective <em>lookup expression</em>,
* which is a query to a method in {@link java.dyn.MethodHandles.Lookup}.
* In the example lookup method expression given in the table above, the name {@code MT}
* stands for a {@code MethodType} built from {@code T} and the sequence of argument types {@code A*}.
* (Note that the type {@code C} is not prepended to the query type {@code MT} even if the member is non-static.)
* In the case of {@code findSpecial}, the name {@code this.class} refers to the class containing
* the bytecodes.
* <p>
* The special name {@code <clinit>} is not allowed.
* The special name {@code <init>} is not allowed except for subtag 8 as shown.
* <p>
* The JVM verifier and linker apply the same access checks and restrictions for these references as for the hypothetical
* bytecode instructions specified in the last column of the table. In particular, method handles to
* bytecode instructions specified in the last column of the table.
* A method handle constant will successfully resolve to a method handle if the symbolic references
* of the corresponding bytecode instruction(s) would also resolve successfully.
* Otherwise, an attempt to resolve the constant will throw equivalent linkage errors.
* In particular, method handles to
* private and protected members can be created in exactly those classes for which the corresponding
* normal accesses are legal.
* <p>
* A constant may refer to a method or constructor with the {@code varargs}
* bit (hexadecimal {@code 80}) set in its modifier bitmask.
* The method handle constant produced for such a method behaves the same
* bit (hexadecimal {@code 0x0080}) set in its modifier bitmask.
* The method handle constant produced for such a method behaves as if
* it were created by {@link java.dyn.MethodHandle#asVarargsCollector asVarargsCollector}.
* In other words, the constant method handle will exhibit variable arity,
* when invoked via {@code invokeGeneric}.
* On the other hand, its behavior with respect to {@code invokeExact} will be the same
* as if the {@code varargs} bit were not set.
* The argument-collecting behavior of {@code varargs} can be emulated by
* adapting the method handle constant with
* {@link java.dyn.MethodHandle#asCollector asCollector}.
* There is no provision for doing this automatically.
* <p>
* Although the {@code CONSTANT_MethodHandle} and {@code CONSTANT_MethodType} constant types
* resolve class names, they do not force class initialization.
@ -186,14 +209,14 @@
* by the execution of {@code invokedynamic} and {@code ldc} instructions.
* (Roughly speaking, this means that every use of a constant pool entry
* must lead to the same outcome.
* If the resoultion succeeds, the same object reference is produced
* If the resolution succeeds, the same object reference is produced
* by every subsequent execution of the same instruction.
* If the resolution of the constant causes an error to occur,
* the same error will be re-thrown on every subsequent attempt
* to use this particular constant.)
* <p>
* Constants created by the resolution of these constant pool types are not necessarily
* interned. Except for {@link CONSTANT_Class} and {@link CONSTANT_String} entries,
* interned. Except for {@code CONSTANT_Class} and {@code CONSTANT_String} entries,
* two distinct constant pool entries might not resolve to the same reference
* even if they contain the same symbolic reference.
*
@ -207,31 +230,31 @@
* <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 signature,
* 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.dyn.MethodType MethodType} object for
* the type signature of the dynamic call site.
* 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>
* Next, the bootstrap method call is started, with four or five values being stacked:
* Next, the bootstrap method call is started, with at least four values being stacked:
* <ul>
* <li>a {@code MethodHandle}, the resolved bootstrap method itself </li>
* <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 signature of the call </li>
* <li>optionally, a single object representing one or more <a href="#args">additional static arguments</a> </li>
* <li>a {@code MethodType}, the resolved type descriptor of the call </li>
* <li>optionally, one or more <a href="#args">additional static arguments</a> </li>
* </ul>
* The method handle is then applied to the other values as if by
* {@link java.dyn.MethodHandle#invokeGeneric invokeGeneric}.
* The returned result must be a {@link java.dyn.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 signature and passed to
* 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>
@ -299,11 +322,11 @@
* chosen target object.
*
* <p style="font-size:smaller;">
* (Historic Note: Unlike some previous versions of this specification,
* <em>Historic Note:</em> Unlike some previous versions of this specification,
* these rules do not enable the JVM to duplicate dynamic call sites,
* or to issue &ldquo;causeless&rdquo; bootstrap method calls.
* Every dynamic call site transitions at most once from unlinked to linked,
* just before its first invocation.)
* just before its first invocation.
*
* <h3><a name="bsmattr">the {@code BootstrapMethods} attribute </h3>
* Each {@code CONSTANT_InvokeDynamic} entry contains an index which references
@ -349,7 +372,6 @@
* Static arguments are specified constant pool indexes stored in the {@code BootstrapMethods} attribute.
* Before the bootstrap method is invoked, each index is used to compute an {@code Object}
* reference to the indexed value in the constant pool.
* If the value is a primitive type, it is converted to a reference by boxing conversion.
* The valid constant pool entries are listed in this table:
* <code>
* <table border=1 cellpadding=5 summary="Static argument types">
@ -369,21 +391,43 @@
* 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,
* a fourth argument will be passed to the bootstrap argument,
* either an {@code Object} reference to the sole extra argument (if there is one)
* or an {@code Object} array of references to all the arguments (if there are two or more),
* as if the bootstrap method is a variable-arity method.
* 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 252 extra arguments can be supplied.)
* The bootstrap method will be invoked as if by either {@code invokeGeneric}
* or {@code invokeWithArguments}. (There is no way to tell the difference.)
* <p>
* The normal argument conversion rules for {@code invokeGeneric} 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 {@code java.dyn.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.
* <code>
* <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>
* </code>
* 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>
* The argument and return types listed here are used by the {@code invokeGeneric}
* call to the bootstrap method.
* 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
@ -391,14 +435,8 @@
* In that case, the {@code invokeGeneric} call will pass the extra method handle
* constant as an {@code Object}, but the type matching machinery of {@code invokeGeneric}
* 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.)
* <p>
* If the fourth argument is an array, the array element type must be {@code Object},
* since object arrays (as produced by the JVM at this point) cannot be converted
* to other array types.
* <p>
* If an array is provided, it will appear to be freshly allocated.
* That is, the same array will not appear to two bootstrap method calls.
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
* resulting in an {@code InvokeDynamicBootstrapError}.)
* <p>
* Extra bootstrap method arguments are intended to allow language implementors
* to safely and compactly encode metadata.
@ -406,24 +444,6 @@
* 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.
*
* <p style="font-size:smaller;">
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* (Usage Note: There is no mechanism for specifying five or more positional arguments to the bootstrap method.
* If there are two or more arguments, the Java code of the bootstrap method is required to extract them from
* a varargs-style object array.
* This design uses varargs because it anticipates some use cases where bootstrap arguments
* contribute components of variable-length structures, such as virtual function tables
* or interpreter token streams.
* Such parameters would be awkward or impossible to manage if represented
* as normal positional method arguments,
* since there would need to be one Java method per length.
* On balance, leaving out the varargs feature would cause more trouble to users than keeping it.
* Also, this design allows bootstrap methods to be called in a limited JVM stack depth.
* At both the user and JVM level, the difference between varargs and non-varargs
* calling sequences can easily be bridged via the
* {@link java.dyn.MethodHandle#asSpreader asSpreader}
* and {@link java.dyn.MethodHandle#asSpreader asCollector} methods.)
*
* <h2><a name="structs"></a>Structure Summary</h2>
* <blockquote><pre>// summary of constant and attribute structures
struct CONSTANT_MethodHandle_info {
@ -435,11 +455,6 @@ struct CONSTANT_MethodType_info {
u1 tag = 16;
u2 descriptor_index; // index to CONSTANT_Utf8, as in NameAndType
}
struct CONSTANT_InvokeDynamic_17_info {
u1 tag = 17;
u2 bootstrap_method_index; // index to CONSTANT_MethodHandle
u2 name_and_type_index; // same as for CONSTANT_Methodref, etc.
}
struct CONSTANT_InvokeDynamic_info {
u1 tag = 18;
u2 bootstrap_method_attr_index; // index into BootstrapMethods_attr
@ -456,9 +471,6 @@ struct BootstrapMethods_attr {
} bootstrap_methods[bootstrap_method_count];
}
* </pre></blockquote>
* <p>
* <em>Note: The Proposed Final Draft of JSR 292 may remove the constant tag 17,
* for the sake of simplicity.</em>
*
* @author John Rose, JSR 292 EG
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -478,37 +478,60 @@ public class AdapterMethodHandle extends BoundMethodHandle {
return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY));
}
static MethodHandle makeTypeHandler(Access token,
MethodHandle target, MethodHandle typeHandler) {
static MethodHandle makeVarargsCollector(Access token,
MethodHandle target, Class<?> arrayType) {
Access.check(token);
return new WithTypeHandler(target, typeHandler);
return new AsVarargsCollector(target, arrayType);
}
static class WithTypeHandler extends AdapterMethodHandle {
final MethodHandle target, typeHandler;
WithTypeHandler(MethodHandle target, MethodHandle typeHandler) {
static class AsVarargsCollector extends AdapterMethodHandle {
final MethodHandle target;
final Class<?> arrayType;
MethodHandle cache;
AsVarargsCollector(MethodHandle target, Class<?> arrayType) {
super(target, target.type(), makeConv(OP_RETYPE_ONLY));
this.target = target;
this.typeHandler = typeHandler.asType(TYPE_HANDLER_TYPE);
this.arrayType = arrayType;
this.cache = target.asCollector(arrayType, 0);
}
public MethodHandle asType(MethodType newType) {
if (this.type() == newType)
return this;
try {
MethodHandle retyped = (MethodHandle) typeHandler.invokeExact(target, newType);
// Contract: Must return the desired type, or throw WMT
if (retyped.type() != newType)
throw new WrongMethodTypeException(retyped.toString());
return retyped;
} catch (Throwable ex) {
if (ex instanceof Error) throw (Error)ex;
if (ex instanceof RuntimeException) throw (RuntimeException)ex;
throw new RuntimeException(ex);
}
@Override
public boolean isVarargsCollector() {
return true;
}
@Override
public MethodHandle asType(MethodType newType) {
MethodType type = this.type();
int collectArg = type.parameterCount() - 1;
int newArity = newType.parameterCount();
if (newArity == collectArg+1 &&
type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
// if arity and trailing parameter are compatible, do normal thing
return super.asType(newType);
}
// check cache
if (cache.type().parameterCount() == newArity)
return cache.asType(newType);
// build and cache a collector
int arrayLength = newArity - collectArg;
MethodHandle collector;
try {
collector = target.asCollector(arrayType, arrayLength);
} catch (IllegalArgumentException ex) {
throw new WrongMethodTypeException("cannot build collector");
}
cache = collector;
return collector.asType(newType);
}
public MethodHandle asVarargsCollector(Class<?> arrayType) {
MethodType type = this.type();
if (type.parameterType(type.parameterCount()-1) == arrayType)
return this;
return super.asVarargsCollector(arrayType);
}
private static final MethodType TYPE_HANDLER_TYPE
= MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class);
}
/** Can a checkcast adapter validly convert the target to newType?
@ -939,7 +962,7 @@ public class AdapterMethodHandle extends BoundMethodHandle {
@Override
public String toString() {
return nonAdapter((MethodHandle)vmtarget).toString();
return MethodHandleImpl.getNameString(IMPL_TOKEN, nonAdapter((MethodHandle)vmtarget), this);
}
private static MethodHandle nonAdapter(MethodHandle mh) {

View File

@ -37,17 +37,17 @@ public class CallSiteImpl {
static CallSite makeSite(MethodHandle bootstrapMethod,
// Callee information:
String name, MethodType type,
// Call-site attributes, if any:
// Extra arguments for BSM, if any:
Object info,
// Caller information:
MemberName callerMethod, int callerBCI) {
Class<?> callerClass = callerMethod.getDeclaringClass();
Object caller;
if (bootstrapMethod.type().parameterType(0) == Class.class)
if (bootstrapMethod.type().parameterType(0) == Class.class && TRANSITIONAL_BEFORE_PFD)
caller = callerClass; // remove for PFD
else
caller = MethodHandleImpl.IMPL_LOOKUP.in(callerClass);
if (bootstrapMethod == null) {
if (bootstrapMethod == null && TRANSITIONAL_BEFORE_PFD) {
// If there is no bootstrap method, throw IncompatibleClassChangeError.
// This is a valid generic error type for resolution (JLS 12.3.3).
throw new IncompatibleClassChangeError
@ -56,30 +56,35 @@ public class CallSiteImpl {
CallSite site;
try {
Object binding;
info = maybeReBox(info);
if (info == null) {
if (false) // switch when invokeGeneric works
binding = bootstrapMethod.invokeGeneric(caller, name, type);
else
binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type });
binding = bootstrapMethod.invokeGeneric(caller, name, type);
} else if (!info.getClass().isArray()) {
binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
} else {
info = maybeReBox(info);
if (false) // switch when invokeGeneric works
binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
Object[] argv = (Object[]) info;
if (3 + argv.length > 255)
new InvokeDynamicBootstrapError("too many bootstrap method arguments");
MethodType bsmType = bootstrapMethod.type();
if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
binding = bootstrapMethod.invokeGeneric(caller, name, type, argv);
else
binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type, info });
binding = MethodHandles.spreadInvoker(bsmType, 3)
.invokeGeneric(bootstrapMethod, caller, name, type, argv);
}
//System.out.println("BSM for "+name+type+" => "+binding);
if (binding instanceof CallSite) {
site = (CallSite) binding;
} else if (binding instanceof MethodHandle) {
} else if (binding instanceof MethodHandle && TRANSITIONAL_BEFORE_PFD) {
// Transitional!
MethodHandle target = (MethodHandle) binding;
site = new ConstantCallSite(target);
} else {
throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite");
throw new ClassCastException("bootstrap method failed to produce a CallSite");
}
PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type,
callerMethod, callerBCI);
if (TRANSITIONAL_BEFORE_PFD)
PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type,
callerMethod, callerBCI);
assert(site.getTarget() != null);
assert(site.getTarget().type().equals(type));
} catch (Throwable ex) {
@ -93,6 +98,8 @@ public class CallSiteImpl {
return site;
}
private static boolean TRANSITIONAL_BEFORE_PFD = true; // FIXME: remove for PFD
private static Object maybeReBox(Object x) {
if (x instanceof Integer) {
int xi = (int) x;
@ -117,11 +124,12 @@ public class CallSiteImpl {
static {
try {
PRIVATE_INITIALIZE_CALL_SITE =
!TRANSITIONAL_BEFORE_PFD ? null :
MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM",
MethodType.methodType(void.class,
String.class, MethodType.class,
MemberName.class, int.class));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}

View File

@ -187,7 +187,7 @@ class FilterGeneric {
MethodHandle entryPoint = null;
try {
entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
}
if (entryPoint == null) continue;
Constructor<? extends Adapter> ctor = null;

View File

@ -56,7 +56,7 @@ public class FilterOneArgument extends BoundMethodHandle {
INVOKE =
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke",
MethodType.genericMethodType(1));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -28,6 +28,7 @@ package sun.dyn;
import java.dyn.*;
import java.lang.reflect.*;
import sun.dyn.util.*;
import static sun.dyn.MethodTypeImpl.invokers;
/**
* Adapters which mediate between incoming calls which are generic
@ -128,7 +129,7 @@ class FromGeneric {
MethodType targetType, MethodType internalType) {
// All the adapters we have here have reference-untyped internal calls.
assert(internalType == internalType.erase());
MethodHandle invoker = MethodHandles.exactInvoker(targetType);
MethodHandle invoker = invokers(targetType).exactInvoker();
// cast all narrow reference types, unbox all primitive arguments:
MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
MethodHandle fixArgs = AdapterMethodHandle.convertArguments(Access.TOKEN,
@ -203,7 +204,7 @@ class FromGeneric {
MethodHandle entryPoint = null;
try {
entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
}
if (entryPoint == null) continue;
Constructor<? extends Adapter> ctor = null;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 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
@ -28,6 +28,7 @@ package sun.dyn;
import java.dyn.*;
import java.lang.reflect.*;
import sun.dyn.util.*;
import static sun.dyn.MethodTypeImpl.invokers;
/**
* Adapters which manage MethodHanndle.invokeGeneric calls.
@ -43,7 +44,7 @@ class InvokeGeneric {
/** Compute and cache information for this adapter, so that it can
* call out to targets of the erasure-family of the given erased type.
*/
private InvokeGeneric(MethodType erasedCallerType) throws NoAccessException {
private InvokeGeneric(MethodType erasedCallerType) throws ReflectiveOperationException {
this.erasedCallerType = erasedCallerType;
this.initialInvoker = makeInitialInvoker();
assert initialInvoker.type().equals(erasedCallerType
@ -63,14 +64,14 @@ class InvokeGeneric {
try {
InvokeGeneric gen = new InvokeGeneric(form.erasedType());
form.genericInvoker = genericInvoker = gen.initialInvoker;
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
return genericInvoker;
}
private MethodHandle makeInitialInvoker() throws NoAccessException {
private MethodHandle makeInitialInvoker() throws ReflectiveOperationException {
// postDispatch = #(MH'; MT, MH; A...){MH'(MT, MH; A)}
MethodHandle postDispatch = makePostDispatchInvoker();
MethodHandle invoker;
@ -87,14 +88,14 @@ class InvokeGeneric {
private MethodHandle makePostDispatchInvoker() {
// Take (MH'; MT, MH; A...) and run MH'(MT, MH; A...).
MethodType invokerType = erasedCallerType.insertParameterTypes(0, EXTRA_ARGS);
return MethodHandles.exactInvoker(invokerType);
return invokers(invokerType).exactInvoker();
}
private MethodHandle dropDispatchArguments(MethodHandle targetInvoker) {
assert(targetInvoker.type().parameterType(0) == MethodHandle.class);
return MethodHandles.dropArguments(targetInvoker, 1, EXTRA_ARGS);
}
private MethodHandle dispatcher(String dispatchName) throws NoAccessException {
private MethodHandle dispatcher(String dispatchName) throws ReflectiveOperationException {
return lookup().bind(this, dispatchName,
MethodType.methodType(MethodHandle.class,
MethodType.class, MethodHandle.class));
@ -108,7 +109,7 @@ class InvokeGeneric {
*/
private MethodHandle dispatch(MethodType callerType, MethodHandle target) {
MethodType targetType = target.type();
if (USE_AS_TYPE_PATH || target instanceof AdapterMethodHandle.WithTypeHandler) {
if (USE_AS_TYPE_PATH || target.isVarargsCollector()) {
MethodHandle newTarget = target.asType(callerType);
targetType = callerType;
Invokers invokers = MethodTypeImpl.invokers(Access.TOKEN, targetType);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -47,7 +47,7 @@ public class Invokers {
private /*lazy*/ MethodHandle genericInvoker;
// generic (untyped) invoker for the outgoing call; accepts a single Object[]
private final /*lazy*/ MethodHandle[] varargsInvokers;
private final /*lazy*/ MethodHandle[] spreadInvokers;
// invoker for an unbound callsite
private /*lazy*/ MethodHandle uninitializedCallSite;
@ -55,10 +55,9 @@ public class Invokers {
/** Compute and cache information common to all collecting adapters
* that implement members of the erasure-family of the given erased type.
*/
public Invokers(Access token, MethodType targetType) {
Access.check(token);
/*non-public*/ Invokers(MethodType targetType) {
this.targetType = targetType;
this.varargsInvokers = new MethodHandle[targetType.parameterCount()+1];
this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
}
public static MethodType invokerType(MethodType targetType) {
@ -69,8 +68,8 @@ public class Invokers {
MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker;
try {
invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType);
} catch (NoAccessException ex) {
invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invokeExact", targetType);
} catch (ReflectiveOperationException ex) {
throw new InternalError("JVM cannot find invoker for "+targetType);
}
assert(invokerType(targetType) == invoker.type());
@ -101,13 +100,12 @@ public class Invokers {
return invoker;
}
public MethodHandle varargsInvoker(int objectArgCount) {
MethodHandle vaInvoker = varargsInvokers[objectArgCount];
public MethodHandle spreadInvoker(int objectArgCount) {
MethodHandle vaInvoker = spreadInvokers[objectArgCount];
if (vaInvoker != null) return vaInvoker;
MethodHandle gInvoker = genericInvoker();
MethodType vaType = MethodType.genericMethodType(objectArgCount, true);
vaInvoker = MethodHandles.spreadArguments(gInvoker, invokerType(vaType));
varargsInvokers[objectArgCount] = vaInvoker;
vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount);
spreadInvokers[objectArgCount] = vaInvoker;
return vaInvoker;
}
@ -118,7 +116,7 @@ public class Invokers {
if (invoker != null) return invoker;
if (targetType.parameterCount() > 0) {
MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount());
Invokers invokers0 = MethodTypeImpl.invokers(Access.TOKEN, type0);
Invokers invokers0 = MethodTypeImpl.invokers(type0);
invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(),
0, targetType.parameterList());
assert(invoker.type().equals(targetType));
@ -130,7 +128,7 @@ public class Invokers {
THROW_UCS = MethodHandleImpl.IMPL_LOOKUP
.findStatic(CallSite.class, "uninitializedCallSite",
MethodType.methodType(Empty.class));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -451,8 +451,6 @@ public final class MemberName implements Member, Cloneable {
return type.toString(); // class java.lang.String
// else it is a field, method, or constructor
StringBuilder buf = new StringBuilder();
if (!isResolved())
buf.append("*.");
if (getDeclaringClass() != null) {
buf.append(getName(clazz));
buf.append('.');
@ -512,14 +510,24 @@ public final class MemberName implements Member, Cloneable {
public static RuntimeException newIllegalArgumentException(String message) {
return new IllegalArgumentException(message);
}
public static NoAccessException newNoAccessException(MemberName name, Class<?> lookupClass) {
return newNoAccessException("cannot access", name, lookupClass);
public static IllegalAccessException newNoAccessException(MemberName name, Object from) {
return newNoAccessException("cannot access", name, from);
}
public static NoAccessException newNoAccessException(String message,
MemberName name, Class<?> lookupClass) {
public static IllegalAccessException newNoAccessException(String message,
MemberName name, Object from) {
message += ": " + name;
if (lookupClass != null) message += ", from " + lookupClass.getName();
return new NoAccessException(message);
if (from != null) message += ", from " + from;
return new IllegalAccessException(message);
}
public static ReflectiveOperationException newNoAccessException(MemberName name) {
if (name.isResolved())
return new IllegalAccessException(name.toString());
else if (name.isConstructor())
return new NoSuchMethodException(name.toString());
else if (name.isMethod())
return new NoSuchMethodException(name.toString());
else
return new NoSuchFieldException(name.toString());
}
public static Error uncaughtException(Exception ex) {
Error err = new InternalError("uncaught exception");
@ -643,14 +651,20 @@ public final class MemberName implements Member, Cloneable {
/** Produce a resolved version of the given member.
* Super types are searched (for inherited members) if {@code searchSupers} is true.
* Access checking is performed on behalf of the given {@code lookupClass}.
* If lookup fails or access is not permitted, a {@linkplain NoAccessException} is thrown.
* If lookup fails or access is not permitted, a {@linkplain ReflectiveOperationException} is thrown.
* Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
*/
public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) throws NoAccessException {
public
<NoSuchMemberException extends ReflectiveOperationException>
MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass,
Class<NoSuchMemberException> nsmClass)
throws IllegalAccessException, NoSuchMemberException {
MemberName result = resolveOrNull(m, searchSupers, lookupClass);
if (result != null)
return result;
throw newNoAccessException(m, lookupClass);
ReflectiveOperationException ex = newNoAccessException(m);
if (ex instanceof IllegalAccessException) throw (IllegalAccessException) ex;
throw nsmClass.cast(ex);
}
/** Return a list of all methods defined by the given class.
* Super types are searched (for inherited members) if {@code searchSupers} is true.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -30,7 +30,6 @@ import java.dyn.MethodHandles.Lookup;
import java.util.logging.Level;
import java.util.logging.Logger;
import sun.dyn.util.VerifyType;
import java.dyn.NoAccessException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -136,6 +135,8 @@ public abstract class MethodHandleImpl {
}
static {
if (!MethodHandleNatives.JVM_SUPPORT) // force init of native API
throw new InternalError("No JVM support for JSR 292");
// Force initialization of Lookup, so it calls us back as initLookup:
MethodHandles.publicLookup();
if (IMPL_LOOKUP_INIT == null)
@ -167,11 +168,11 @@ public abstract class MethodHandleImpl {
* @param doDispatch whether the method handle will test the receiver type
* @param lookupClass access-check relative to this class
* @return a direct handle to the matching method
* @throws NoAccessException if the given method cannot be accessed by the lookup class
* @throws IllegalAccessException if the given method cannot be accessed by the lookup class
*/
public static
MethodHandle findMethod(Access token, MemberName method,
boolean doDispatch, Class<?> lookupClass) throws NoAccessException {
boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException {
Access.check(token); // only trusted calls
MethodType mtype = method.getMethodType();
if (!method.isStatic()) {
@ -184,7 +185,10 @@ public abstract class MethodHandleImpl {
if (!mh.isValid())
throw newNoAccessException(method, lookupClass);
assert(mh.type() == mtype);
return mh;
if (!method.isVarargs())
return mh;
else
return mh.asVarargsCollector(mtype.parameterType(mtype.parameterCount()-1));
}
public static
@ -302,7 +306,7 @@ public abstract class MethodHandleImpl {
MethodHandle invoke = null;
try {
invoke = lookup.findVirtual(AllocateObject.class, name, MethodType.genericMethodType(nargs));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
}
if (invoke == null) break;
invokes.add(invoke);
@ -317,7 +321,7 @@ public abstract class MethodHandleImpl {
static {
try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
@ -469,7 +473,7 @@ public abstract class MethodHandleImpl {
MethodHandle mh;
try {
mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
if (evclass != vclass || (!isStatic && ecclass != cclass)) {
@ -537,7 +541,7 @@ public abstract class MethodHandleImpl {
MethodHandle mh;
try {
mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
if (caclass != null) {
@ -1009,7 +1013,7 @@ public abstract class MethodHandleImpl {
MethodHandle invoke = null;
try {
invoke = lookup.findVirtual(GuardWithTest.class, name, MethodType.genericMethodType(nargs));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
}
if (invoke == null) break;
invokes.add(invoke);
@ -1024,7 +1028,7 @@ public abstract class MethodHandleImpl {
static {
try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
@ -1145,7 +1149,7 @@ public abstract class MethodHandleImpl {
MethodHandle invoke = null;
try {
invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
}
if (invoke == null) break;
invokes.add(invoke);
@ -1160,7 +1164,7 @@ public abstract class MethodHandleImpl {
static {
try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
@ -1207,20 +1211,30 @@ public abstract class MethodHandleImpl {
THROW_EXCEPTION
= IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
MethodType.methodType(Empty.class, Throwable.class));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
public static String getNameString(Access token, MethodHandle target) {
public static String getNameString(Access token, MethodHandle target, Object type) {
Access.check(token);
if (!(type instanceof MethodType)) {
if (type == null)
type = target.type();
else if (type instanceof MethodHandle)
type = ((MethodHandle)type).type();
}
MemberName name = null;
if (target != null)
name = MethodHandleNatives.getMethodName(target);
if (name == null)
return "invoke" + target.type();
return name.getName() + target.type();
return "invoke" + type;
return name.getName() + type;
}
public static String getNameString(Access token, MethodHandle target) {
return getNameString(token, target, null);
}
static String addTypeString(Object obj, MethodHandle target) {
@ -1263,8 +1277,8 @@ public abstract class MethodHandleImpl {
return MethodHandleNatives.getBootstrap(callerClass);
}
public static MethodHandle withTypeHandler(Access token, MethodHandle target, MethodHandle typeHandler) {
public static MethodHandle asVarargsCollector(Access token, MethodHandle target, Class<?> arrayType) {
Access.check(token);
return AdapterMethodHandle.makeTypeHandler(token, target, typeHandler);
return AdapterMethodHandle.makeVarargsCollector(token, target, arrayType);
}
}

View File

@ -350,7 +350,7 @@ class MethodHandleNatives {
case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type );
}
throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
Error err = new IncompatibleClassChangeError();
err.initCause(ex);
throw err;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -498,9 +498,12 @@ public class MethodTypeImpl {
public static Invokers invokers(Access token, MethodType type) {
Access.check(token);
return invokers(type);
}
/*non-public*/ static Invokers invokers(MethodType type) {
Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type);
if (inv != null) return inv;
inv = new Invokers(token, type);
inv = new Invokers(type);
METHOD_TYPE_FRIEND.setInvokers(type, inv);
return inv;
}

View File

@ -167,7 +167,7 @@ class SpreadGeneric {
MethodHandle entryPoint = null;
try {
entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
}
if (entryPoint == null) continue;
Constructor<? extends Adapter> ctor = null;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* 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
@ -31,6 +31,7 @@ import java.lang.reflect.InvocationTargetException;
import sun.dyn.util.ValueConversions;
import sun.dyn.util.Wrapper;
import static sun.dyn.MemberName.newIllegalArgumentException;
import static sun.dyn.MethodTypeImpl.invokers;
/**
* Adapters which mediate between incoming calls which are not generic
@ -72,7 +73,7 @@ class ToGeneric {
assert(entryType.erase() == entryType); // for now
// incoming call will first "forget" all reference types except Object
this.entryType = entryType;
MethodHandle invoker0 = MethodHandles.exactInvoker(entryType.generic());
MethodHandle invoker0 = invokers(entryType.generic()).exactInvoker();
MethodType rawEntryTypeInit;
Adapter ad = findAdapter(rawEntryTypeInit = entryType);
if (ad != null) {
@ -284,7 +285,7 @@ class ToGeneric {
try {
entryPoint = MethodHandleImpl.IMPL_LOOKUP.
findSpecial(acls, iname, entryPointType, acls);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
}
if (entryPoint == null) continue;
Constructor<? extends Adapter> ctor = null;

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 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 sun.dyn;
import java.dyn.MethodHandle;
/**
* Private API used inside of java.dyn.MethodHandles.
* Interface implemented by every object which is produced by
* {@link java.dyn.MethodHandles#asInstance MethodHandles.asInstance}.
* The methods of this interface allow a caller to recover the parameters
* to {@code asInstance}.
* This allows applications to repeatedly convert between method handles
* and SAM objects, without the risk of creating unbounded delegation chains.
*/
public interface WrapperInstance {
/** Produce or recover a target method handle which is behaviorally
* equivalent to the SAM method of this object.
*/
public MethodHandle getWrapperInstanceTarget();
/** Recover the SAM type for which this object was created.
*/
public Class<?> getWrapperInstanceType();
}

View File

@ -153,7 +153,7 @@ public class ValueConversions {
try {
// actually, type is wrong; the Java method takes Object
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
mh = null;
}
} else {
@ -289,7 +289,7 @@ public class ValueConversions {
if (exact) {
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
mh = null;
}
} else {
@ -408,7 +408,7 @@ public class ValueConversions {
if (exact) {
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
mh = null;
}
} else {
@ -492,7 +492,7 @@ public class ValueConversions {
case INT: case LONG: case FLOAT: case DOUBLE:
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
mh = null;
}
break;
@ -654,7 +654,7 @@ public class ValueConversions {
type = type.appendParameterTypes(wrap.primitiveType());
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
mh = null;
}
if (mh == null && wrap == Wrapper.VOID) {
@ -723,7 +723,7 @@ public class ValueConversions {
MethodHandle array = null;
try {
array = lookup.findStatic(ValueConversions.class, name, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
}
if (array == null) break;
arrays.add(array);
@ -784,7 +784,7 @@ public class ValueConversions {
MethodHandle array = null;
try {
array = lookup.findStatic(ValueConversions.class, name, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
}
if (array == null) break;
arrays.add(array);

View File

@ -25,7 +25,6 @@
package sun.dyn.util;
import java.dyn.NoAccessException;
import java.lang.reflect.Modifier;
import sun.dyn.MemberName;
import sun.dyn.MethodHandleImpl;
@ -139,6 +138,8 @@ public class VerifyAccess {
* <li>C is public.
* <li>C and D are members of the same runtime package.
* </ul>
* @param refc the symbolic reference class to which access is being checked (C)
* @param lookupClass the class performing the lookup (D)
*/
public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass) {
int mods = refc.getModifiers();

View File

@ -23,15 +23,19 @@
/* @test
* @summary smoke test for invokedynamic instructions
* @library indify
* @build indify.Indify
* @compile InvokeDynamicPrintArgs.java
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic
* indify.Indify
* --verify-specifier-count=3 --transitionalJSR292=false
* --expand-properties --classpath ${test.classes}
* --java InvokeDynamicPrintArgs --check-output
* --java test.java.dyn.InvokeDynamicPrintArgs --check-output
*/
package test.java.dyn;
import org.junit.Test;
import java.util.*;
import java.io.*;
@ -53,6 +57,20 @@ public class InvokeDynamicPrintArgs {
closeBuf();
}
@Test
public void testInvokeDynamicPrintArgs() throws IOException {
System.err.println(System.getProperties());
String testClassPath = System.getProperty("build.test.classes.dir");
if (testClassPath == null) throw new RuntimeException();
String[] args = new String[]{
"--verify-specifier-count=3", "--transitionalJSR292=false",
"--expand-properties", "--classpath", testClassPath,
"--java", "test.java.dyn.InvokeDynamicPrintArgs", "--check-output"
};
System.err.println("Indify: "+Arrays.toString(args));
indify.Indify.main(args);
}
private static PrintStream oldOut;
private static ByteArrayOutputStream buf;
private static void openBuf() {
@ -79,11 +97,11 @@ public class InvokeDynamicPrintArgs {
}
private static final String[] EXPECT_OUTPUT = {
"Printing some argument lists, starting with a empty one:",
"[InvokeDynamicPrintArgs, nothing, ()void][]",
"[InvokeDynamicPrintArgs, bar, (java.lang.String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]",
"[InvokeDynamicPrintArgs, bar2, (java.lang.String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]",
"[InvokeDynamicPrintArgs, baz, (java.lang.String,int,double)void, 1234.5][baz arg, 2, 3.14]",
"[InvokeDynamicPrintArgs, foo, (java.lang.String)void][foo arg]",
"[test.java.dyn.InvokeDynamicPrintArgs, nothing, ()void][]",
"[test.java.dyn.InvokeDynamicPrintArgs, bar, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]",
"[test.java.dyn.InvokeDynamicPrintArgs, bar2, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]",
"[test.java.dyn.InvokeDynamicPrintArgs, baz, (String,int,double)void, 1234.5][baz arg, 2, 3.14]",
"[test.java.dyn.InvokeDynamicPrintArgs, foo, (String)void][foo arg]",
"Done printing argument lists."
};
@ -110,18 +128,15 @@ public class InvokeDynamicPrintArgs {
return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm());
}
private static CallSite bsm2(Lookup caller, String name, MethodType type, Object arg) throws ReflectiveOperationException {
private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws ReflectiveOperationException {
// ignore caller and name, but match the type:
List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type));
if (arg instanceof Object[])
bsmInfo.addAll(Arrays.asList((Object[])arg));
else
bsmInfo.add(arg);
bsmInfo.addAll(Arrays.asList((Object[])arg));
return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
}
private static MethodType MT_bsm2() {
shouldNotCallThis();
return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object.class);
return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object[].class);
}
private static MethodHandle MH_bsm2() throws ReflectiveOperationException {
shouldNotCallThis();

View File

@ -320,7 +320,7 @@ public class InvokeGenericTest {
MethodHandle callable(List<Class<?>> params) {
MethodHandle mh = CALLABLES.get(params);
if (mh == null) {
mh = collectArguments(collector_MH, methodType(Object.class, params));
mh = collector_MH.asType(methodType(Object.class, params));
CALLABLES.put(params, mh);
}
return mh;
@ -338,7 +338,7 @@ public class InvokeGenericTest {
= LOOKUP.findStatic(LOOKUP.lookupClass(),
"collector",
methodType(Object.class, Object[].class));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 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
@ -74,7 +74,7 @@ static final private Lookup LOOKUP = lookup();
// static final private MethodHandle HASHCODE_1 = LOOKUP.findVirtual(Object.class,
// "hashCode", methodType(int.class));
// form required if NoAccessException is intercepted:
// form required if ReflectiveOperationException is intercepted:
static final private MethodHandle CONCAT_2, HASHCODE_2;
static {
try {
@ -82,7 +82,7 @@ static {
"concat", methodType(String.class, String.class));
HASHCODE_2 = LOOKUP.findVirtual(Object.class,
"hashCode", methodType(int.class));
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
@ -142,37 +142,79 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
Assert.assertEquals(exp, act);
}
static MethodHandle asList;
@Test public void testWithTypeHandler() throws Throwable {
@Test public void testMethodHandlesSummary() throws Throwable {
{{
{} /// JAVADOC
MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList());
MethodHandle asList = lookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
JavaDocExamplesTest.asList = asList;
/*
static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
return asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
}
*/
MethodHandle collectingTypeHandler = lookup()
.findStatic(lookup().lookupClass(), "collectingTypeHandler",
methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
assertEquals("[]", makeAnyList.invokeGeneric().toString());
assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
Object x, y; String s; int i;
MethodType mt; MethodHandle mh;
MethodHandles.Lookup lookup = MethodHandles.lookup();
// mt is (char,char)String
mt = MethodType.methodType(String.class, char.class, char.class);
mh = lookup.findVirtual(String.class, "replace", mt);
s = (String) mh.invokeExact("daddy",'d','n');
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
assert(s.equals("nanny"));
// weakly typed invocation (using MHs.invoke)
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
assert(s.equals("savvy"));
// mt is (Object[])List
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
x = mh.invokeGeneric("one", "two");
// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assert(x.equals(java.util.Arrays.asList("one","two")));
// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
mh = mh.asType(mt);
x = mh.invokeExact((Object)1, (Object)2, (Object)3);
// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
assert(x.equals(java.util.Arrays.asList(1,2,3)));
// mt is { =&gt; int}
mt = MethodType.methodType(int.class);
mh = lookup.findVirtual(java.util.List.class, "size", mt);
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
// invokeExact(Ljava/util/List;)I
assert(i == 3);
mt = MethodType.methodType(void.class, String.class);
mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
mh.invokeExact(System.out, "Hello, world.");
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
{}
}}
}
static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
//System.out.println("Converting "+asList+" to "+newType);
MethodHandle conv = asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
//System.out.println(" =>"+conv);
return conv;
}
@Test public void testAsVarargsCollector() throws Throwable {
{{
{} /// JAVADOC
MethodHandle asList = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
.asVarargsCollector(Object[].class);
assertEquals("[]", asList.invokeGeneric().toString());
assertEquals("[1]", asList.invokeGeneric(1).toString());
assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
Object[] argv = { "three", "thee", "tee" };
assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
List ls = (List) asList.invokeGeneric((Object)argv);
assertEquals(1, ls.size());
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
}}
}
@Test public void testVarargsCollectorSuppression() throws Throwable {
{{
{} /// JAVADOC
MethodHandle vamh = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
.asVarargsCollector(Object[].class);
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
assert(vamh.type().equals(mh.type()));
assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
boolean failed = false;
try { mh.invokeGeneric(1,2,3); }
catch (WrongMethodTypeException ex) { failed = true; }
assert(failed);
{}
}}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 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
@ -323,6 +323,44 @@ public class MethodHandlesTest {
return x.getClass().getSimpleName();
}
/** Return lambda(arg...[arity]) { new Object[]{ arg... } } */
static MethodHandle varargsList(int arity) {
return ValueConversions.varargsList(arity);
}
/** Return lambda(arg...[arity]) { Arrays.asList(arg...) } */
static MethodHandle varargsArray(int arity) {
return ValueConversions.varargsArray(arity);
}
/** Variation of varargsList, but with the given rtype. */
static MethodHandle varargsList(int arity, Class<?> rtype) {
MethodHandle list = varargsList(arity);
MethodType listType = list.type().changeReturnType(rtype);
if (List.class.isAssignableFrom(rtype) || rtype == void.class || rtype == Object.class) {
// OK
} else if (rtype.isAssignableFrom(String.class)) {
if (LIST_TO_STRING == null)
try {
LIST_TO_STRING = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToString",
MethodType.methodType(String.class, List.class));
} catch (Exception ex) { throw new RuntimeException(ex); }
list = MethodHandles.filterReturnValue(list, LIST_TO_STRING);
} else if (rtype.isPrimitive()) {
if (LIST_TO_INT == null)
try {
LIST_TO_INT = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToInt",
MethodType.methodType(int.class, List.class));
} catch (Exception ex) { throw new RuntimeException(ex); }
list = MethodHandles.filterReturnValue(list, LIST_TO_INT);
list = MethodHandles.explicitCastArguments(list, listType);
} else {
throw new RuntimeException("varargsList: "+rtype);
}
return list.asType(listType);
}
private static MethodHandle LIST_TO_STRING, LIST_TO_INT;
private static String listToString(List x) { return x.toString(); }
private static int listToInt(List x) { return x.toString().hashCode(); }
static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) {
return changeArgTypes(target, 0, 999, argType);
}
@ -458,8 +496,12 @@ public class MethodHandlesTest {
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.in(defc).findStatic(defc, name, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
noAccess = ex;
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
@ -528,8 +570,12 @@ public class MethodHandlesTest {
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.in(defc).findVirtual(defc, methodName, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
noAccess = ex;
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findVirtual "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
@ -558,11 +604,12 @@ public class MethodHandlesTest {
testFindSpecial(SubExample.class, Example.class, void.class, "v0");
testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
// Do some negative testing:
testFindSpecial(false, EXAMPLE, SubExample.class, Example.class, void.class, "bogus");
testFindSpecial(false, PRIVATE, SubExample.class, Example.class, void.class, "bogus");
for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) {
testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0");
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class);
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0");
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus");
}
}
@ -583,8 +630,12 @@ public class MethodHandlesTest {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
if (verbosity >= 5) System.out.println(" lookup => "+lookup.in(specialCaller));
target = lookup.in(specialCaller).findSpecial(defc, name, type, specialCaller);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
noAccess = ex;
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findSpecial from "+specialCaller.getName()+" to "+defc.getName()+"."+name+"/"+type+" => "+target
@ -639,8 +690,12 @@ public class MethodHandlesTest {
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.in(defc).bind(receiver, methodName, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
noAccess = ex;
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
@ -698,14 +753,9 @@ public class MethodHandlesTest {
Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
MethodType type = MethodType.methodType(ret, params);
Method rmethod = null;
Method rmethod = defc.getDeclaredMethod(name, params);
MethodHandle target = null;
Exception noAccess = null;
try {
rmethod = defc.getDeclaredMethod(name, params);
} catch (NoSuchMethodException ex) {
throw new NoAccessException(ex);
}
boolean isStatic = (rcvc == null);
boolean isSpecial = (specialCaller != null);
try {
@ -714,8 +764,12 @@ public class MethodHandlesTest {
target = lookup.in(specialCaller).unreflectSpecial(rmethod, specialCaller);
else
target = lookup.in(defc).unreflect(rmethod);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
noAccess = ex;
if (name.contains("bogus"))
assertTrue(noAccess instanceof NoSuchMethodException);
else
assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("unreflect"+(isSpecial?"Special":"")+" "+defc.getName()+"."+name+"/"+type
@ -824,25 +878,28 @@ public class MethodHandlesTest {
if (type == float.class) {
float v = 'F';
if (isStatic) v++;
assert(value.equals(v));
assertTrue(value.equals(v));
}
assert(name.equals(field.getName()));
assert(type.equals(field.getType()));
assert(isStatic == (Modifier.isStatic(field.getModifiers())));
assertTrue(name.equals(field.getName()));
assertTrue(type.equals(field.getType()));
assertTrue(isStatic == (Modifier.isStatic(field.getModifiers())));
cases.add(new Object[]{ field, value });
}
}
cases.add(new Object[]{ new Object[]{ false, HasFields.class, "bogus_fD", double.class }, Error.class });
cases.add(new Object[]{ new Object[]{ true, HasFields.class, "bogus_sL", Object.class }, Error.class });
CASES = cases.toArray(new Object[0][]);
}
}
static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC_FIELD = 3;
static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10;
static boolean testModeMatches(int testMode, boolean isStatic) {
switch (testMode) {
case TEST_FIND_STATIC_FIELD: return isStatic;
case TEST_FIND_STATIC: return isStatic;
case TEST_FIND_FIELD: return !isStatic;
default: return true; // unreflect matches both
case TEST_UNREFLECT: return true; // unreflect matches both
}
throw new InternalError("testMode="+testMode);
}
@Test
@ -858,54 +915,161 @@ public class MethodHandlesTest {
@Test
public void testFindStaticGetter() throws Throwable {
startTest("findStaticGetter");
testGetter(TEST_FIND_STATIC_FIELD);
testGetter(TEST_FIND_STATIC);
}
public void testGetter(int testMode) throws Throwable {
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
for (Object[] c : HasFields.CASES) {
Field f = (Field)c[0];
Object value = c[1];
Class<?> type = f.getType();
testGetter(lookup, f, type, value, testMode);
boolean positive = (c[1] != Error.class);
testGetter(positive, lookup, c[0], c[1], testMode);
}
testGetter(true, lookup,
new Object[]{ true, System.class, "out", java.io.PrintStream.class },
System.out, testMode);
for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
testGetter(false, lookup,
new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
null, testMode);
}
}
public void testGetter(MethodHandles.Lookup lookup,
Field f, Class<?> type, Object value, int testMode) throws Throwable {
boolean isStatic = Modifier.isStatic(f.getModifiers());
Class<?> fclass = f.getDeclaringClass();
String fname = f.getName();
Class<?> ftype = f.getType();
public void testGetter(boolean positive, MethodHandles.Lookup lookup,
Object fieldRef, Object value, int testMode) throws Throwable {
testAccessor(positive, lookup, fieldRef, value, testMode);
}
public void testAccessor(boolean positive, MethodHandles.Lookup lookup,
Object fieldRef, Object value, int testMode0) throws Throwable {
boolean isGetter = ((testMode0 & TEST_SETTER) == 0);
int testMode = testMode0 & ~TEST_SETTER;
boolean isStatic;
Class<?> fclass;
String fname;
Class<?> ftype;
Field f = (fieldRef instanceof Field ? (Field)fieldRef : null);
if (f != null) {
isStatic = Modifier.isStatic(f.getModifiers());
fclass = f.getDeclaringClass();
fname = f.getName();
ftype = f.getType();
} else {
Object[] scnt = (Object[]) fieldRef;
isStatic = (Boolean) scnt[0];
fclass = (Class<?>) scnt[1];
fname = (String) scnt[2];
ftype = (Class<?>) scnt[3];
try {
f = fclass.getDeclaredField(fname);
} catch (ReflectiveOperationException ex) {
f = null;
}
}
if (!testModeMatches(testMode, isStatic)) return;
countTest(true);
MethodType expType = MethodType.methodType(type, HasFields.class);
if (f == null && testMode == TEST_UNREFLECT) return;
countTest(positive);
MethodType expType;
if (isGetter)
expType = MethodType.methodType(ftype, HasFields.class);
else
expType = MethodType.methodType(void.class, HasFields.class, ftype);
if (isStatic) expType = expType.dropParameterTypes(0, 1);
MethodHandle mh = lookup.unreflectGetter(f);
Exception noAccess = null;
MethodHandle mh;
try {
switch (testMode0) {
case TEST_UNREFLECT: mh = lookup.unreflectGetter(f); break;
case TEST_FIND_FIELD: mh = lookup.findGetter(fclass, fname, ftype); break;
case TEST_FIND_STATIC: mh = lookup.findStaticGetter(fclass, fname, ftype); break;
case TEST_SETTER|
TEST_UNREFLECT: mh = lookup.unreflectSetter(f); break;
case TEST_SETTER|
TEST_FIND_FIELD: mh = lookup.findSetter(fclass, fname, ftype); break;
case TEST_SETTER|
TEST_FIND_STATIC: mh = lookup.findStaticSetter(fclass, fname, ftype); break;
default:
throw new InternalError("testMode="+testMode);
}
} catch (ReflectiveOperationException ex) {
mh = null;
noAccess = ex;
if (fname.contains("bogus"))
assertTrue(noAccess instanceof NoSuchFieldException);
else
assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype
+" => "+mh
+(noAccess == null ? "" : " !! "+noAccess));
if (positive && noAccess != null) throw new RuntimeException(noAccess);
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, mh != null);
if (!positive) return; // negative test failed as expected
assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount());
assertSame(mh.type(), expType);
assertNameStringContains(mh, fname);
HasFields fields = new HasFields();
Object sawValue;
Class<?> rtype = type;
if (type != int.class) rtype = Object.class;
mh = MethodHandles.convertArguments(mh, mh.type().generic().changeReturnType(rtype));
Object expValue = value;
for (int i = 0; i <= 1; i++) {
if (isStatic) {
if (type == int.class)
sawValue = (int) mh.invokeExact(); // do these exactly
else
sawValue = mh.invokeExact();
} else {
if (type == int.class)
sawValue = (int) mh.invokeExact((Object) fields);
else
sawValue = mh.invokeExact((Object) fields);
}
assertEquals(sawValue, expValue);
Object random = randomArg(type);
f.set(fields, random);
expValue = random;
Class<?> vtype = ftype;
if (ftype != int.class) vtype = Object.class;
if (isGetter) {
mh = MethodHandles.convertArguments(mh, mh.type().generic()
.changeReturnType(vtype));
} else {
int last = mh.type().parameterCount() - 1;
mh = MethodHandles.convertArguments(mh, mh.type().generic()
.changeReturnType(void.class)
.changeParameterType(last, vtype));
}
if (f != null && f.getDeclaringClass() == HasFields.class) {
assertEquals(f.get(fields), value); // clean to start with
}
if (isGetter) {
Object expValue = value;
for (int i = 0; i <= 1; i++) {
if (isStatic) {
if (ftype == int.class)
sawValue = (int) mh.invokeExact(); // do these exactly
else
sawValue = mh.invokeExact();
} else {
if (ftype == int.class)
sawValue = (int) mh.invokeExact((Object) fields);
else
sawValue = mh.invokeExact((Object) fields);
}
assertEquals(sawValue, expValue);
if (f != null && f.getDeclaringClass() == HasFields.class
&& !Modifier.isFinal(f.getModifiers())) {
Object random = randomArg(ftype);
f.set(fields, random);
expValue = random;
} else {
break;
}
}
} else {
for (int i = 0; i <= 1; i++) {
Object putValue = randomArg(ftype);
if (isStatic) {
if (ftype == int.class)
mh.invokeExact((int)putValue); // do these exactly
else
mh.invokeExact(putValue);
} else {
if (ftype == int.class)
mh.invokeExact((Object) fields, (int)putValue);
else
mh.invokeExact((Object) fields, putValue);
}
if (f != null && f.getDeclaringClass() == HasFields.class) {
assertEquals(f.get(fields), putValue);
}
}
}
if (f != null && f.getDeclaringClass() == HasFields.class) {
f.set(fields, value); // put it back
}
f.set(fields, value); // put it back
}
@ -922,61 +1086,24 @@ public class MethodHandlesTest {
@Test
public void testFindStaticSetter() throws Throwable {
startTest("findStaticSetter");
testSetter(TEST_FIND_STATIC_FIELD);
testSetter(TEST_FIND_STATIC);
}
public void testSetter(int testMode) throws Throwable {
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
startTest("unreflectSetter");
for (Object[] c : HasFields.CASES) {
Field f = (Field)c[0];
Object value = c[1];
Class<?> type = f.getType();
testSetter(lookup, f, type, value, testMode);
boolean positive = (c[1] != Error.class);
testSetter(positive, lookup, c[0], c[1], testMode);
}
for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
testSetter(false, lookup,
new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
null, testMode);
}
}
public void testSetter(MethodHandles.Lookup lookup,
Field f, Class<?> type, Object value, int testMode) throws Throwable {
boolean isStatic = Modifier.isStatic(f.getModifiers());
Class<?> fclass = f.getDeclaringClass();
String fname = f.getName();
Class<?> ftype = f.getType();
if (!testModeMatches(testMode, isStatic)) return;
countTest(true);
MethodType expType = MethodType.methodType(void.class, HasFields.class, type);
if (isStatic) expType = expType.dropParameterTypes(0, 1);
MethodHandle mh;
if (testMode == TEST_UNREFLECT)
mh = lookup.unreflectSetter(f);
else if (testMode == TEST_FIND_FIELD)
mh = lookup.findSetter(fclass, fname, ftype);
else if (testMode == TEST_FIND_STATIC_FIELD)
mh = lookup.findStaticSetter(fclass, fname, ftype);
else throw new InternalError();
assertSame(mh.type(), expType);
assertNameStringContains(mh, fname);
HasFields fields = new HasFields();
Object sawValue;
Class<?> vtype = type;
if (type != int.class) vtype = Object.class;
int last = mh.type().parameterCount() - 1;
mh = MethodHandles.convertArguments(mh, mh.type().generic().changeReturnType(void.class).changeParameterType(last, vtype));
assertEquals(f.get(fields), value); // clean to start with
for (int i = 0; i <= 1; i++) {
Object putValue = randomArg(type);
if (isStatic) {
if (type == int.class)
mh.invokeExact((int)putValue); // do these exactly
else
mh.invokeExact(putValue);
} else {
if (type == int.class)
mh.invokeExact((Object) fields, (int)putValue);
else
mh.invokeExact((Object) fields, putValue);
}
assertEquals(f.get(fields), putValue);
}
f.set(fields, value); // put it back
public void testSetter(boolean positive, MethodHandles.Lookup lookup,
Object fieldRef, Object value, int testMode) throws Throwable {
testAccessor(positive, lookup, fieldRef, value, testMode | TEST_SETTER);
}
@Test
@ -1108,18 +1235,6 @@ public class MethodHandlesTest {
}
}
static MethodHandle typeHandler2(MethodHandle target, MethodType newType) {
MethodType oldType = target.type();
int oldArity = oldType.parameterCount();
int newArity = newType.parameterCount();
if (newArity < oldArity)
return MethodHandles.insertArguments(target, oldArity, "OPTIONAL");
else if (newArity > oldArity)
return MethodHandles.dropArguments(target, oldArity-1, newType.parameterType(oldArity-1));
else
return target; // attempt no further conversions
}
@Test
public void testConvertArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
@ -1137,23 +1252,6 @@ public class MethodHandlesTest {
testConvert(true, true, id, rtype, name, params);
}
@Test
public void testTypeHandler() throws Throwable {
MethodHandle id = Callee.ofType(1);
MethodHandle th2 = PRIVATE.findStatic(MethodHandlesTest.class, "typeHandler2",
MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
MethodHandle id2 = id.withTypeHandler(th2);
testConvert(true, false, id2, null, "id", Object.class);
testConvert(true, true, id2, null, "id", Object.class);
if (true) return; //FIXME
testConvert(true, false, id2, null, "id", String.class); // FIXME: throws WMT
testConvert(false, true, id2, null, "id", String.class); // FIXME: should not succeed
testConvert(false, false, id2, null, "id", Object.class, String.class); //FIXME: array[1] line 1164
testConvert(true, true, id2, null, "id", Object.class, String.class);
testConvert(false, false, id2, null, "id");
testConvert(true, true, id2, null, "id");
}
void testConvert(boolean positive, boolean useAsType,
MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
countTest(positive);
@ -1183,9 +1281,9 @@ public class MethodHandlesTest {
RuntimeException error = null;
try {
if (useAsType)
target = MethodHandles.convertArguments(id, newType);
else
target = id.asType(newType);
else
target = MethodHandles.convertArguments(id, newType);
} catch (RuntimeException ex) {
error = ex;
}
@ -1204,6 +1302,20 @@ public class MethodHandlesTest {
System.out.print(':');
}
@Test
public void testVarargsCollector() throws Throwable {
MethodHandle vac0 = PRIVATE.findStatic(MethodHandlesTest.class, "called",
MethodType.methodType(Object.class, String.class, Object[].class));
vac0 = vac0.bindTo("vac");
MethodHandle vac = vac0.asVarargsCollector(Object[].class);
testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
for (Class<?> at : new Class[] { Object.class, String.class, Integer.class }) {
testConvert(true, true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
testConvert(true, true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
}
}
@Test
public void testPermuteArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
@ -1300,7 +1412,7 @@ public class MethodHandlesTest {
types[i] = args[i].getClass();
}
int inargs = args.length, outargs = reorder.length;
assert(inargs == types.length);
assertTrue(inargs == types.length);
if (verbosity >= 3)
System.out.println("permuteArguments "+Arrays.toString(reorder));
Object[] permArgs = new Object[outargs];
@ -1317,7 +1429,7 @@ public class MethodHandlesTest {
}
MethodType inType = MethodType.methodType(Object.class, types);
MethodType outType = MethodType.methodType(Object.class, permTypes);
MethodHandle target = MethodHandles.convertArguments(ValueConversions.varargsList(outargs), outType);
MethodHandle target = MethodHandles.convertArguments(varargsList(outargs), outType);
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
Object result = newTarget.invokeWithArguments(args);
Object expected = Arrays.asList(permArgs);
@ -1344,7 +1456,7 @@ public class MethodHandlesTest {
}
public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
countTest();
MethodHandle target = ValueConversions.varargsArray(nargs);
MethodHandle target = varargsArray(nargs);
MethodHandle target2 = changeArgTypes(target, argType);
if (verbosity >= 3)
System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
@ -1374,7 +1486,7 @@ public class MethodHandlesTest {
spreadParams.clear(); spreadParams.add(Object[].class);
}
MethodType newType = MethodType.methodType(Object.class, newParams);
MethodHandle result = MethodHandles.spreadArguments(target2, newType);
MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType);
Object[] returnValue;
if (pos == 0) {
// In the following line, the first cast implies
@ -1408,7 +1520,7 @@ public class MethodHandlesTest {
public void testCollectArguments(Class<?> argType, int pos, int nargs) throws Throwable {
countTest();
// fake up a MH with the same type as the desired adapter:
MethodHandle fake = ValueConversions.varargsArray(nargs);
MethodHandle fake = varargsArray(nargs);
fake = changeArgTypes(fake, argType);
MethodType newType = fake.type();
Object[] args = randomArgs(newType.parameterArray());
@ -1416,12 +1528,12 @@ public class MethodHandlesTest {
Object[] collectedArgs = Arrays.copyOfRange(args, 0, pos+1);
collectedArgs[pos] = Arrays.copyOfRange(args, pos, args.length);
// here is the MH which will witness the collected argument tail:
MethodHandle target = ValueConversions.varargsArray(pos+1);
MethodHandle target = varargsArray(pos+1);
target = changeArgTypes(target, 0, pos, argType);
target = changeArgTypes(target, pos, pos+1, Object[].class);
if (verbosity >= 3)
System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]");
MethodHandle result = MethodHandles.collectArguments(target, newType);
MethodHandle result = target.asCollector(Object[].class, nargs-pos).asType(newType);
Object[] returnValue = (Object[]) result.invokeWithArguments(args);
// assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]);
// returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]);
@ -1445,7 +1557,7 @@ public class MethodHandlesTest {
void testInsertArguments(int nargs, int pos, int ins) throws Throwable {
countTest();
MethodHandle target = ValueConversions.varargsArray(nargs + ins);
MethodHandle target = varargsArray(nargs + ins);
Object[] args = randomArgs(target.type().parameterArray());
List<Object> resList = Arrays.asList(args);
List<Object> argsToPass = new ArrayList<Object>(resList);
@ -1464,6 +1576,55 @@ public class MethodHandlesTest {
assertEquals(resList, res2List);
}
@Test
public void testFilterReturnValue() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("filterReturnValue");
Class<?> classOfVCList = varargsList(1).invokeWithArguments(0).getClass();
assertTrue(List.class.isAssignableFrom(classOfVCList));
for (int nargs = 0; nargs <= 3; nargs++) {
for (Class<?> rtype : new Class[] { Object.class,
List.class,
int.class,
//byte.class, //FIXME: add this
//long.class, //FIXME: add this
CharSequence.class,
String.class }) {
testFilterReturnValue(nargs, rtype);
}
}
}
void testFilterReturnValue(int nargs, Class<?> rtype) throws Throwable {
countTest();
MethodHandle target = varargsList(nargs, rtype);
MethodHandle filter;
if (List.class.isAssignableFrom(rtype) || rtype.isAssignableFrom(List.class))
filter = varargsList(1); // add another layer of list-ness
else
filter = MethodHandles.identity(rtype);
filter = filter.asType(MethodType.methodType(target.type().returnType(), rtype));
Object[] argsToPass = randomArgs(nargs, Object.class);
if (verbosity >= 3)
System.out.println("filter "+target+" to "+rtype.getSimpleName()+" with "+filter);
MethodHandle target2 = MethodHandles.filterReturnValue(target, filter);
if (verbosity >= 4)
System.out.println("filtered target: "+target2);
// Simulate expected effect of filter on return value:
Object unfiltered = target.invokeWithArguments(argsToPass);
Object expected = filter.invokeWithArguments(unfiltered);
if (verbosity >= 4)
System.out.println("unfiltered: "+unfiltered+" : "+unfiltered.getClass().getSimpleName());
if (verbosity >= 4)
System.out.println("expected: "+expected+" : "+expected.getClass().getSimpleName());
Object result = target2.invokeWithArguments(argsToPass);
if (verbosity >= 3)
System.out.println("result: "+result+" : "+result.getClass().getSimpleName());
if (!expected.equals(result))
System.out.println("*** fail at n/rt = "+nargs+"/"+rtype.getSimpleName()+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected);
assertEquals(expected, result);
}
@Test
public void testFilterArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
@ -1477,8 +1638,8 @@ public class MethodHandlesTest {
void testFilterArguments(int nargs, int pos) throws Throwable {
countTest();
MethodHandle target = ValueConversions.varargsList(nargs);
MethodHandle filter = ValueConversions.varargsList(1);
MethodHandle target = varargsList(nargs);
MethodHandle filter = varargsList(1);
filter = MethodHandles.convertArguments(filter, filter.type().generic());
Object[] argsToPass = randomArgs(nargs, Object.class);
if (verbosity >= 3)
@ -1492,7 +1653,7 @@ public class MethodHandlesTest {
if (verbosity >= 3)
System.out.println("result: "+result);
if (!expected.equals(result))
System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+argsToPass+" => "+result);
System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected);
assertEquals(expected, result);
}
@ -1512,8 +1673,8 @@ public class MethodHandlesTest {
void testFoldArguments(int nargs, int pos, int fold) throws Throwable {
if (pos != 0) return; // can fold only at pos=0 for now
countTest();
MethodHandle target = ValueConversions.varargsList(1 + nargs);
MethodHandle combine = ValueConversions.varargsList(fold).asType(MethodType.genericMethodType(fold));
MethodHandle target = varargsList(1 + nargs);
MethodHandle combine = varargsList(fold).asType(MethodType.genericMethodType(fold));
List<Object> argsToPass = Arrays.asList(randomArgs(nargs, Object.class));
if (verbosity >= 3)
System.out.println("fold "+target+" with "+combine);
@ -1529,7 +1690,7 @@ public class MethodHandlesTest {
if (verbosity >= 3)
System.out.println("result: "+result);
if (!expected.equals(result))
System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result);
System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result+" != "+expected);
assertEquals(expected, result);
}
@ -1548,7 +1709,7 @@ public class MethodHandlesTest {
void testDropArguments(int nargs, int pos, int drop) throws Throwable {
countTest();
MethodHandle target = ValueConversions.varargsArray(nargs);
MethodHandle target = varargsArray(nargs);
Object[] args = randomArgs(target.type().parameterArray());
MethodHandle target2 = MethodHandles.dropArguments(target, pos,
Collections.nCopies(drop, Object.class).toArray(new Class[0]));
@ -1599,7 +1760,8 @@ public class MethodHandlesTest {
boolean testRetCode = type.returnType() != void.class;
MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "invokee",
MethodType.genericMethodType(0, true));
target = MethodHandles.collectArguments(target, type);
assertTrue(target.isVarargsCollector());
target = target.asType(type);
Object[] args = randomArgs(type.parameterArray());
List<Object> targetPlusArgs = new ArrayList<Object>(Arrays.asList(args));
targetPlusArgs.add(0, target);
@ -1644,14 +1806,14 @@ public class MethodHandlesTest {
assertCalled("invokee", args);
// varargs invoker #0
calledLog.clear();
inv = MethodHandles.varargsInvoker(type, 0);
inv = MethodHandles.spreadInvoker(type, 0);
result = inv.invokeExact(target, args);
if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args);
if (nargs >= 1) {
// varargs invoker #1
calledLog.clear();
inv = MethodHandles.varargsInvoker(type, 1);
inv = MethodHandles.spreadInvoker(type, 1);
result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs));
if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args);
@ -1659,7 +1821,7 @@ public class MethodHandlesTest {
if (nargs >= 2) {
// varargs invoker #2
calledLog.clear();
inv = MethodHandles.varargsInvoker(type, 2);
inv = MethodHandles.spreadInvoker(type, 2);
result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs));
if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args);
@ -1667,7 +1829,7 @@ public class MethodHandlesTest {
if (nargs >= 3) {
// varargs invoker #3
calledLog.clear();
inv = MethodHandles.varargsInvoker(type, 3);
inv = MethodHandles.spreadInvoker(type, 3);
result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs));
if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args);
@ -1676,7 +1838,7 @@ public class MethodHandlesTest {
// varargs invoker #0..N
countTest();
calledLog.clear();
inv = MethodHandles.varargsInvoker(type, k);
inv = MethodHandles.spreadInvoker(type, k);
List<Object> targetPlusVarArgs = new ArrayList<Object>(targetPlusArgs);
List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
Object[] tail = tailList.toArray();
@ -1823,7 +1985,7 @@ public class MethodHandlesTest {
MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2));
while (thrower.type().parameterCount() < nargs)
thrower = MethodHandles.dropArguments(thrower, thrower.type().parameterCount(), Object.class);
MethodHandle catcher = ValueConversions.varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs));
MethodHandle catcher = varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs));
MethodHandle target = MethodHandles.catchException(thrower,
thrown.getClass(), catcher);
assertEquals(thrower.type(), target.type());
@ -2089,25 +2251,12 @@ public class MethodHandlesTest {
}
}
// Test error checking:
MethodHandle genericMH = ValueConversions.varargsArray(0);
genericMH = MethodHandles.convertArguments(genericMH, genericMH.type().generic());
for (Class<?> sam : new Class[] { Runnable.class,
Fooable.class,
Iterable.class }) {
try {
// Must throw, because none of these guys has generic type.
MethodHandles.asInstance(genericMH, sam);
System.out.println("Failed to throw");
assertTrue(false);
} catch (IllegalArgumentException ex) {
}
}
for (Class<?> nonSAM : new Class[] { Object.class,
String.class,
CharSequence.class,
Example.class }) {
try {
MethodHandles.asInstance(ValueConversions.varargsArray(0), nonSAM);
MethodHandles.asInstance(varargsArray(0), nonSAM);
System.out.println("Failed to throw");
assertTrue(false);
} catch (IllegalArgumentException ex) {
@ -2159,12 +2308,13 @@ class ValueConversions {
MethodHandle array = null;
try {
array = lookup.findStatic(ValueConversions.class, name, type);
} catch (NoAccessException ex) {
} catch (ReflectiveOperationException ex) {
// break from loop!
}
if (array == null) break;
arrays.add(array);
}
assert(arrays.size() == 11); // current number of methods
assertTrue(arrays.size() == 11); // current number of methods
return arrays.toArray(new MethodHandle[0]);
}
static final MethodHandle[] ARRAYS = makeArrays();
@ -2211,22 +2361,23 @@ class ValueConversions {
Object a8, Object a9)
{ return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
static MethodHandle[] makeLists() {
ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>();
ArrayList<MethodHandle> lists = new ArrayList<MethodHandle>();
MethodHandles.Lookup lookup = IMPL_LOOKUP;
for (;;) {
int nargs = arrays.size();
int nargs = lists.size();
MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class);
String name = "list";
MethodHandle array = null;
MethodHandle list = null;
try {
array = lookup.findStatic(ValueConversions.class, name, type);
} catch (NoAccessException ex) {
list = lookup.findStatic(ValueConversions.class, name, type);
} catch (ReflectiveOperationException ex) {
// break from loop!
}
if (array == null) break;
arrays.add(array);
if (list == null) break;
lists.add(list);
}
assert(arrays.size() == 11); // current number of methods
return arrays.toArray(new MethodHandle[0]);
assertTrue(lists.size() == 11); // current number of methods
return lists.toArray(new MethodHandle[0]);
}
static final MethodHandle[] LISTS = makeLists();

View File

@ -0,0 +1,542 @@
/*
* Copyright 2008, 2011 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/* @test
* @summary unit tests for java.dyn.MethodType
* @compile MethodTypeTest.java
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.MethodTypeTest
*/
package test.java.dyn;
import sun.dyn.MemberName;
import java.dyn.MethodType;
import java.lang.reflect.Method;
import java.util.*;
import org.junit.*;
import static org.junit.Assert.*;
/**
*
* @author jrose
*/
public class MethodTypeTest {
private Class<?> rtype;
private Class<?>[] ptypes;
private MethodType mt_viS, mt_OO, mt_OO2, mt_vv, mt_Vv, mt_Ov;
private MethodType mt_iSI, mt_ISi, mt_ISI, mt_iSi;
private MethodType mt_viO, mt_iO2, mt_OOi, mt_iOi;
private MethodType mt_VIO, mt_IO2, mt_OOI, mt_IOI, mt_VIS;
private MethodType mt_vOiSzA, mt_OO99;
private MethodType[] GALLERY;
private Method compareTo;
@Before
public void setUp() throws Exception {
rtype = void.class;
ptypes = new Class<?>[] { int.class, String.class };
mt_viS = MethodType.methodType(void.class, int.class, String.class);
mt_OO = MethodType.methodType(Object.class, Object.class);
mt_OO2 = MethodType.methodType(Object.class, Object.class, Object.class);
mt_vv = MethodType.methodType(void.class);
mt_Vv = MethodType.methodType(Void.class);
mt_Ov = MethodType.methodType(Object.class);
mt_iSI = MethodType.methodType(int.class, String.class, Integer.class);
mt_ISi = MethodType.methodType(Integer.class, String.class, int.class);
mt_ISI = MethodType.methodType(Integer.class, String.class, Integer.class);
mt_iSi = MethodType.methodType(int.class, String.class, int.class);
compareTo = String.class.getDeclaredMethod("compareTo", String.class);
mt_viO = MethodType.methodType(void.class, int.class, Object.class);
mt_iO2 = MethodType.methodType(int.class, Object.class, Object.class);
mt_OOi = MethodType.methodType(Object.class, Object.class, int.class);
mt_iOi = MethodType.methodType(int.class, Object.class, int.class);
mt_VIO = MethodType.methodType(Void.class, Integer.class, Object.class);
mt_IO2 = MethodType.methodType(Integer.class, Object.class, Object.class);
mt_OOI = MethodType.methodType(Object.class, Object.class, Integer.class);
mt_IOI = MethodType.methodType(Integer.class, Object.class, Integer.class);
mt_VIS = MethodType.methodType(Void.class, Integer.class, String.class);
mt_vOiSzA = MethodType.methodType(void.class, Object.class, int.class, String.class, boolean.class, Object[].class);
mt_OO99 = MethodType.genericMethodType(99);
GALLERY = new MethodType[] {
mt_viS, mt_OO, mt_OO2, mt_vv, mt_Vv, mt_Ov,
mt_iSI, mt_ISi, mt_ISI, mt_iSi,
mt_viO, mt_iO2, mt_OOi, mt_iOi,
mt_VIO, mt_IO2, mt_OOI, mt_IOI,
mt_VIS, mt_vOiSzA, mt_OO99
};
}
@After
public void tearDown() throws Exception {
}
/** Make sure the method types are all distinct. */
@Test
public void testDistinct() {
List<MethodType> gallery2 = new ArrayList<>();
for (MethodType mt : GALLERY) {
assertFalse(mt.toString(), gallery2.contains(mt));
gallery2.add(mt);
}
// check self-equality also:
assertEquals(Arrays.asList(GALLERY), gallery2);
}
/**
* Test of make method, of class MethodType.
*/
@Test
public void testMake_Class_ClassArr() {
System.out.println("make (from type array)");
MethodType result = MethodType.methodType(rtype, ptypes);
assertSame(mt_viS, result);
}
/**
* Test of make method, of class MethodType.
*/
@Test
public void testMake_Class_List() {
System.out.println("make (from type list)");
MethodType result = MethodType.methodType(rtype, Arrays.asList(ptypes));
assertSame(mt_viS, result);
}
/**
* Test of make method, of class MethodType.
*/
@Test
public void testMake_3args() {
System.out.println("make (from type with varargs)");
MethodType result = MethodType.methodType(rtype, ptypes[0], ptypes[1]);
assertSame(mt_viS, result);
}
/**
* Test of make method, of class MethodType.
*/
@Test
public void testMake_Class() {
System.out.println("make (from single type)");
Class<?> rt = Integer.class;
MethodType expResult = MethodType.methodType(rt, new Class<?>[0]);
MethodType result = MethodType.methodType(rt);
assertSame(expResult, result);
}
@Test
public void testMakeGeneric() {
System.out.println("makeGeneric");
int objectArgCount = 2;
MethodType expResult = mt_OO2;
MethodType result = MethodType.genericMethodType(objectArgCount);
assertSame(expResult, result);
}
/**
* Test of make method, of class MethodType.
*/
@Test
public void testMake_Method() {
System.out.println("make (via MemberName.getMethodType)");
MethodType expResult = MethodType.methodType(int.class, String.class);
MemberName name = new MemberName(compareTo);
MethodType result = name.getMethodType();
assertSame(expResult, result);
}
/**
* Test of make method, of class MethodType.
*/
@Test
public void testMake_MethodType() {
System.out.println("make (from rtype, MethodType)");
MethodType expResult = mt_iO2;
MethodType result = MethodType.methodType(int.class, mt_IO2);
assertSame(expResult, result);
}
/**
* Test of make method, of class MethodType.
*/
@Test
public void testMake_String_ClassLoader() {
System.out.println("make (from bytecode signature)");
ClassLoader loader = null;
MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
String obj = "Ljava/lang/Object;";
assertEquals(obj, concat(Object.class));
String[] expResults = {
"(ILjava/lang/String;)V",
concat("(", obj, 2, ")", Object.class),
"()V", "()"+obj,
concat("(", String.class, Integer.class, ")I"),
concat("(", String.class, "I)", Integer.class),
concat("(", String.class, Integer.class, ")", Integer.class),
concat("(", String.class, "I)I")
};
for (int i = 0; i < instances.length; i++) {
MethodType instance = instances[i];
String result = instance.toMethodDescriptorString();
assertEquals("#"+i, expResults[i], result);
MethodType parsed = MethodType.fromMethodDescriptorString(result, loader);
assertSame("--#"+i, instance, parsed);
}
}
private static String concat(Object... parts) {
StringBuilder sb = new StringBuilder();
Object prevPart = "";
for (Object part : parts) {
if (part instanceof Class) {
part = "L"+((Class)part).getName()+";";
}
if (part instanceof Integer) {
for (int n = (Integer) part; n > 1; n--)
sb.append(prevPart);
part = "";
}
sb.append(part);
prevPart = part;
}
return sb.toString().replace('.', '/');
}
@Test
public void testHasPrimitives() {
System.out.println("hasPrimitives");
MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
boolean[] expResults = {true, false, true, false, true, true, false, true};
for (int i = 0; i < instances.length; i++) {
boolean result = instances[i].hasPrimitives();
assertEquals("#"+i, expResults[i], result);
}
}
@Test
public void testHasWrappers() {
System.out.println("hasWrappers");
MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
boolean[] expResults = {false, false, false, false, true, true, true, false};
for (int i = 0; i < instances.length; i++) {
System.out.println(" hasWrappers "+instances[i]);
boolean result = instances[i].hasWrappers();
assertEquals("#"+i, expResults[i], result);
}
}
@Test
public void testErase() {
System.out.println("erase");
MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
MethodType[] expResults = {mt_viO, mt_OO2, mt_vv, mt_Ov, mt_iO2, mt_OOi, mt_OO2, mt_iOi};
for (int i = 0; i < instances.length; i++) {
MethodType result = instances[i].erase();
assertSame("#"+i, expResults[i], result);
}
}
@Test
public void testGeneric() {
System.out.println("generic");
MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
MethodType[] expResults = {mt_OO2, mt_OO2, mt_Ov, mt_Ov, mt_OO2, mt_OO2, mt_OO2, mt_OO2};
for (int i = 0; i < instances.length; i++) {
MethodType result = instances[i].generic();
assertSame("#"+i, expResults[i], result);
}
}
@Test
public void testWrap() {
System.out.println("wrap");
MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
MethodType[] expResults = {mt_VIS, mt_OO2, mt_Vv, mt_Ov, mt_ISI, mt_ISI, mt_ISI, mt_ISI};
for (int i = 0; i < instances.length; i++) {
MethodType result = instances[i].wrap();
assertSame("#"+i, expResults[i], result);
}
}
@Test
public void testUnwrap() {
System.out.println("unwrap");
MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
MethodType[] expResults = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSi, mt_iSi, mt_iSi, mt_iSi};
for (int i = 0; i < instances.length; i++) {
MethodType result = instances[i].unwrap();
assertSame("#"+i, expResults[i], result);
}
}
/**
* Test of parameterType method, of class MethodType.
*/
@Test
public void testParameterType() {
System.out.println("parameterType");
for (int num = 0; num < ptypes.length; num++) {
MethodType instance = mt_viS;
Class<?> expResult = ptypes[num];
Class<?> result = instance.parameterType(num);
assertSame(expResult, result);
}
}
/**
* Test of parameterCount method, of class MethodType.
*/
@Test
public void testParameterCount() {
System.out.println("parameterCount");
MethodType instance = mt_viS;
int expResult = 2;
int result = instance.parameterCount();
assertEquals(expResult, result);
}
/**
* Test of returnType method, of class MethodType.
*/
@Test
public void testReturnType() {
System.out.println("returnType");
MethodType instance = mt_viS;
Class<?> expResult = void.class;
Class<?> result = instance.returnType();
assertSame(expResult, result);
}
/**
* Test of parameterList method, of class MethodType.
*/
@Test
public void testParameterList() {
System.out.println("parameterList");
MethodType instance = mt_viS;
List<Class<?>> expResult = Arrays.asList(ptypes);
List<Class<?>> result = instance.parameterList();
assertEquals(expResult, result);
}
/**
* Test of parameterArray method, of class MethodType.
*/
@Test
public void testParameterArray() {
System.out.println("parameterArray");
MethodType instance = mt_viS;
Class<?>[] expResult = ptypes;
Class<?>[] result = instance.parameterArray();
assertEquals(Arrays.asList(expResult), Arrays.asList(result));
}
/**
* Test of equals method, of class MethodType.
*/
@Test
public void testEquals_Object() {
System.out.println("equals");
Object x = null;
MethodType instance = mt_viS;
boolean expResult = false;
boolean result = instance.equals(x);
assertEquals(expResult, result);
}
/**
* Test of equals method, of class MethodType.
*/
@Test
public void testEquals_MethodType() {
System.out.println("equals");
MethodType that = mt_viS;
MethodType instance = mt_viS;
boolean expResult = true;
boolean result = instance.equals(that);
assertEquals(expResult, result);
}
/**
* Test of hashCode method, of class MethodType.
*/
@Test
public void testHashCode() {
System.out.println("hashCode");
MethodType instance = mt_viS;
ArrayList<Class<?>> types = new ArrayList<Class<?>>();
types.add(instance.returnType());
types.addAll(instance.parameterList());
int expResult = types.hashCode();
int result = instance.hashCode();
assertEquals(expResult, result);
}
/**
* Test of toString method, of class MethodType.
*/
@Test
public void testToString() {
System.out.println("toString");
MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
//String expResult = "void[int, class java.lang.String]";
String[] expResults = {
"(int,String)void",
"(Object,Object)Object",
"()void",
"()Object",
"(String,Integer)int",
"(String,int)Integer",
"(String,Integer)Integer",
"(String,int)int"
};
for (int i = 0; i < instances.length; i++) {
MethodType instance = instances[i];
String result = instance.toString();
System.out.println("#"+i+":"+result);
assertEquals("#"+i, expResults[i], result);
}
}
private static byte[] writeSerial(Object x) throws java.io.IOException {
try (java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
java.io.ObjectOutputStream out = new java.io.ObjectOutputStream(bout)
) {
out.writeObject(x);
out.flush();
return bout.toByteArray();
}
}
private static Object readSerial(byte[] wire) throws java.io.IOException, ClassNotFoundException {
try (java.io.ByteArrayInputStream bin = new java.io.ByteArrayInputStream(wire);
java.io.ObjectInputStream in = new java.io.ObjectInputStream(bin)) {
return in.readObject();
}
}
private static void testSerializedEquality(Object x) throws java.io.IOException, ClassNotFoundException {
if (x instanceof Object[])
x = Arrays.asList((Object[]) x); // has proper equals method
byte[] wire = writeSerial(x);
Object y = readSerial(wire);
assertEquals(x, y);
}
/** Test (de-)serialization. */
@Test
public void testSerialization() throws Throwable {
System.out.println("serialization");
for (MethodType mt : GALLERY) {
testSerializedEquality(mt);
}
testSerializedEquality(GALLERY);
// Make a list of mixed objects:
List<Object> stuff = new ArrayList<>();
Collections.addAll(stuff, GALLERY); // copy #1
Object[] triples = Arrays.copyOfRange(GALLERY, 0, GALLERY.length/2);
Collections.addAll(stuff, triples); // copy #3 (partial)
for (MethodType mt : GALLERY) {
Collections.addAll(stuff, mt.parameterArray());
}
Collections.shuffle(stuff, new Random(292));
Collections.addAll(stuff, GALLERY); // copy #2
testSerializedEquality(stuff);
}
/** Test serialization formats. */
@Test
public void testPortableSerialFormat() throws Throwable {
System.out.println("portable serial format");
Object[][] cases = {
{ mt_vv, new byte[] { // ()void
(byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x13,
(byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x64, (byte)0x79, (byte)0x6e,
(byte)0x2e, (byte)0x4d, (byte)0x65, (byte)0x74, (byte)0x68, (byte)0x6f, (byte)0x64, (byte)0x54,
(byte)0x79, (byte)0x70, (byte)0x65, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x01, (byte)0x24, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
(byte)0x76, (byte)0x72, (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x6f, (byte)0x69, (byte)0x64,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00,
(byte)0x12, (byte)0x5b, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e,
(byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x43, (byte)0x6c, (byte)0x61,
(byte)0x73, (byte)0x73, (byte)0x3b, (byte)0xab, (byte)0x16, (byte)0xd7, (byte)0xae, (byte)0xcb,
(byte)0xcd, (byte)0x5a, (byte)0x99, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78,
} },
{ mt_OO, new byte[] { // (Object)Object
(byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x13,
(byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x64, (byte)0x79, (byte)0x6e,
(byte)0x2e, (byte)0x4d, (byte)0x65, (byte)0x74, (byte)0x68, (byte)0x6f, (byte)0x64, (byte)0x54,
(byte)0x79, (byte)0x70, (byte)0x65, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x01, (byte)0x24, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
(byte)0x76, (byte)0x72, (byte)0x00, (byte)0x10, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61,
(byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x4f, (byte)0x62,
(byte)0x6a, (byte)0x65, (byte)0x63, (byte)0x74, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78,
(byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00, (byte)0x12, (byte)0x5b, (byte)0x4c, (byte)0x6a,
(byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67,
(byte)0x2e, (byte)0x43, (byte)0x6c, (byte)0x61, (byte)0x73, (byte)0x73, (byte)0x3b, (byte)0xab,
(byte)0x16, (byte)0xd7, (byte)0xae, (byte)0xcb, (byte)0xcd, (byte)0x5a, (byte)0x99, (byte)0x02,
(byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
(byte)0x71, (byte)0x00, (byte)0x7e, (byte)0x00, (byte)0x03, (byte)0x78,
} },
};
boolean generateData = false;
//generateData = true;
for (Object[] c : cases) {
MethodType mt = (MethodType) c[0];
System.out.println("deserialize "+mt);
byte[] wire = (byte[]) c[1];
if (generateData) {
wire = writeSerial(mt);
final String INDENT = " ";
System.out.print("{ // "+mt);
for (int i = 0; i < wire.length; i++) {
if (i % 8 == 0) { System.out.println(); System.out.print(INDENT+" "); }
String hex = Integer.toHexString(wire[i] & 0xFF);
if (hex.length() == 1) hex = "0"+hex;
System.out.print(" (byte)0x"+hex+",");
}
System.out.println();
System.out.println(INDENT+"}");
System.out.flush();
}
Object decode;
try {
decode = readSerial(wire);
} catch (Exception ex) {
decode = ex; // oops!
}
assertEquals(mt, decode);
}
}
}

View File

@ -98,8 +98,9 @@ $ $JAVA_HOME/bin/java -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic
(same output as above)
* </pre></blockquote>
* <p>
* Until the format of {@code CONSTANT_InvokeDynamic} entries is finalized,
* the {@code --transitionalJSR292} switch is recommended (and turned on by default).
* Before OpenJDK build b123, the format of {@code CONSTANT_InvokeDynamic} is in transition,
* and the switch {@code --transitionalJSR292=yes} is recommended.
* It is turned <em>off</em> by default, but users of earlier builds may need to turn it on.
* <p>
* A version of this transformation built on top of <a href="http://asm.ow2.org/">http://asm.ow2.org/</a> would be welcome.
* @author John Rose
@ -116,7 +117,7 @@ public class Indify {
public boolean overwrite = false;
public boolean quiet = false;
public boolean verbose = false;
public boolean transitionalJSR292 = true; // default to false later
public boolean transitionalJSR292 = false; // final version is distributed
public boolean all = false;
public int verifySpecifierCount = -1;
@ -158,6 +159,7 @@ public class Indify {
av = avl.toArray(new String[0]);
Class<?> mainClass = Class.forName(mainClassName, true, makeClassLoader());
java.lang.reflect.Method main = mainClass.getMethod("main", String[].class);
try { main.setAccessible(true); } catch (SecurityException ex) { }
main.invoke(null, (Object) av);
}
@ -223,8 +225,8 @@ public class Indify {
private boolean booleanOption(String s) {
if (s == null) return true;
switch (s) {
case "true": case "yes": case "1": return true;
case "false": case "no": case "0": return false;
case "true": case "yes": case "on": case "1": return true;
case "false": case "no": case "off": case "0": return false;
}
throw new IllegalArgumentException("unrecognized boolean flag="+s);
}
@ -284,7 +286,7 @@ public class Indify {
}
File classPathFile(File pathDir, String className) {
String qualname = className+".class";
String qualname = className.replace('.','/')+".class";
qualname = qualname.replace('/', File.separatorChar);
return new File(pathDir, qualname);
}
@ -339,6 +341,7 @@ public class Indify {
private File findClassInPath(String name) {
for (String s : classpath) {
File f = classPathFile(new File(s), name);
//System.out.println("Checking for "+f);
if (f.exists() && f.canRead()) {
return f;
}
@ -353,11 +356,11 @@ public class Indify {
}
}
private Class<?> transformAndLoadClass(File f) throws ClassNotFoundException, IOException {
if (verbose) System.out.println("Loading class from "+f);
if (verbose) System.err.println("Loading class from "+f);
ClassFile cf = new ClassFile(f);
Logic logic = new Logic(cf);
boolean changed = logic.transform();
if (verbose && !changed) System.out.println("(no change)");
if (verbose && !changed) System.err.println("(no change)");
logic.reportPatternMethods(!verbose, keepgoing);
byte[] bytes = cf.toByteArray();
return defineClass(null, bytes, 0, bytes.length);
@ -525,6 +528,7 @@ public class Indify {
throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount);
}
}
if (!quiet) System.err.flush();
}
// mark constant pool entries according to participation in patterns
@ -696,6 +700,18 @@ public class Indify {
args.clear();
break;
case opc_new:
{
String type = pool.getString(CONSTANT_Class, (short)i.u2At(1));
//System.out.println("new "+type);
switch (type) {
case "java/lang/StringBuilder":
jvm.push("StringBuilder");
continue decode; // go to next instruction
}
break decode; // bail out
}
case opc_getstatic:
{
// int.class compiles to getstatic Integer.TYPE
@ -732,8 +748,9 @@ public class Indify {
case opc_invokestatic:
case opc_invokevirtual:
case opc_invokespecial:
{
boolean hasRecv = (bc == opc_invokevirtual);
boolean hasRecv = (bc != opc_invokestatic);
int methi = i.u2At(1);
char mark = poolMarks[methi];
Short[] ref = pool.getMemberRef((short)methi);
@ -770,6 +787,7 @@ public class Indify {
if (mark == 'T' || mark == 'H' || mark == 'I') {
ownMethod = findMember(cf.methods, ref[1], ref[2]);
}
//if (intrinsic != null) System.out.println("intrinsic = "+intrinsic);
switch (intrinsic == null ? "" : intrinsic) {
case "fromMethodDescriptorString":
con = makeMethodTypeCon(args.get(0));
@ -860,6 +878,15 @@ public class Indify {
}
}
break decode;
case "StringBuilder.append":
// allow calls like ("value = "+x)
removeEmptyJVMSlots(args);
args.subList(1, args.size()).clear();
continue;
case "StringBuilder.toString":
args.clear();
args.add(intrinsic);
continue;
}
if (!hasRecv && ownMethod != null && patternMark != 0) {
con = constants.get(ownMethod);
@ -1506,6 +1533,7 @@ public class Indify {
out.write(bytes);
} else {
trueSize = flatten(out);
//if (!(item instanceof Code)) System.err.println("wrote complex attr name="+(int)(char)name+" size="+trueSize+" data="+Arrays.toString(flatten()));
}
if (trueSize != size && size >= 0)
System.err.println("warning: attribute size changed "+size+" to "+trueSize);
@ -1525,7 +1553,7 @@ public class Indify {
}
public List<Attr> attrs() { return null; } // Code overrides this
public byte[] flatten() {
ByteArrayOutputStream buf = new ByteArrayOutputStream(size);
ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(20, size));
flatten(buf);
return buf.toByteArray();
}
@ -1642,6 +1670,7 @@ public class Indify {
opc_invokestatic = 184,
opc_invokeinterface = 185,
opc_invokedynamic = 186,
opc_new = 187,
opc_anewarray = 189,
opc_checkcast = 192,
opc_ifnull = 198,