Merge
This commit is contained in:
commit
41fcd26eb6
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
195
jdk/src/share/classes/java/dyn/SwitchPoint.java
Normal file
195
jdk/src/share/classes/java/dyn/SwitchPoint.java
Normal 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<MutableCallSite> mcss = new ArrayList<>();
|
||||
for (SwitchPoint spt : spts) mcss.add(spt.mcs);
|
||||
for (MutableCallSite mcs : mcss) mcs.setTarget(K_false);
|
||||
MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0]));
|
||||
}
|
||||
}
|
||||
* </pre></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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>( )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.<init>(A*)void</td><td>(A*)C</td><td>new C; dup; invokespecial C.<init>(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>( )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.<init>(A*)void</td><td>(A*)C</td><td>new C; dup; invokespecial C.<init>(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 “causeless” 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
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
48
jdk/src/share/classes/sun/dyn/WrapperInstance.java
Normal file
48
jdk/src/share/classes/sun/dyn/WrapperInstance.java
Normal 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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 { => 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);
|
||||
{}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
542
jdk/test/java/dyn/MethodTypeTest.java
Normal file
542
jdk/test/java/dyn/MethodTypeTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user