Merge
This commit is contained in:
commit
ae511866fd
jdk
src/share/classes
java/dyn
BootstrapMethod.javaCallSite.javaClassValue.javaConstantCallSite.javaInvokeDynamic.javaInvokeDynamicBootstrapError.javaLinkage.javaLinkagePermission.javaMethodHandle.javaMethodHandleProvider.javaMethodHandles.javaMethodType.javaNoAccessException.javapackage-info.java
sun/dyn
test/java/dyn
82
jdk/src/share/classes/java/dyn/BootstrapMethod.java
Normal file
82
jdk/src/share/classes/java/dyn/BootstrapMethod.java
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation on InvokeDynamic method calls which requests the JVM to use a specific
|
||||
* <a href="package-summary.html#bsm">bootstrap method</a>
|
||||
* to link the call. This annotation is not retained as such in the class file,
|
||||
* but is transformed into a constant-pool entry for the invokedynamic instruction which
|
||||
* specifies the desired bootstrap method.
|
||||
* <p>
|
||||
* If only the <code>value</code> is given, it must name a subclass of {@link CallSite}
|
||||
* with a constructor which accepts a class, string, and method type.
|
||||
* If the <code>value</code> and <code>name</code> are both given, there must be
|
||||
* a static method in the given class of the given name which accepts a class, string,
|
||||
* and method type, and returns a reference coercible to {@link CallSite}.
|
||||
* <p>
|
||||
* This annotation can be placed either on the return type of a single {@link InvokeDynamic}
|
||||
* call (see examples) or else it can be placed on an enclosing class or method, where it
|
||||
* determines a default bootstrap method for any {@link InvokeDynamic} calls which are not
|
||||
* specifically annotated with a bootstrap method.
|
||||
* Every {@link InvokeDynamic} call must be given a bootstrap method.
|
||||
* <p>
|
||||
* Examples:
|
||||
<blockquote><pre>
|
||||
@BootstrapMethod(value=MyLanguageRuntime.class, name="bootstrapDynamic")
|
||||
String x = (String) InvokeDynamic.greet();
|
||||
//BSM => MyLanguageRuntime.bootstrapDynamic(Here.class, "greet", methodType(String.class))
|
||||
@BootstrapMethod(MyCallSite.class)
|
||||
void example() throws Throwable {
|
||||
InvokeDynamic.greet();
|
||||
//BSM => new MyCallSite(Here.class, "greet", methodType(void.class))
|
||||
}
|
||||
</pre></blockquote>
|
||||
* <p>
|
||||
*/
|
||||
@Target({ElementType.TYPE_USE,
|
||||
// For defaulting every indy site within a class or method; cf. @SuppressWarnings:
|
||||
ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface BootstrapMethod {
|
||||
/** The class containing the bootstrap method. */
|
||||
Class<?> value();
|
||||
|
||||
/** The name of the bootstrap method.
|
||||
* If this is the empty string, an instance of the bootstrap class is created,
|
||||
* and a constructor is invoked.
|
||||
* Otherwise, there must be a static method of the required name.
|
||||
*/
|
||||
String name() default ""; // empty string denotes a constructor with 'new'
|
||||
|
||||
/** The argument types of the bootstrap method, as passed out by the JVM.
|
||||
* There is usually no reason to override the default.
|
||||
*/
|
||||
Class<?>[] arguments() default {Class.class, String.class, MethodType.class};
|
||||
}
|
@ -25,56 +25,26 @@
|
||||
|
||||
package java.dyn;
|
||||
|
||||
import sun.dyn.Access;
|
||||
import sun.dyn.MemberName;
|
||||
import sun.dyn.CallSiteImpl;
|
||||
import sun.dyn.*;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A {@code CallSite} reifies an {@code invokedynamic} instruction from bytecode,
|
||||
* and controls its linkage.
|
||||
* Every linked {@code CallSite} object corresponds to a distinct instance
|
||||
* of the {@code invokedynamic} instruction, and vice versa.
|
||||
* A {@code CallSite} is a holder for a variable {@link MethodHandle},
|
||||
* which is called its {@code target}.
|
||||
* Every call to a {@code CallSite} is delegated to the site's current target.
|
||||
* <p>
|
||||
* Every linked {@code CallSite} object has one state variable,
|
||||
* a {@link MethodHandle} reference called the {@code target}.
|
||||
* This reference is never null. Though it can change its value
|
||||
* successive values must always have exactly the {@link MethodType method type}
|
||||
* called for by the bytecodes of the associated {@code invokedynamic} instruction
|
||||
* A call site is initially created in an <em>unlinked</em> state,
|
||||
* which is distinguished by a null target variable.
|
||||
* Before the call site may be invoked (and before certain other
|
||||
* operations are attempted), the call site must be linked to
|
||||
* a non-null target.
|
||||
* <p>
|
||||
* It is the responsibility of each class's
|
||||
* {@link Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method}
|
||||
* to produce call sites which have been pre-linked to an initial target method.
|
||||
* The required {@link MethodType type} for the target method is a parameter
|
||||
* to each bootstrap method call.
|
||||
* <p>
|
||||
* The bootstrap method may elect to produce call sites of a
|
||||
* language-specific subclass of {@code CallSite}. In such a case,
|
||||
* the subclass may claim responsibility for initializing its target to
|
||||
* a non-null value, by overriding {@link #initialTarget}.
|
||||
* <p>
|
||||
* An {@code invokedynamic} instruction which has not yet been executed
|
||||
* is said to be <em>unlinked</em>. When an unlinked call site is executed,
|
||||
* the containing class's bootstrap method is called to manufacture a call site,
|
||||
* for the instruction. If the bootstrap method does not assign a non-null
|
||||
* value to the new call site's target variable, the method {@link #initialTarget}
|
||||
* is called to produce the new call site's first target method.
|
||||
* <p>
|
||||
* A freshly-created {@code CallSite} object is not yet in a linked state.
|
||||
* An unlinked {@code CallSite} object reports null for its {@code callerClass}.
|
||||
* When the JVM receives a {@code CallSite} object from a bootstrap method,
|
||||
* it first ensures that its target is non-null and of the correct type.
|
||||
* The JVM then links the {@code CallSite} object to the call site instruction,
|
||||
* enabling the {@code callerClass} to return the class in which the instruction occurs.
|
||||
* <p>
|
||||
* Next, the JVM links the instruction to the {@code CallSite}, at which point
|
||||
* any further execution of the {@code invokedynamic} instruction implicitly
|
||||
* invokes the current target of the {@code CallSite} object.
|
||||
* After this two-way linkage, both the instruction and the {@code CallSite}
|
||||
* object are said to be linked.
|
||||
* <p>
|
||||
* This state of linkage continues until the method containing the
|
||||
* dynamic call site is garbage collected, or the dynamic call site
|
||||
* is invalidated by an explicit request.
|
||||
* A call site may be <em>relinked</em> by changing its target.
|
||||
* The new target must be non-null and must have the same
|
||||
* {@linkplain MethodHandle#type() type}
|
||||
* as the previous target.
|
||||
* Thus, though a call site can be relinked to a series of
|
||||
* successive targets, it cannot change its type.
|
||||
* <p>
|
||||
* Linkage happens once in the lifetime of any given {@code CallSite} object.
|
||||
* Because of call site invalidation, this linkage can be repeated for
|
||||
@ -87,6 +57,10 @@ import sun.dyn.CallSiteImpl;
|
||||
* Here is a sample use of call sites and bootstrap methods which links every
|
||||
* dynamic call site to print its arguments:
|
||||
<blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java -->
|
||||
@BootstrapMethod(value=PrintArgsDemo.class, name="bootstrapDynamic")
|
||||
static void test() throws Throwable {
|
||||
InvokeDynamic.baz("baz arg", 2, 3.14);
|
||||
}
|
||||
private static void printArgs(Object... args) {
|
||||
System.out.println(java.util.Arrays.deepToString(args));
|
||||
}
|
||||
@ -96,17 +70,16 @@ static {
|
||||
Class thisClass = lookup.lookupClass(); // (who am I?)
|
||||
printArgs = lookup.findStatic(thisClass,
|
||||
"printArgs", MethodType.methodType(void.class, Object[].class));
|
||||
Linkage.registerBootstrapMethod("bootstrapDynamic");
|
||||
}
|
||||
private static CallSite bootstrapDynamic(Class caller, String name, MethodType type) {
|
||||
// ignore caller and name, but match the type:
|
||||
return new CallSite(MethodHandles.collectArguments(printArgs, type));
|
||||
}
|
||||
</pre></blockquote>
|
||||
* @see Linkage#registerBootstrapMethod(java.lang.Class, java.dyn.MethodHandle)
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class CallSite
|
||||
implements MethodHandleProvider
|
||||
{
|
||||
private static final Access IMPL_TOKEN = Access.getToken();
|
||||
|
||||
@ -209,6 +182,7 @@ public class CallSite
|
||||
* {@code InvokeDynamicBootstrapError}, which in turn causes the
|
||||
* linkage of the {@code invokedynamic} instruction to terminate
|
||||
* abnormally.
|
||||
* @deprecated transitional form defined in EDR but removed in PFD
|
||||
*/
|
||||
protected MethodHandle initialTarget(Class<?> callerClass, String name, MethodType type) {
|
||||
throw new InvokeDynamicBootstrapError("target must be initialized before call site is linked: "+name+type);
|
||||
@ -278,16 +252,44 @@ public class CallSite
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder("CallSite#");
|
||||
buf.append(hashCode());
|
||||
if (!isLinked())
|
||||
buf.append("[unlinked]");
|
||||
else
|
||||
buf.append("[")
|
||||
.append("from ").append(vmmethod.getDeclaringClass().getName())
|
||||
.append(" : ").append(getTarget().type())
|
||||
.append(" => ").append(getTarget())
|
||||
.append("]");
|
||||
return buf.toString();
|
||||
return "CallSite"+(target == null ? "" : target.type());
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* Produce a method handle equivalent to an invokedynamic instruction
|
||||
* which has been linked to this call site.
|
||||
* <p>If this call site is a {@link ConstantCallSite}, this method
|
||||
* simply returns the call site's target, since that will not change.
|
||||
* <p>Otherwise, this method is equivalent to the following code:
|
||||
* <p><blockquote><pre>
|
||||
* MethodHandle getTarget, invoker, result;
|
||||
* getTarget = MethodHandles.lookup().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 getTarget(); // will not change dynamically
|
||||
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) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
/** Implementation of {@link MethodHandleProvider} which returns {@code this.dynamicInvoker()}. */
|
||||
public final MethodHandle asMethodHandle() { return dynamicInvoker(); }
|
||||
|
||||
/** Implementation of {@link MethodHandleProvider}, which returns {@code this.dynamicInvoker().asType(type)}. */
|
||||
public final MethodHandle asMethodHandle(MethodType type) { return dynamicInvoker().asType(type); }
|
||||
}
|
||||
|
173
jdk/src/share/classes/java/dyn/ClassValue.java
Normal file
173
jdk/src/share/classes/java/dyn/ClassValue.java
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Lazily associate a computed value with (potentially) every class.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public abstract class ClassValue<T> {
|
||||
/**
|
||||
* Compute the given class's derived value for this {@code ClassValue}.
|
||||
* <p>
|
||||
* This method will be invoked within the first thread that accesses
|
||||
* the value with the {@link #get}.
|
||||
* <p>
|
||||
* Normally, this method is invoked at most once per class,
|
||||
* but it may be invoked again in case of subsequent invocations
|
||||
* of {@link #remove} followed by {@link #get}.
|
||||
*
|
||||
* @return the computed value for this thread-local
|
||||
*/
|
||||
protected abstract T computeValue(Class<?> type);
|
||||
|
||||
/**
|
||||
* Creates a new class value.
|
||||
*/
|
||||
protected ClassValue() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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} method.
|
||||
* <p>
|
||||
* The actual installation of the value on the class
|
||||
* is performed while the class's synchronization lock
|
||||
* is held. At that point, if racing threads have
|
||||
* computed values, one is chosen, and returned to
|
||||
* all the racing threads.
|
||||
*
|
||||
* @return the current thread's value of this thread-local
|
||||
*/
|
||||
public T get(Class<?> type) {
|
||||
ClassValueMap map = getMap(type);
|
||||
if (map != null) {
|
||||
Object x = map.get(this);
|
||||
if (x != null) {
|
||||
return (T) map.unmaskNull(x);
|
||||
}
|
||||
}
|
||||
return setComputedValue(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the associated value for the given class.
|
||||
* If this value is subsequently {@linkplain #get read} for the same class,
|
||||
* its value will be reinitialized by invoking its {@link #computeValue} method.
|
||||
* This may result in an additional invocation of the
|
||||
* {@code computeValue} method for the given class.
|
||||
*/
|
||||
public void remove(Class<?> type) {
|
||||
ClassValueMap map = getMap(type);
|
||||
if (map != null) {
|
||||
synchronized (map) {
|
||||
map.remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation...
|
||||
|
||||
/** The hash code for this type is based on the identity of the object,
|
||||
* and is well-dispersed for power-of-two tables.
|
||||
*/
|
||||
public final int hashCode() { return hashCode; }
|
||||
private final int hashCode = HASH_CODES.getAndAdd(0x61c88647);
|
||||
private static final AtomicInteger HASH_CODES = new AtomicInteger();
|
||||
|
||||
private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
|
||||
|
||||
/** Slow path for {@link #get}. */
|
||||
private T setComputedValue(Class<?> type) {
|
||||
ClassValueMap map = getMap(type);
|
||||
if (map == null) {
|
||||
map = initializeMap(type);
|
||||
}
|
||||
T value = computeValue(type);
|
||||
STORE_BARRIER.lazySet(0);
|
||||
// All stores pending from computeValue are completed.
|
||||
synchronized (map) {
|
||||
// Warm up the table with a null entry.
|
||||
map.preInitializeEntry(this);
|
||||
}
|
||||
// All stores pending from table expansion are completed.
|
||||
synchronized (map) {
|
||||
value = (T) map.initializeEntry(this, value);
|
||||
// One might fear a possible race condition here
|
||||
// if the code for map.put has flushed the write
|
||||
// to map.table[*] before the writes to the Map.Entry
|
||||
// are done. This is not possible, since we have
|
||||
// warmed up the table with an empty entry.
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Replace this map by a per-class slot.
|
||||
private static final WeakHashMap<Class<?>, ClassValueMap> ROOT
|
||||
= new WeakHashMap<Class<?>, ClassValueMap>();
|
||||
|
||||
private static ClassValueMap getMap(Class<?> type) {
|
||||
return ROOT.get(type);
|
||||
}
|
||||
|
||||
private static ClassValueMap initializeMap(Class<?> type) {
|
||||
synchronized (ClassValue.class) {
|
||||
ClassValueMap map = ROOT.get(type);
|
||||
if (map == null)
|
||||
ROOT.put(type, map = new ClassValueMap());
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassValueMap extends WeakHashMap<ClassValue, Object> {
|
||||
/** Make sure this table contains an Entry for the given key, even if it is empty. */
|
||||
void preInitializeEntry(ClassValue key) {
|
||||
if (!this.containsKey(key))
|
||||
this.put(key, null);
|
||||
}
|
||||
/** Make sure this table contains a non-empty Entry for the given key. */
|
||||
Object initializeEntry(ClassValue key, Object value) {
|
||||
Object prior = this.get(key);
|
||||
if (prior != null) {
|
||||
return unmaskNull(prior);
|
||||
}
|
||||
this.put(key, maskNull(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
Object maskNull(Object x) {
|
||||
return x == null ? this : x;
|
||||
}
|
||||
Object unmaskNull(Object x) {
|
||||
return x == this ? null : x;
|
||||
}
|
||||
}
|
||||
}
|
43
jdk/src/share/classes/java/dyn/ConstantCallSite.java
Normal file
43
jdk/src/share/classes/java/dyn/ConstantCallSite.java
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed.
|
||||
* The only way to relink an {@code invokedynamic} instruction bound to a {@code ConstantCallSite} is
|
||||
* to invalidate the instruction as a whole.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class ConstantCallSite extends CallSite {
|
||||
/** Create a call site with a permanent target. */
|
||||
public ConstantCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
}
|
||||
/** Throw an {@link IllegalArgumentException}, because this kind of call site cannot change its target. */
|
||||
@Override public final void setTarget(MethodHandle ignore) {
|
||||
throw new IllegalArgumentException("ConstantCallSite");
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ package java.dyn;
|
||||
* The target method is a property of the reified {@linkplain CallSite call site object}
|
||||
* which is linked to each active {@code invokedynamic} instruction.
|
||||
* The call site object is initially produced by a
|
||||
* {@linkplain java.dyn.Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method}
|
||||
* {@linkplain BootstrapMethod bootstrap method}
|
||||
* associated with the class whose bytecodes include the dynamic call site.
|
||||
* <p>
|
||||
* The type {@code InvokeDynamic} has no particular meaning as a
|
||||
@ -45,22 +45,31 @@ package java.dyn;
|
||||
* It may be imported for ease of use.
|
||||
* <p>
|
||||
* Here are some examples:
|
||||
* <p><blockquote><pre>
|
||||
* Object x; String s; int i;
|
||||
* x = InvokeDynamic.greet("world"); // greet(Ljava/lang/String;)Ljava/lang/Object;
|
||||
* s = InvokeDynamic.<String>hail(x); // hail(Ljava/lang/Object;)Ljava/lang/String;
|
||||
* InvokeDynamic.<void>cogito(); // cogito()V
|
||||
* i = InvokeDynamic.<int>#"op:+"(2, 3); // "op:+"(II)I
|
||||
* </pre></blockquote>
|
||||
<blockquote><pre><!-- see indy-demo/src/JavaDocExamples.java -->
|
||||
@BootstrapMethod(value=Here.class, name="bootstrapDynamic")
|
||||
static void example() throws Throwable {
|
||||
Object x; String s; int i;
|
||||
x = InvokeDynamic.greet("world"); // greet(Ljava/lang/String;)Ljava/lang/Object;
|
||||
s = (String) InvokeDynamic.hail(x); // hail(Ljava/lang/Object;)Ljava/lang/String;
|
||||
InvokeDynamic.cogito(); // cogito()V
|
||||
i = (int) InvokeDynamic.#"op:+"(2, 3); // "op:+"(II)I
|
||||
}
|
||||
static MethodHandle bootstrapDynamic(Class caller, String name, MethodType type) { ... }
|
||||
</pre></blockquote>
|
||||
* Each of the above calls generates a single invokedynamic instruction
|
||||
* with the name-and-type descriptors indicated in the comments.
|
||||
* <p>
|
||||
* The argument types are taken directly from the actual arguments,
|
||||
* while the return type is taken from the type parameter.
|
||||
* (This type parameter may be a primtive, and it defaults to {@code Object}.)
|
||||
* while the return type corresponds to the target of the assignment.
|
||||
* (Currently, the return type must be given as a false type parameter.
|
||||
* This type parameter is an irregular use of the generic type syntax,
|
||||
* and is likely to change in favor of a convention based on target typing.)
|
||||
* <p>
|
||||
* The final example uses a special syntax for uttering non-Java names.
|
||||
* Any name legal to the JVM may be given between the double quotes.
|
||||
* <p>
|
||||
* None of these calls is complete without a bootstrap method,
|
||||
* which must be registered by the static initializer of the enclosing class.
|
||||
* which must be declared for the enclosing class or method.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
@MethodHandle.PolymorphicSignature
|
||||
|
@ -28,15 +28,11 @@ package java.dyn;
|
||||
/**
|
||||
* Thrown to indicate that an {@code invokedynamic} instruction has
|
||||
* failed to find its
|
||||
* {@linkplain Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method},
|
||||
* {@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}.
|
||||
* <p>
|
||||
* The bootstrap method must have been declared during a class's initialization
|
||||
* by a call to one of the overloadings of
|
||||
* {@link Linkage#registerBootstrapMethod registerBootstrapMethod}.
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package java.dyn;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.dyn.MethodHandles.Lookup;
|
||||
import java.util.WeakHashMap;
|
||||
import sun.dyn.Access;
|
||||
@ -56,11 +55,7 @@ public class Linkage {
|
||||
* <li>the class containing the {@code invokedynamic} instruction, for which the bootstrap method was registered
|
||||
* <li>the name of the method being invoked (a {@link String})
|
||||
* <li>the type of the method being invoked (a {@link MethodType})
|
||||
* <li><em>TBD</em> optionally, an unordered array of {@link Annotation}s attached to the call site
|
||||
* <em>(Until this feature is implemented, this will always receive an empty array.)</em>
|
||||
* </ul>
|
||||
* <em>(TBD: The final argument type may be missing from the method handle's type.
|
||||
* Additional arguments may be added in the future.)</em>
|
||||
* The bootstrap method acts as a factory method which accepts the given arguments
|
||||
* and returns a {@code CallSite} object (possibly of a subclass of {@code CallSite}).
|
||||
* <p>
|
||||
@ -86,6 +81,7 @@ public class Linkage {
|
||||
* or is already running in another thread
|
||||
* @exception SecurityException if there is a security manager installed,
|
||||
* and a {@link LinkagePermission} check fails for "registerBootstrapMethod"
|
||||
* @deprecated Use @{@link BootstrapMethod} annotations instead
|
||||
*/
|
||||
public static
|
||||
void registerBootstrapMethod(Class callerClass, MethodHandle bootstrapMethod) {
|
||||
@ -97,14 +93,9 @@ public class Linkage {
|
||||
|
||||
static private void checkBSM(MethodHandle mh) {
|
||||
if (mh == null) throw newIllegalArgumentException("null bootstrap method");
|
||||
if (mh.type() == BOOTSTRAP_METHOD_TYPE_2)
|
||||
// For now, always pass an empty array for the Annotations argument
|
||||
mh = MethodHandles.insertArguments(mh, BOOTSTRAP_METHOD_TYPE_2.parameterCount()-1,
|
||||
(Object)NO_ANNOTATIONS);
|
||||
if (mh.type() == BOOTSTRAP_METHOD_TYPE) return;
|
||||
throw new WrongMethodTypeException(mh.toString());
|
||||
}
|
||||
static private final Annotation[] NO_ANNOTATIONS = { };
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
@ -115,6 +106,7 @@ public class Linkage {
|
||||
* @throws NoSuchMethodException if there is no such method
|
||||
* @throws IllegalStateException if the caller class's static initializer
|
||||
* has already run, or is already running in another thread
|
||||
* @deprecated Use @{@link BootstrapMethod} annotations instead
|
||||
*/
|
||||
public static
|
||||
void registerBootstrapMethod(Class<?> runtime, String name) {
|
||||
@ -131,6 +123,7 @@ public class Linkage {
|
||||
* @throws IllegalArgumentException if there is no such method
|
||||
* @throws IllegalStateException if the caller class's static initializer
|
||||
* has already run, or is already running in another thread
|
||||
* @deprecated Use @{@link BootstrapMethod} annotations instead
|
||||
*/
|
||||
public static
|
||||
void registerBootstrapMethod(String name) {
|
||||
@ -142,18 +135,10 @@ public class Linkage {
|
||||
void registerBootstrapMethodLookup(Class<?> callerClass, Class<?> runtime, String name) {
|
||||
Lookup lookup = new Lookup(IMPL_TOKEN, callerClass);
|
||||
MethodHandle bootstrapMethod;
|
||||
// Try both types. TBD
|
||||
try {
|
||||
bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE_2);
|
||||
bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE);
|
||||
} catch (NoAccessException ex) {
|
||||
bootstrapMethod = null;
|
||||
}
|
||||
if (bootstrapMethod == null) {
|
||||
try {
|
||||
bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE);
|
||||
} catch (NoAccessException ex) {
|
||||
throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex);
|
||||
}
|
||||
throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex);
|
||||
}
|
||||
checkBSM(bootstrapMethod);
|
||||
MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod);
|
||||
@ -172,6 +157,7 @@ public class Linkage {
|
||||
* and the immediate caller of this method is not in the same
|
||||
* package as the caller class
|
||||
* and a {@link LinkagePermission} check fails for "getBootstrapMethod"
|
||||
* @deprecated
|
||||
*/
|
||||
public static
|
||||
MethodHandle getBootstrapMethod(Class callerClass) {
|
||||
@ -188,10 +174,6 @@ public class Linkage {
|
||||
public static final MethodType BOOTSTRAP_METHOD_TYPE
|
||||
= MethodType.methodType(CallSite.class,
|
||||
Class.class, String.class, MethodType.class);
|
||||
static final MethodType BOOTSTRAP_METHOD_TYPE_2
|
||||
= MethodType.methodType(CallSite.class,
|
||||
Class.class, String.class, MethodType.class,
|
||||
Annotation[].class);
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
|
@ -31,6 +31,7 @@ import java.util.Hashtable;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* This class is for managing runtime permission checking for
|
||||
* operations performed by methods in the {@link Linkage} class.
|
||||
* Like a {@link RuntimePermission}, on which it is modeled,
|
||||
@ -52,13 +53,6 @@ import java.util.StringTokenizer;
|
||||
* </tr>
|
||||
*
|
||||
* <tr>
|
||||
* <td>registerBootstrapMethod.{class name}</td>
|
||||
* <td>Specifying a bootstrap method for {@code invokedynamic} instructions within a class of the given name</td>
|
||||
* <td>An attacker could attempt to attach a bootstrap method to a class which
|
||||
* has just been loaded, thus gaining control of its {@code invokedynamic} calls.</td>
|
||||
* </tr>
|
||||
*
|
||||
* <tr>
|
||||
* <td>invalidateAll</td>
|
||||
* <td>Force the relinking of invokedynamic call sites everywhere.</td>
|
||||
* <td>This could allow an attacker to slow down the system,
|
||||
@ -73,8 +67,9 @@ import java.util.StringTokenizer;
|
||||
* <td>See {@code invalidateAll}.</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* <p>ISSUE: Is this still needed?
|
||||
*
|
||||
* @see java.security.RuntimePermission
|
||||
* @see java.lang.RuntimePermission
|
||||
* @see java.lang.SecurityManager
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
@ -86,7 +81,7 @@ public final class LinkagePermission extends BasicPermission {
|
||||
/**
|
||||
* Create a new LinkagePermission with the given name.
|
||||
* The name is the symbolic name of the LinkagePermission, such as
|
||||
* "registerBootstrapMethod", "invalidateCallerClass.*", etc. An asterisk
|
||||
* "invalidateCallerClass.*", etc. An asterisk
|
||||
* may appear at the end of the name, following a ".", or by itself, to
|
||||
* signify a wildcard match.
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -36,11 +36,13 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility
|
||||
/**
|
||||
* A method handle is a typed, directly executable reference to a method,
|
||||
* constructor, field, or similar low-level operation, with optional
|
||||
* conversion or substitution of arguments or return values.
|
||||
* transformations of arguments or return values.
|
||||
* (These transformations include conversion, insertion, deletion,
|
||||
* substitution. See the methods of this class and of {@link MethodHandles}.)
|
||||
* <p>
|
||||
* Method handles are strongly typed according to signature.
|
||||
* They are not distinguished by method name or enclosing class.
|
||||
* A method handle must be invoked under a signature which exactly matches
|
||||
* A method handle must be invoked under a signature which matches
|
||||
* the method handle's own {@link MethodType method type}.
|
||||
* <p>
|
||||
* Every method handle confesses its type via the {@code type} accessor.
|
||||
@ -174,9 +176,10 @@ assert(i == 3);
|
||||
* merely a documentation convention. These type parameters do
|
||||
* not play a role in type-checking method handle invocations.
|
||||
* <p>
|
||||
* Note: Like classes and strings, method handles that correspond directly
|
||||
* to fields and methods can be represented directly as constants to be
|
||||
* loaded by {@code ldc} bytecodes.
|
||||
* Like classes and strings, method handles that correspond to accessible
|
||||
* fields, methods, and constructors can be represented directly
|
||||
* in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
|
||||
* Loading such a constant causes the component classes of its type to be loaded as necessary.
|
||||
*
|
||||
* @see MethodType
|
||||
* @see MethodHandles
|
||||
@ -186,6 +189,7 @@ public abstract class MethodHandle
|
||||
// Note: This is an implementation inheritance hack, and will be removed
|
||||
// with a JVM change which moves the required hidden state onto this class.
|
||||
extends MethodHandleImpl
|
||||
implements MethodHandleProvider
|
||||
{
|
||||
private static Access IMPL_TOKEN = Access.getToken();
|
||||
|
||||
@ -197,7 +201,7 @@ public abstract class MethodHandle
|
||||
* those methods which are signature polymorphic.
|
||||
*/
|
||||
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.TYPE})
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS)
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@interface PolymorphicSignature { }
|
||||
|
||||
private MethodType type;
|
||||
@ -274,10 +278,14 @@ public abstract class MethodHandle
|
||||
* and performing simple conversions for arguments and return types.
|
||||
* The signature at the call site of {@code invokeGeneric} must
|
||||
* have the same arity as this method handle's {@code type}.
|
||||
* The same conversions are allowed on arguments or return values as are supported by
|
||||
* by {@link MethodHandles#convertArguments}.
|
||||
* <p>
|
||||
* If the call site signature exactly matches this method handle's {@code type},
|
||||
* the call proceeds as if by {@link #invokeExact}.
|
||||
* <p>
|
||||
* Otherwise, the call proceeds as if this method handle were first
|
||||
* adjusted by calling {@link #asType} to adjust this method handle
|
||||
* to the required type, and then the call proceeds as if by
|
||||
* {@link #invokeExact} on the adjusted method handle.
|
||||
*/
|
||||
public final native @PolymorphicSignature <R,A> R invokeGeneric(A... args) throws Throwable;
|
||||
|
||||
@ -538,4 +546,10 @@ public abstract class MethodHandle
|
||||
public final MethodHandle bindTo(Object x) {
|
||||
return MethodHandles.insertArguments(this, 0, x);
|
||||
}
|
||||
|
||||
/** Implementation of {@link MethodHandleProvider}, which returns {@code this}. */
|
||||
public final MethodHandle asMethodHandle() { return this; }
|
||||
|
||||
/** Implementation of {@link MethodHandleProvider}, which returns {@code this.asType(type)}. */
|
||||
public final MethodHandle asMethodHandle(MethodType type) { return this.asType(type); }
|
||||
}
|
||||
|
80
jdk/src/share/classes/java/dyn/MethodHandleProvider.java
Normal file
80
jdk/src/share/classes/java/dyn/MethodHandleProvider.java
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 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;
|
||||
|
||||
/**
|
||||
* An interface for an object to provide a target {@linkplain MethodHandle method handle} to a {@code invokedynamic} instruction.
|
||||
* There are many function-like objects in various Java APIs.
|
||||
* This interface provides a standard way for such function-like objects to be bound
|
||||
* to a dynamic call site, by providing a view of their behavior in the form of a low-level method handle.
|
||||
* <p>
|
||||
* The type {@link MethodHandle} is a concrete class whose implementation
|
||||
* hierarchy (if any) may be tightly coupled to the underlying JVM implementation.
|
||||
* It cannot also serve as a base type for user-defined functional APIs.
|
||||
* For this reason, {@code MethodHandle} cannot be subclassed to add new
|
||||
* behavior to method handles. But this interface can be used to provide
|
||||
* a link between a user-defined function and the {@code invokedynamic}
|
||||
* instruction and the method handle API.
|
||||
*/
|
||||
public interface MethodHandleProvider {
|
||||
/** Produce a method handle which will serve as a behavioral proxy for the current object.
|
||||
* The type and invocation behavior of the proxy method handle are user-defined,
|
||||
* and should have some relation to the intended meaning of the original object itself.
|
||||
* <p>
|
||||
* The current object may have a changeable behavior.
|
||||
* For example, {@link CallSite} has a {@code setTarget} method which changes its invocation.
|
||||
* In such a case, it is <em>incorrect</em> for {@code asMethodHandle} to return
|
||||
* a method handle whose behavior may diverge from that of the current object.
|
||||
* Rather, the returned method handle must stably and permanently access
|
||||
* the behavior of the current object, even if that behavior is changeable.
|
||||
* <p>
|
||||
* The reference identity of the proxy method handle is not guaranteed to
|
||||
* have any particular relation to the reference identity of the object.
|
||||
* In particular, several objects with the same intended meaning could
|
||||
* share a common method handle, or the same object could return different
|
||||
* method handles at different times. In the latter case, the different
|
||||
* method handles should have the same type and invocation behavior,
|
||||
* and be usable from any thread at any time.
|
||||
* In particular, if a MethodHandleProvider is bound to an <code>invokedynamic</code>
|
||||
* call site, the proxy method handle extracted at the time of binding
|
||||
* will be used for an unlimited time, until the call site is rebound.
|
||||
* <p>
|
||||
* The type {@link MethodHandle} itself implements {@code MethodHandleProvider}, and
|
||||
* for this method simply returns {@code this}.
|
||||
*/
|
||||
public MethodHandle asMethodHandle();
|
||||
|
||||
/** Produce a method handle of a given type which will serve as a behavioral proxy for the current object.
|
||||
* As for the no-argument version {@link #asMethodHandle()}, the invocation behavior of the
|
||||
* proxy method handle is user-defined. But the type must be the given type,
|
||||
* or else a {@link WrongMethodTypeException} must be thrown.
|
||||
* <p>
|
||||
* If the current object somehow represents a variadic or overloaded behavior,
|
||||
* the method handle returned for a given type might represent only a subset of
|
||||
* the current object's repertoire of behaviors, which correspond to that type.
|
||||
*/
|
||||
public MethodHandle asMethodHandle(MethodType type) throws WrongMethodTypeException;
|
||||
}
|
@ -25,15 +25,12 @@
|
||||
|
||||
package java.dyn;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.*;
|
||||
import sun.dyn.Access;
|
||||
import sun.dyn.MemberName;
|
||||
import sun.dyn.MethodHandleImpl;
|
||||
import sun.dyn.util.VerifyAccess;
|
||||
import sun.dyn.util.Wrapper;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -81,6 +78,14 @@ public class MethodHandles {
|
||||
* Return a {@link Lookup lookup object} which is trusted minimally.
|
||||
* It can only be used to create method handles to
|
||||
* publicly accessible fields and methods.
|
||||
* <p>
|
||||
* As a matter of pure convention, the {@linkplain Lookup#lookupClass lookup class}
|
||||
* of this lookup object will be {@link java.lang.Object}.
|
||||
* <p>
|
||||
* The lookup class can be changed to any other class {@code C} using an expression of the form
|
||||
* {@linkplain Lookup#in <code>publicLookup().in(C.class)</code>}.
|
||||
* Since all classes have equal access to public names,
|
||||
* such a change would confer no new access rights.
|
||||
*/
|
||||
public static Lookup publicLookup() {
|
||||
return Lookup.PUBLIC_LOOKUP;
|
||||
@ -90,9 +95,10 @@ public class MethodHandles {
|
||||
* A <em>lookup object</em> is a factory for creating method handles,
|
||||
* when the creation requires access checking.
|
||||
* Method handles do not perform
|
||||
* access checks when they are called; this is a major difference
|
||||
* access checks when they are called, but rather when they are created.
|
||||
* (This is a major difference
|
||||
* from reflective {@link Method}, which performs access checking
|
||||
* against every caller, on every call.
|
||||
* against every caller, on every call.)
|
||||
* Therefore, method handle access
|
||||
* restrictions must be enforced when a method handle is created.
|
||||
* The caller class against which those restrictions are enforced
|
||||
@ -107,7 +113,7 @@ public class MethodHandles {
|
||||
* It may then use this factory to create method handles on
|
||||
* all of its methods, including private ones.
|
||||
* It may also delegate the lookup (e.g., to a metaobject protocol)
|
||||
* by passing the {@code Lookup} object to other code.
|
||||
* by passing the lookup object to other code.
|
||||
* If this other code creates method handles, they will be access
|
||||
* checked against the original lookup class, and not with any higher
|
||||
* privileges.
|
||||
@ -125,23 +131,28 @@ public class MethodHandles {
|
||||
* It can also fail if a security manager is installed and refuses
|
||||
* access. In any of these cases, an exception will be
|
||||
* thrown from the attempted lookup.
|
||||
* <p>
|
||||
* In general, the conditions under which a method handle may be
|
||||
* created for a method {@code M} are exactly as restrictive as the conditions
|
||||
* under which the lookup class could have compiled a call to {@code M}.
|
||||
* At least some of these error conditions are likely to be
|
||||
* represented by checked exceptions in the final version of this API.
|
||||
* This rule is applied even if the Java compiler might have created
|
||||
* an wrapper method to access a private method of another class
|
||||
* in the same top-level declaration.
|
||||
* For example, a lookup object created for a nested class {@code C.D}
|
||||
* can access private members within other related classes such as
|
||||
* {@code C}, {@code C.D.E}, or {@code C.B}.
|
||||
*/
|
||||
public static final
|
||||
class Lookup {
|
||||
/** The class on behalf of whom the lookup is being performed. */
|
||||
private final Class<?> lookupClass;
|
||||
|
||||
/** The allowed sorts of members which may be looked up (public, etc.), with STRICT for package. */
|
||||
/** The allowed sorts of members which may be looked up (public, etc.), with STATIC for package. */
|
||||
private final int allowedModes;
|
||||
|
||||
private static final int
|
||||
PUBLIC = Modifier.PUBLIC,
|
||||
PACKAGE = Modifier.STRICT,
|
||||
PACKAGE = Modifier.STATIC,
|
||||
PROTECTED = Modifier.PROTECTED,
|
||||
PRIVATE = Modifier.PRIVATE,
|
||||
ALL_MODES = (PUBLIC | PACKAGE | PROTECTED | PRIVATE),
|
||||
@ -155,8 +166,10 @@ public class MethodHandles {
|
||||
/** Which class is performing the lookup? It is this class against
|
||||
* which checks are performed for visibility and access permissions.
|
||||
* <p>
|
||||
* This value is null if and only if this lookup was produced
|
||||
* by {@link MethodHandles#publicLookup}.
|
||||
* The class implies a maximum level of access permission,
|
||||
* but the permissions may be additionally limited by the bitmask
|
||||
* {@link #lookupModes}, which controls whether non-public members
|
||||
* can be accessed.
|
||||
*/
|
||||
public Class<?> lookupClass() {
|
||||
return lookupClass;
|
||||
@ -168,10 +181,15 @@ public class MethodHandles {
|
||||
}
|
||||
|
||||
/** Which types of members can this lookup object produce?
|
||||
* The result is a bit-mask of the modifier bits PUBLIC, PROTECTED, PRIVATE, and STRICT.
|
||||
* The modifier bit STRICT stands in for the (non-existent) package protection mode.
|
||||
* The result is a bit-mask of the {@link Modifier} bits
|
||||
* {@linkplain Modifier#PUBLIC PUBLIC (0x01)},
|
||||
* {@linkplain Modifier#PROTECTED PROTECTED (0x02)},
|
||||
* {@linkplain Modifier#PRIVATE PRIVATE (0x04)},
|
||||
* and {@linkplain Modifier#STATIC STATIC (0x08)}.
|
||||
* The modifier bit {@code STATIC} stands in for the package protection mode,
|
||||
* which does not have an explicit modifier bit.
|
||||
*/
|
||||
int lookupModes() {
|
||||
public int lookupModes() {
|
||||
return allowedModes & ALL_MODES;
|
||||
}
|
||||
|
||||
@ -621,32 +639,32 @@ public class MethodHandles {
|
||||
|
||||
/// Helper methods, all package-private.
|
||||
|
||||
MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) {
|
||||
MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoAccessException {
|
||||
checkSymbolicClass(refc); // do this before attempting to resolve
|
||||
int mods = (isStatic ? Modifier.STATIC : 0);
|
||||
return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
|
||||
}
|
||||
|
||||
MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) {
|
||||
MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoAccessException {
|
||||
checkSymbolicClass(refc); // do this before attempting to resolve
|
||||
int mods = (isStatic ? Modifier.STATIC : 0);
|
||||
return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
|
||||
}
|
||||
|
||||
MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic,
|
||||
boolean searchSupers, Class<?> specialCaller) {
|
||||
boolean searchSupers, Class<?> specialCaller) throws NoAccessException {
|
||||
checkSymbolicClass(refc); // do this before attempting to resolve
|
||||
int mods = (isStatic ? Modifier.STATIC : 0);
|
||||
return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller);
|
||||
}
|
||||
|
||||
void checkSymbolicClass(Class<?> refc) {
|
||||
void checkSymbolicClass(Class<?> refc) throws NoAccessException {
|
||||
Class<?> caller = lookupClassOrNull();
|
||||
if (caller != null && !VerifyAccess.isClassAccessible(refc, caller))
|
||||
throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), caller);
|
||||
}
|
||||
|
||||
void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) {
|
||||
void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws NoAccessException {
|
||||
String message;
|
||||
if (m.isConstructor())
|
||||
message = "expected a method, not a constructor";
|
||||
@ -659,7 +677,7 @@ public class MethodHandles {
|
||||
throw newNoAccessException(message, m, lookupClass());
|
||||
}
|
||||
|
||||
void checkAccess(Class<?> refc, MemberName m) {
|
||||
void checkAccess(Class<?> refc, MemberName m) throws NoAccessException {
|
||||
int allowedModes = this.allowedModes;
|
||||
if (allowedModes == TRUSTED) return;
|
||||
int mods = m.getModifiers();
|
||||
@ -695,14 +713,14 @@ public class MethodHandles {
|
||||
return "member is private to package";
|
||||
}
|
||||
|
||||
void checkSpecialCaller(Class<?> specialCaller) {
|
||||
void checkSpecialCaller(Class<?> specialCaller) throws NoAccessException {
|
||||
if (allowedModes == TRUSTED) return;
|
||||
if (!VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))
|
||||
throw newNoAccessException("no private access for invokespecial",
|
||||
new MemberName(specialCaller), lookupClass());
|
||||
}
|
||||
|
||||
MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) {
|
||||
MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws NoAccessException {
|
||||
// The accessing class only has the right to use a protected member
|
||||
// on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc.
|
||||
if (!method.isProtected() || method.isStatic()
|
||||
@ -712,7 +730,7 @@ public class MethodHandles {
|
||||
else
|
||||
return restrictReceiver(method, mh, lookupClass());
|
||||
}
|
||||
MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) {
|
||||
MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws NoAccessException {
|
||||
assert(!method.isStatic());
|
||||
Class<?> defc = method.getDeclaringClass(); // receiver type of mh is too wide
|
||||
if (defc.isInterface() || !defc.isAssignableFrom(caller)) {
|
||||
@ -898,11 +916,16 @@ public class MethodHandles {
|
||||
* @return a method handle which always invokes the call site's target
|
||||
*/
|
||||
public static
|
||||
MethodHandle dynamicInvoker(CallSite site) {
|
||||
MethodHandle dynamicInvoker(CallSite site) throws NoAccessException {
|
||||
MethodHandle getCSTarget = GET_TARGET;
|
||||
if (getCSTarget == null)
|
||||
GET_TARGET = getCSTarget = Lookup.IMPL_LOOKUP.
|
||||
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
|
||||
if (getCSTarget == null) {
|
||||
try {
|
||||
GET_TARGET = getCSTarget = Lookup.IMPL_LOOKUP.
|
||||
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
|
||||
} catch (NoAccessException ex) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, getCSTarget, site);
|
||||
MethodHandle invoker = exactInvoker(site.type());
|
||||
return foldArguments(invoker, getTarget);
|
||||
@ -1260,17 +1283,20 @@ public class MethodHandles {
|
||||
* <p>
|
||||
* <b>Example:</b>
|
||||
* <p><blockquote><pre>
|
||||
* MethodHandle cat = MethodHandles.lookup().
|
||||
* findVirtual(String.class, "concat", String.class, String.class);
|
||||
* System.out.println(cat.<String>invokeExact("x", "y")); // xy
|
||||
* import static java.dyn.MethodHandles.*;
|
||||
* import static java.dyn.MethodType.*;
|
||||
* ...
|
||||
* MethodHandle cat = lookup().findVirtual(String.class,
|
||||
* "concat", methodType(String.class, String.class));
|
||||
* System.out.println((String) cat.invokeExact("x", "y")); // xy
|
||||
* MethodHandle d0 = dropArguments(cat, 0, String.class);
|
||||
* System.out.println(d0.<String>invokeExact("x", "y", "z")); // xy
|
||||
* System.out.println((String) d0.invokeExact("x", "y", "z")); // yz
|
||||
* MethodHandle d1 = dropArguments(cat, 1, String.class);
|
||||
* System.out.println(d1.<String>invokeExact("x", "y", "z")); // xz
|
||||
* System.out.println((String) d1.invokeExact("x", "y", "z")); // xz
|
||||
* MethodHandle d2 = dropArguments(cat, 2, String.class);
|
||||
* System.out.println(d2.<String>invokeExact("x", "y", "z")); // yz
|
||||
* MethodHandle d12 = dropArguments(cat, 1, String.class, String.class);
|
||||
* System.out.println(d12.<String>invokeExact("w", "x", "y", "z")); // wz
|
||||
* System.out.println((String) d2.invokeExact("x", "y", "z")); // xy
|
||||
* MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
|
||||
* System.out.println((String) d12.invokeExact("x", 12, true, "z")); // xz
|
||||
* </pre></blockquote>
|
||||
* @param target the method handle to invoke after the argument is dropped
|
||||
* @param valueTypes the type(s) of the argument to drop
|
||||
@ -1562,4 +1588,107 @@ public class MethodHandles {
|
||||
MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
|
||||
return MethodHandleImpl.throwException(IMPL_TOKEN, MethodType.methodType(returnType, exType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a wrapper instance of the given "SAM" type which redirects its calls to the given method handle.
|
||||
* A SAM type is a type which declares a single abstract method.
|
||||
* Additionally, it must have either no constructor (as an interface)
|
||||
* or have a public or protected constructor of zero arguments (as a class).
|
||||
* <p>
|
||||
* The resulting instance of the required SAM type will respond to
|
||||
* invocation of the SAM type's single abstract method by calling
|
||||
* the given {@code target} on the incoming arguments,
|
||||
* and returning or throwing whatever the {@code target}
|
||||
* returns or throws. The invocation will be as if by
|
||||
* {@code target.invokeExact}.
|
||||
* <p>
|
||||
* The method handle may throw an <em>undeclared exception</em>,
|
||||
* which means any checked exception (or other checked throwable)
|
||||
* not declared by the SAM type's single abstract method.
|
||||
* If this happens, the throwable will be wrapped in an instance
|
||||
* of {@link UndeclaredThrowableException} and thrown in that
|
||||
* wrapped form.
|
||||
* <p>
|
||||
* The wrapper instance is guaranteed to be of a non-public
|
||||
* implementation class C in a package containing no classes
|
||||
* or methods except system-defined classes and methods.
|
||||
* The implementation class C will have no public supertypes
|
||||
* or public methods beyond the following:
|
||||
* <ul>
|
||||
* <li>the SAM type itself and any methods in the SAM type
|
||||
* <li>the supertypes of the SAM type (if any) and their methods
|
||||
* <li>{@link Object} and its methods
|
||||
* <li>{@link MethodHandleProvider} and its methods
|
||||
* </ul>
|
||||
* <p>
|
||||
* No stable mapping is promised between the SAM type and
|
||||
* the implementation class C. Over time, several implementation
|
||||
* classes might be used for the same SAM type.
|
||||
* <p>
|
||||
* This method is not guaranteed to return a distinct
|
||||
* wrapper object for each separate call. If the JVM is able
|
||||
* to prove that a wrapper has already been created for a given
|
||||
* method handle, or for another method handle with the
|
||||
* same behavior, the JVM may return that wrapper in place of
|
||||
* a new wrapper.
|
||||
* @param target the method handle to invoke from the wrapper
|
||||
* @param samType the desired type of the wrapper, a SAM type
|
||||
* @return a correctly-typed wrapper for the given {@code target}
|
||||
* @throws IllegalArgumentException if the {@code target} throws
|
||||
* an undeclared exception
|
||||
*/
|
||||
// ISSUE: Should we delegate equals/hashCode to the targets?
|
||||
// Not useful unless there is a stable equals/hashCode behavior
|
||||
// for MethodHandle, and for MethodHandleProvider.asMethodHandle.
|
||||
public static
|
||||
<T> T asInstance(MethodHandle target, Class<T> samType) {
|
||||
// POC implementation only; violates the above contract several ways
|
||||
final Method sam = getSamMethod(samType);
|
||||
if (sam == null)
|
||||
throw new IllegalArgumentException("not a SAM type: "+samType.getName());
|
||||
MethodType samMT = MethodType.methodType(sam.getReturnType(), sam.getParameterTypes());
|
||||
if (!samMT.equals(target.type()))
|
||||
throw new IllegalArgumentException("wrong method type");
|
||||
final MethodHandle mh = target;
|
||||
return samType.cast(Proxy.newProxyInstance(
|
||||
samType.getClassLoader(),
|
||||
new Class[]{ samType, MethodHandleProvider.class },
|
||||
new InvocationHandler() {
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (method.getDeclaringClass() == MethodHandleProvider.class) {
|
||||
return method.invoke(mh, args);
|
||||
}
|
||||
assert method.equals(sam) : method;
|
||||
return mh.invokeVarargs(args);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static
|
||||
Method getSamMethod(Class<?> samType) {
|
||||
Method sam = null;
|
||||
for (Method m : samType.getMethods()) {
|
||||
int mod = m.getModifiers();
|
||||
if (Modifier.isAbstract(mod)) {
|
||||
if (sam != null)
|
||||
return null; // too many abstract methods
|
||||
sam = m;
|
||||
}
|
||||
}
|
||||
if (!samType.isInterface() && getSamConstructor(samType) == null)
|
||||
return null; // wrong kind of constructor
|
||||
return sam;
|
||||
}
|
||||
|
||||
private static
|
||||
Constructor getSamConstructor(Class<?> samType) {
|
||||
for (Constructor c : samType.getDeclaredConstructors()) {
|
||||
if (c.getParameterTypes().length == 0) {
|
||||
int mod = c.getModifiers();
|
||||
if (Modifier.isPublic(mod) || Modifier.isProtected(mod))
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -40,24 +40,37 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
|
||||
* returned by a method handle, or the arguments and return type passed
|
||||
* 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 all times.
|
||||
* and the JVM's operations enforce this matching at, specifically
|
||||
* during calls to {@link MethodHandle#invokeExact}
|
||||
* and {@link MethodHandle#invokeGeneric}, and during execution
|
||||
* of {@code invokedynamic} instructions.
|
||||
* <p>
|
||||
* The structure is a return type accompanied by any number of parameter types.
|
||||
* The types (primitive, void, and reference) are represented by Class objects.
|
||||
* The types (primitive, {@code void}, and reference) are represented by {@link Class} objects.
|
||||
* (For ease of exposition, we treat {@code void} as if it were a type.
|
||||
* In fact, it denotes the absence of a return type.)
|
||||
* <p>
|
||||
* All instances of <code>MethodType</code> are immutable.
|
||||
* All instances of {@code MethodType} are immutable.
|
||||
* Two instances are completely interchangeable if they compare equal.
|
||||
* Equality depends on pairwise correspondence of the return and parameter types and on nothing else.
|
||||
* <p>
|
||||
* This type can be created only by factory methods.
|
||||
* All factory methods may cache values, though caching is not guaranteed.
|
||||
* <p>
|
||||
* Note: Like classes and strings, method types can be represented directly
|
||||
* as constants to be loaded by {@code ldc} bytecodes.
|
||||
* {@code MethodType} objects are sometimes derived from bytecode instructions
|
||||
* such as {@code invokedynamic}, specifically from the type descriptor strings associated
|
||||
* with the instructions in a class file's constant pool.
|
||||
* When this occurs, any classes named in the descriptor strings must be loaded.
|
||||
* (But they need not be initialized.)
|
||||
* This loading may occur at any time before the {@code MethodType} object is first derived.
|
||||
* <p>
|
||||
* Like classes and strings, method types can be represented directly
|
||||
* in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
|
||||
* Loading such a constant causes its component classes to be loaded as necessary.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public final
|
||||
class MethodType {
|
||||
class MethodType implements java.lang.reflect.Type {
|
||||
private final Class<?> rtype;
|
||||
private final Class<?>[] ptypes;
|
||||
private MethodTypeForm form; // erased form, plus cached data about primitives
|
||||
@ -636,11 +649,11 @@ class MethodType {
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* Find or create an instance of the given method type.
|
||||
* Any class or interface name embedded in the signature string
|
||||
* 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).
|
||||
* <p>
|
||||
* Note that it is possible to build method types which cannot be
|
||||
* Note that it is possible to encounter method types which cannot be
|
||||
* constructed by this method, because their component types are
|
||||
* not all reachable from a common class loader.
|
||||
* <p>
|
||||
@ -662,8 +675,11 @@ class MethodType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bytecode signature representation of the type.
|
||||
* Note that this is not a strict inverse of
|
||||
* Create a bytecode descriptor representation of the method type.
|
||||
* <p>
|
||||
* Note that this is not a strict inverse of {@link #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.
|
||||
|
@ -37,7 +37,7 @@ package java.dyn;
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
*/
|
||||
public class NoAccessException extends RuntimeException {
|
||||
public class NoAccessException extends ReflectiveOperationException {
|
||||
private static final long serialVersionUID = 292L;
|
||||
|
||||
/**
|
||||
|
@ -40,20 +40,18 @@
|
||||
* 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>
|
||||
*
|
||||
* <li>In source code, the class {@link java.dyn.InvokeDynamic} appears to accept
|
||||
* <li>In source code, the class {@link java.dyn.InvokeDynamic InvokeDynamic} appears to accept
|
||||
* any static method invocation, of any name and any signature.
|
||||
* But instead of emitting
|
||||
* an {@code invokestatic} instruction for such a call, the Java compiler emits
|
||||
* an {@code invokedynamic} instruction with the given name and signature.
|
||||
*
|
||||
* <li>When the JVM links an {@code invokedynamic} instruction, it calls the
|
||||
* {@linkplain java.dyn.Linkage#registerBootstrapMethod(Class, MethodHandle) bootstrap method}
|
||||
* of the containing class to obtain a {@linkplain java.dyn.CallSite call site} object through which
|
||||
* the call site will link its target {@linkplain java.dyn.MethodHandle method handle}.
|
||||
* </li>
|
||||
*
|
||||
* <li>The JVM bytecode format supports immediate constants of
|
||||
* the classes {@link java.dyn.MethodHandle} and {@link java.dyn.MethodType}.
|
||||
* the classes {@link java.dyn.MethodHandle MethodHandle} and {@link java.dyn.MethodType MethodType}.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2><a name="jvm_mods"></a>Corresponding JVM bytecode format changes</h2>
|
||||
@ -65,18 +63,50 @@
|
||||
* The first byte is the opcode 186 (hexadecimal {@code BA}).
|
||||
* The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions).
|
||||
* The final two bytes are reserved for future use and required to be zero.
|
||||
* The constant pool reference is to a entry with tag {@code CONSTANT_NameAndType}
|
||||
* (decimal 12). It is thus not a method reference of any sort, but merely
|
||||
* the method name, argument types, and return type of the dynamic call site.
|
||||
* <em>(TBD: The EG is discussing the possibility of a special constant pool entry type,
|
||||
* so that other information may be added, such as a per-instruction bootstrap
|
||||
* method and/or annotations.)</em>
|
||||
* The constant pool reference of an {@code invokedynamic} instruction is to a entry
|
||||
* with tag {@code CONSTANT_InvokeDynamic} (decimal 17). See below for its format.
|
||||
* The entry specifies the bootstrap method (a {@link java.dyn.MethodHandle MethodHandle} constant),
|
||||
* the dynamic invocation name, and the argument types and return type of the call.
|
||||
* <p>
|
||||
* Each instance of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>.
|
||||
* Multiple instances of an {@code invokedynamic} instruction can share a single
|
||||
* {@code CONSTANT_InvokeDynamic} entry.
|
||||
* In any case, distinct call sites always have distinct linkage state.
|
||||
* <p>
|
||||
* Moreover, for the purpose of distinguishing dynamic call sites,
|
||||
* the JVM is allowed (but not required) to make internal copies
|
||||
* of {@code invokedynamic} instructions, each one
|
||||
* constituting a separate dynamic call site with its own linkage state.
|
||||
* Such copying, if it occurs, cannot be observed except indirectly via
|
||||
* execution of bootstrap methods and target methods.
|
||||
* <p>
|
||||
* A dynamic call site is originally in an unlinked state. In this state, there is
|
||||
* no target method for the call site to invoke.
|
||||
* A dynamic call site is linked by means of a bootstrap method,
|
||||
* as <a href="#bsm">described below</a>.
|
||||
* <p>
|
||||
* <em>(Historic Note: 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.)</em>
|
||||
*
|
||||
* <h3>constant pool entries for {@code invokedynamic} instructions</h3>
|
||||
* If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 17),
|
||||
* it must contain exactly four more bytes.
|
||||
* The first two bytes after the tag must be an index to a {@code CONSTANT_MethodHandle}
|
||||
* entry, and the second two bytes must be an index to a {@code CONSTANT_NameAndType}.
|
||||
* 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 {@code CONSTANT_Class} reference in a {@code CONSTANT_Methodref} entry
|
||||
* is replaced by a bootstrap method reference.
|
||||
*
|
||||
* <h3>constant pool entries for {@code MethodType}s</h3>
|
||||
* If a constant pool entry has the tag {@code CONSTANT_MethodType} (decimal 16),
|
||||
* it must contain exactly two more bytes, which are an index to a {@code CONSTANT_Utf8}
|
||||
* entry which represents a method type signature. The JVM will ensure that on first
|
||||
* execution of an {@code ldc} instruction for this entry, a {@link java.dyn.MethodType}
|
||||
* it must contain exactly two more bytes, which must be an index to a {@code CONSTANT_Utf8}
|
||||
* entry which represents a method type signature.
|
||||
* <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.
|
||||
* Any classes mentioned in the {@code MethodType} will be loaded if necessary,
|
||||
* but not initialized.
|
||||
@ -86,12 +116,15 @@
|
||||
* <h3>constant pool entries for {@code MethodHandle}s</h3>
|
||||
* If a constant pool entry has the tag {@code CONSTANT_MethodHandle} (decimal 15),
|
||||
* it must contain exactly three more bytes. The first byte after the tag is a subtag
|
||||
* value in the range 1 through 9, and the last two are an index to a
|
||||
* value which must be in the range 1 through 9, and the last two must be an index to a
|
||||
* {@code CONSTANT_Fieldref}, {@code CONSTANT_Methodref}, or
|
||||
* {@code CONSTANT_InterfaceMethodref} entry which represents a field or method
|
||||
* for which a method handle is to be created.
|
||||
* Furthermore, the subtag value and the type of the constant index value
|
||||
* must agree according to the table below.
|
||||
* <p>
|
||||
* The JVM will ensure that on first execution of an {@code ldc} instruction
|
||||
* for this entry, a {@link java.dyn.MethodHandle} will be created which represents
|
||||
* for this entry, a {@link java.dyn.MethodHandle MethodHandle} will be created which represents
|
||||
* the field or method reference, according to the specific mode implied by the subtag.
|
||||
* <p>
|
||||
* As with {@code CONSTANT_Class} and {@code CONSTANT_MethodType} constants,
|
||||
@ -126,6 +159,129 @@
|
||||
* Method handles for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic}
|
||||
* may force class initialization on their first invocation, just like the corresponding bytecodes.
|
||||
*
|
||||
* <h2><a name="bsm"></a>Bootstrap Methods</h2>
|
||||
* Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
|
||||
* the call site must first be <em>linked</em>.
|
||||
* Linking is accomplished by calling a <em>bootstrap method</em>
|
||||
* which is given the static information content of the call site,
|
||||
* and which must produce a {@link java.dyn.MethodHandle method handle}
|
||||
* that gives the behavior of the call site.
|
||||
* <p>
|
||||
* Each {@code invokedynamic} instruction statically specifies its own
|
||||
* bootstrap method as a constant pool reference.
|
||||
* The constant pool reference also specifies the call site's name and type signature,
|
||||
* 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.
|
||||
* 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 values being stacked:
|
||||
* <ul>
|
||||
* <li>a {@code MethodHandle}, the resolved bootstrap method itself </li>
|
||||
* <li>a {@code Class}, 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>
|
||||
* </ul>
|
||||
* The method handle is then applied to the other values as if by
|
||||
* {@linkplain java.dyn.MethodHandle#invokeGeneric the <code>invokeGeneric</code> method}.
|
||||
* The returned result must be a {@link java.dyn.CallSite CallSite}, a {@link java.dyn.MethodHandle MethodHandle},
|
||||
* or another {@link java.dyn.MethodHandleProvider MethodHandleProvider} value.
|
||||
* The method {@linkplain java.dyn.MethodHandleProvider#asMethodHandle asMethodHandle}
|
||||
* is then called on the returned value. The result of that second
|
||||
* call is the {@code MethodHandle} which becomes the
|
||||
* permanent binding for the dynamic call site.
|
||||
* That method handle's type must be exactly equal to the type
|
||||
* derived from the dynamic call site signature and passed to
|
||||
* the bootstrap method.
|
||||
* <p>
|
||||
* After resolution, the linkage process may fail in a variety of ways.
|
||||
* All failures are reported by an {@link java.dyn.InvokeDynamicBootstrapError InvokeDynamicBootstrapError},
|
||||
* which is thrown as the abnormal termination of the dynamic call
|
||||
* site execution.
|
||||
* The following circumstances will cause this:
|
||||
* <ul>
|
||||
* <li>the bootstrap method invocation completes abnormally </li>
|
||||
* <li>the result from the bootstrap invocation is not a reference to
|
||||
* an object of type {@link java.dyn.MethodHandleProvider MethodHandleProvider} </li>
|
||||
* <li>the call to {@code asMethodHandle} completes abnormally </li>
|
||||
* <li>the call to {@code asMethodHandle} fails to return a reference to
|
||||
* an object of type {@link java.dyn.MethodHandle MethodHandle} </li>
|
||||
* <li>the method handle produced by {@code asMethodHandle} does not have
|
||||
* the expected {@code MethodType} </li>
|
||||
* </ul>
|
||||
* <h3>timing of linkage</h3>
|
||||
* A dynamic call site is linked just before its first execution.
|
||||
* The bootstrap method call implementing the linkage occurs within
|
||||
* a thread that is attempting a first execution.
|
||||
* <p>
|
||||
* If there are several such threads, the JVM picks one thread
|
||||
* and runs the bootstrap method while the others wait for the
|
||||
* invocation to terminate normally or abnormally.
|
||||
* <p>
|
||||
* After a bootstrap method is called and a method handle target
|
||||
* successfully extracted, the JVM attempts to link the instruction
|
||||
* being executed to the target method handle.
|
||||
* This may fail if there has been intervening linkage
|
||||
* or invalidation event for the same instruction.
|
||||
* If such a failure occurs, the dynamic call site must be
|
||||
* re-executed from the beginning, either re-linking it
|
||||
* (if it has been invalidated) or invoking the target
|
||||
* (if it the instruction has been linked by some other means).
|
||||
* <p>
|
||||
* If the instruction is linked successfully, the target method
|
||||
* handle is invoked to complete the instruction execution.
|
||||
* The state of linkage continues until the method containing the
|
||||
* dynamic call site is garbage collected, or the dynamic call site
|
||||
* is invalidated by an explicit request,
|
||||
* such as {@link java.dyn.Linkage#invalidateCallerClass Linkage.invalidateCallerClass}.
|
||||
* <p>
|
||||
* In an application which requires dynamic call sites with individually
|
||||
* mutable behaviors, their bootstrap methods should produce distinct
|
||||
* {@link java.dyn.CallSite CallSite} objects, one for each linkage request.
|
||||
* <p>
|
||||
* If a class containing {@code invokedynamic} instructions
|
||||
* is {@linkplain java.dyn.Linkage#invalidateCallerClass(Class) invalidated},
|
||||
* subsequent execution of those {@code invokedynamic} instructions
|
||||
* will require linking.
|
||||
* It is as if they had never been executed in the first place.
|
||||
* (However, invalidation does not cause constant pool entries to be
|
||||
* resolved a second time.)
|
||||
* <p>
|
||||
* Invalidation events and bootstrap method calls for a particular
|
||||
* dynamic call site are globally ordered relative to each other.
|
||||
* When an invokedynamic instruction is invalidated, if there is
|
||||
* simultaneously a bootstrap method invocation in process
|
||||
* (in the same thread or a different thread), the result
|
||||
* eventually returned must not be used to link the call site.
|
||||
* Put another way, when a call site is invalidated, its
|
||||
* subsequent linkage (if any) must be performed by a bootstrap method
|
||||
* call initiated after the invalidation occurred.
|
||||
* <p>
|
||||
* If several threads simultaneously execute a bootstrap method for a single dynamic
|
||||
* call site, the JVM must choose one target object and installs it visibly to
|
||||
* all threads. Any other bootstrap method calls are allowed to complete, but their
|
||||
* results are ignored, and their dynamic call site invocations proceed with the originally
|
||||
* chosen target object.
|
||||
* <p>
|
||||
* The JVM is free to duplicate dynamic call sites.
|
||||
* This means that, even if a class contains just one {@code invokedynamic}
|
||||
* instruction, its bootstrap method may be executed several times,
|
||||
* once for each duplicate. Thus, bootstrap method code should not
|
||||
* assume an exclusive one-to-one correspondence between particular occurrences
|
||||
* of {@code invokedynamic} bytecodes in class files and linkage events.
|
||||
* <p>
|
||||
* In principle, each individual execution of an {@code invokedynamic}
|
||||
* instruction could be deemed (by a conforming implementation) to be a separate
|
||||
* duplicate, requiring its own execution of the bootstrap method.
|
||||
* However, implementations are expected to perform code duplication
|
||||
* (if at all) in order to improve performance, not make it worse.
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
|
||||
|
@ -48,8 +48,6 @@ public class BoundMethodHandle extends MethodHandle {
|
||||
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
|
||||
|
||||
// Constructors in this class *must* be package scoped or private.
|
||||
// Exception: JavaMethodHandle constructors are protected.
|
||||
// (The link between JMH and BMH is temporary.)
|
||||
|
||||
/** Bind a direct MH to its receiver (or first ref. argument).
|
||||
* The JVM will pre-dispatch the MH if it is not already static.
|
||||
@ -122,55 +120,6 @@ public class BoundMethodHandle extends MethodHandle {
|
||||
assert(this instanceof JavaMethodHandle);
|
||||
}
|
||||
|
||||
/** Initialize the current object as a Java method handle.
|
||||
*/
|
||||
protected BoundMethodHandle(String entryPointName, MethodType type, boolean matchArity) {
|
||||
super(Access.TOKEN, null);
|
||||
MethodHandle entryPoint
|
||||
= findJavaMethodHandleEntryPoint(this.getClass(),
|
||||
entryPointName, type, matchArity);
|
||||
MethodHandleImpl.initType(this, entryPoint.type().dropParameterTypes(0, 1));
|
||||
this.argument = this; // kludge; get rid of
|
||||
this.vmargslot = this.type().parameterSlotDepth(0);
|
||||
initTarget(entryPoint, 0);
|
||||
assert(this instanceof JavaMethodHandle);
|
||||
}
|
||||
|
||||
private static
|
||||
MethodHandle findJavaMethodHandleEntryPoint(Class<?> caller,
|
||||
String name,
|
||||
MethodType type,
|
||||
boolean matchArity) {
|
||||
if (matchArity) type.getClass(); // elicit NPE
|
||||
List<MemberName> methods = IMPL_NAMES.getMethods(caller, true, name, null, caller);
|
||||
MethodType foundType = null;
|
||||
MemberName foundMethod = null;
|
||||
for (MemberName method : methods) {
|
||||
if (method.getDeclaringClass() == MethodHandle.class)
|
||||
continue; // ignore methods inherited from MH class itself
|
||||
MethodType mtype = method.getMethodType();
|
||||
if (type != null && type.parameterCount() != mtype.parameterCount())
|
||||
continue;
|
||||
else if (foundType == null)
|
||||
foundType = mtype;
|
||||
else if (foundType != mtype)
|
||||
throw newIllegalArgumentException("more than one method named "+name+" in "+caller.getName());
|
||||
// discard overrides
|
||||
if (foundMethod == null)
|
||||
foundMethod = method;
|
||||
else if (foundMethod.getDeclaringClass().isAssignableFrom(method.getDeclaringClass()))
|
||||
foundMethod = method;
|
||||
}
|
||||
if (foundMethod == null)
|
||||
throw newIllegalArgumentException("no method named "+name+" in "+caller.getName());
|
||||
MethodHandle entryPoint = MethodHandleImpl.findMethod(IMPL_TOKEN, foundMethod, true, caller);
|
||||
if (type != null) {
|
||||
MethodType epType = type.insertParameterTypes(0, entryPoint.type().parameterType(0));
|
||||
entryPoint = MethodHandles.convertArguments(entryPoint, epType);
|
||||
}
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
/** Make sure the given {@code argument} can be used as {@code argnum}-th
|
||||
* parameter of the given method handle {@code mh}, which must be a reference.
|
||||
* <p>
|
||||
|
@ -26,6 +26,7 @@
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.*;
|
||||
import static sun.dyn.MemberName.uncaughtException;
|
||||
|
||||
/**
|
||||
* Parts of CallSite known to the JVM.
|
||||
@ -49,18 +50,21 @@ public class CallSiteImpl {
|
||||
}
|
||||
CallSite site;
|
||||
try {
|
||||
if (bootstrapMethod.type().parameterCount() == 3)
|
||||
site = bootstrapMethod.<CallSite>invokeExact(caller, name, type);
|
||||
else if (bootstrapMethod.type().parameterCount() == 4)
|
||||
site = bootstrapMethod.<CallSite>invokeExact(caller, name, type,
|
||||
!(info instanceof java.lang.annotation.Annotation[]) ? null
|
||||
: (java.lang.annotation.Annotation[]) info);
|
||||
Object binding;
|
||||
if (false) // switch when invokeGeneric works
|
||||
binding = bootstrapMethod.invokeGeneric(caller, name, type);
|
||||
else
|
||||
throw new InternalError("bad BSM: "+bootstrapMethod);
|
||||
if (!(site instanceof CallSite))
|
||||
throw new InvokeDynamicBootstrapError("class bootstrap method failed to create a call site: "+caller);
|
||||
PRIVATE_INITIALIZE_CALL_SITE.<void>invokeExact(site,
|
||||
name, type,
|
||||
binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type });
|
||||
//System.out.println("BSM for "+name+type+" => "+binding);
|
||||
if (binding instanceof CallSite) {
|
||||
site = (CallSite) binding;
|
||||
} else if (binding instanceof MethodHandleProvider) {
|
||||
MethodHandle target = ((MethodHandleProvider) binding).asMethodHandle();
|
||||
site = new ConstantCallSite(target);
|
||||
} else {
|
||||
throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite");
|
||||
}
|
||||
PRIVATE_INITIALIZE_CALL_SITE.<void>invokeExact(site, name, type,
|
||||
callerMethod, callerBCI);
|
||||
assert(site.getTarget() != null);
|
||||
assert(site.getTarget().type().equals(type));
|
||||
@ -77,11 +81,18 @@ public class CallSiteImpl {
|
||||
|
||||
// This method is private in CallSite because it touches private fields in CallSite.
|
||||
// These private fields (vmmethod, vmindex) are specific to the JVM.
|
||||
private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE =
|
||||
private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE;
|
||||
static {
|
||||
try {
|
||||
PRIVATE_INITIALIZE_CALL_SITE =
|
||||
MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM",
|
||||
MethodType.methodType(void.class,
|
||||
String.class, MethodType.class,
|
||||
MemberName.class, int.class));
|
||||
} catch (NoAccessException ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setCallSiteTarget(Access token, CallSite site, MethodHandle target) {
|
||||
Access.check(token);
|
||||
|
@ -25,12 +25,8 @@
|
||||
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.JavaMethodHandle;
|
||||
import java.dyn.MethodHandle;
|
||||
import java.dyn.MethodType;
|
||||
import java.dyn.NoAccessException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.dyn.*;
|
||||
import java.lang.reflect.*;
|
||||
import static sun.dyn.MemberName.newIllegalArgumentException;
|
||||
|
||||
/**
|
||||
@ -119,7 +115,7 @@ class FilterGeneric {
|
||||
|
||||
static MethodHandle make(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
|
||||
FilterGeneric fgen = of(kind, pos, filter.type(), target.type());
|
||||
return fgen.makeInstance(kind, pos, filter, target);
|
||||
return fgen.makeInstance(kind, pos, filter, target).asMethodHandle();
|
||||
}
|
||||
|
||||
/** Return the adapter information for this target and filter type. */
|
||||
|
@ -25,9 +25,8 @@
|
||||
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.JavaMethodHandle;
|
||||
import java.dyn.MethodHandle;
|
||||
import java.dyn.MethodType;
|
||||
import java.dyn.*;
|
||||
import static sun.dyn.MemberName.uncaughtException;
|
||||
|
||||
/**
|
||||
* Unary function composition, useful for many small plumbing jobs.
|
||||
@ -51,8 +50,16 @@ public class FilterOneArgument extends JavaMethodHandle {
|
||||
return target.invokeExact(filteredArgument);
|
||||
}
|
||||
|
||||
private static final MethodHandle INVOKE =
|
||||
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke", MethodType.genericMethodType(1));
|
||||
private static final MethodHandle INVOKE;
|
||||
static {
|
||||
try {
|
||||
INVOKE =
|
||||
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke",
|
||||
MethodType.genericMethodType(1));
|
||||
} catch (NoAccessException ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected FilterOneArgument(MethodHandle filter, MethodHandle target) {
|
||||
super(INVOKE);
|
||||
|
@ -25,15 +25,9 @@
|
||||
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.JavaMethodHandle;
|
||||
import java.dyn.MethodHandle;
|
||||
import java.dyn.MethodHandles;
|
||||
import java.dyn.MethodType;
|
||||
import java.dyn.NoAccessException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import sun.dyn.util.ValueConversions;
|
||||
import sun.dyn.util.Wrapper;
|
||||
import java.dyn.*;
|
||||
import java.lang.reflect.*;
|
||||
import sun.dyn.util.*;
|
||||
|
||||
/**
|
||||
* Adapters which mediate between incoming calls which are generic
|
||||
|
@ -25,10 +25,7 @@
|
||||
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.MethodHandle;
|
||||
import java.dyn.MethodHandles;
|
||||
import java.dyn.MethodType;
|
||||
|
||||
import java.dyn.*;
|
||||
|
||||
/**
|
||||
* Construction and caching of often-used invokers.
|
||||
@ -63,8 +60,11 @@ public class Invokers {
|
||||
public MethodHandle exactInvoker() {
|
||||
MethodHandle invoker = exactInvoker;
|
||||
if (invoker != null) return invoker;
|
||||
invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType);
|
||||
if (invoker == null) throw new InternalError("JVM cannot find invoker for "+targetType);
|
||||
try {
|
||||
invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType);
|
||||
} catch (NoAccessException ex) {
|
||||
throw new InternalError("JVM cannot find invoker for "+targetType);
|
||||
}
|
||||
assert(invokerType(targetType) == invoker.type());
|
||||
exactInvoker = invoker;
|
||||
return invoker;
|
||||
|
@ -23,8 +23,9 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.dyn;
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.*;
|
||||
import sun.dyn.Access;
|
||||
|
||||
/**
|
||||
@ -168,70 +169,4 @@ public abstract class JavaMethodHandle
|
||||
protected JavaMethodHandle(MethodHandle entryPoint) {
|
||||
super(entryPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a method handle whose entry point is a non-static method
|
||||
* visible in the exact (most specific) class of
|
||||
* the newly constructed object.
|
||||
* <p>
|
||||
* The method is specified by name and type, as if via this expression:
|
||||
* {@code MethodHandles.lookup().findVirtual(this.getClass(), name, type)}.
|
||||
* The class defining the method might be an anonymous inner class.
|
||||
* <p>
|
||||
* The method handle type of {@code this} (i.e, the fully constructed object)
|
||||
* will be the given method handle type.
|
||||
* A call to {@code this} will invoke the selected method.
|
||||
* The receiver argument will be bound to {@code this} on every method
|
||||
* handle invocation.
|
||||
* <p>
|
||||
* <i>Rationale:</i>
|
||||
* Although this constructor may seem to be a mere luxury,
|
||||
* it is not subsumed by the more general constructor which
|
||||
* takes any {@code MethodHandle} as the entry point argument.
|
||||
* In order to convert an entry point name to a method handle,
|
||||
* the self-class of the object is required (in order to do
|
||||
* the lookup). The self-class, in turn, is generally not
|
||||
* available at the time of the constructor invocation,
|
||||
* due to the rules of Java and the JVM verifier.
|
||||
* One cannot call {@code this.getClass()}, because
|
||||
* the value of {@code this} is inaccessible at the point
|
||||
* of the constructor call. (Changing this would require
|
||||
* change to the Java language, verifiers, and compilers.)
|
||||
* In particular, this constructor allows {@code JavaMethodHandle}s
|
||||
* to be created in combination with the anonymous inner class syntax.
|
||||
* @param entryPointName the name of the entry point method
|
||||
* @param type (optional) the desired type of the method handle
|
||||
*/
|
||||
protected JavaMethodHandle(String entryPointName, MethodType type) {
|
||||
super(entryPointName, type, true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a method handle whose entry point is a non-static method
|
||||
* visible in the exact (most specific) class of
|
||||
* the newly constructed object.
|
||||
* <p>
|
||||
* The method is specified only by name.
|
||||
* There must be exactly one method of that name visible in the object class,
|
||||
* either inherited or locally declared.
|
||||
* (That is, the method must not be overloaded.)
|
||||
* <p>
|
||||
* The method handle type of {@code this} (i.e, the fully constructed object)
|
||||
* will be the same as the type of the selected non-static method.
|
||||
* The receiver argument will be bound to {@code this} on every method
|
||||
* handle invocation.
|
||||
* <p>ISSUE: This signature wildcarding feature does not correspond to
|
||||
* any MethodHandles.Lookup API element. Can we eliminate it?
|
||||
* Alternatively, it is useful for naming non-overloaded methods.
|
||||
* Shall we make type arguments optional in the Lookup methods,
|
||||
* throwing an error in cases of ambiguity?
|
||||
* <p>
|
||||
* For this method's rationale, see the documentation
|
||||
* for {@link #JavaMethodHandle(String,MethodType)}.
|
||||
* @param entryPointName the name of the entry point method
|
||||
*/
|
||||
protected JavaMethodHandle(String entryPointName) {
|
||||
super(entryPointName, (MethodType) null, false);
|
||||
}
|
||||
}
|
@ -521,6 +521,11 @@ public final class MemberName implements Member, Cloneable {
|
||||
if (lookupClass != null) message += ", from " + lookupClass.getName();
|
||||
return new NoAccessException(message);
|
||||
}
|
||||
public static Error uncaughtException(Exception ex) {
|
||||
Error err = new InternalError("uncaught exception");
|
||||
err.initCause(ex);
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Actually making a query requires an access check. */
|
||||
public static Factory getFactory(Access token) {
|
||||
@ -641,7 +646,7 @@ public final class MemberName implements Member, Cloneable {
|
||||
* If lookup fails or access is not permitted, a {@linkplain NoAccessException} 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) {
|
||||
public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) throws NoAccessException {
|
||||
MemberName result = resolveOrNull(m, searchSupers, lookupClass);
|
||||
if (result != null)
|
||||
return result;
|
||||
|
@ -25,11 +25,8 @@
|
||||
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.JavaMethodHandle;
|
||||
import java.dyn.MethodHandle;
|
||||
import java.dyn.MethodHandles;
|
||||
import java.dyn.*;
|
||||
import java.dyn.MethodHandles.Lookup;
|
||||
import java.dyn.MethodType;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import sun.dyn.util.VerifyType;
|
||||
@ -46,6 +43,7 @@ import sun.dyn.util.Wrapper;
|
||||
import sun.misc.Unsafe;
|
||||
import static sun.dyn.MemberName.newIllegalArgumentException;
|
||||
import static sun.dyn.MemberName.newNoAccessException;
|
||||
import static sun.dyn.MemberName.uncaughtException;
|
||||
|
||||
/**
|
||||
* Base class for method handles, containing JVM-specific fields and logic.
|
||||
@ -173,7 +171,7 @@ public abstract class MethodHandleImpl {
|
||||
*/
|
||||
public static
|
||||
MethodHandle findMethod(Access token, MemberName method,
|
||||
boolean doDispatch, Class<?> lookupClass) {
|
||||
boolean doDispatch, Class<?> lookupClass) throws NoAccessException {
|
||||
Access.check(token); // only trusted calls
|
||||
MethodType mtype = method.getMethodType();
|
||||
if (!method.isStatic()) {
|
||||
@ -320,7 +318,7 @@ public abstract class MethodHandleImpl {
|
||||
try {
|
||||
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true));
|
||||
} catch (NoAccessException ex) {
|
||||
throw new InternalError("");
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
// Corresponding generic constructor types:
|
||||
@ -416,9 +414,7 @@ public abstract class MethodHandleImpl {
|
||||
f = c.getDeclaredField(field.getName());
|
||||
return unsafe.staticFieldBase(f);
|
||||
} catch (Exception ee) {
|
||||
Error e = new InternalError();
|
||||
e.initCause(ee);
|
||||
throw e;
|
||||
throw uncaughtException(ee);
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,10 +469,8 @@ public abstract class MethodHandleImpl {
|
||||
MethodHandle mh;
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
|
||||
} catch (NoAccessException ee) {
|
||||
Error e = new InternalError("name,type="+name+type);
|
||||
e.initCause(ee);
|
||||
throw e;
|
||||
} catch (NoAccessException ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
if (evclass != vclass || (!isStatic && ecclass != cclass)) {
|
||||
MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
|
||||
@ -543,10 +537,8 @@ public abstract class MethodHandleImpl {
|
||||
MethodHandle mh;
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
|
||||
} catch (NoAccessException ee) {
|
||||
Error e = new InternalError("name,type="+name+type);
|
||||
e.initCause(ee);
|
||||
throw e;
|
||||
} catch (NoAccessException ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
if (caclass != null) {
|
||||
MethodType strongType = FieldAccessor.atype(caclass, isSetter);
|
||||
@ -1031,7 +1023,7 @@ public abstract class MethodHandleImpl {
|
||||
try {
|
||||
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true));
|
||||
} catch (NoAccessException ex) {
|
||||
throw new InternalError("");
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1167,7 +1159,7 @@ public abstract class MethodHandleImpl {
|
||||
try {
|
||||
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
|
||||
} catch (NoAccessException ex) {
|
||||
throw new InternalError("");
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1207,9 +1199,16 @@ public abstract class MethodHandleImpl {
|
||||
return AdapterMethodHandle.makeRetypeRaw(token, type, THROW_EXCEPTION);
|
||||
}
|
||||
|
||||
static final MethodHandle THROW_EXCEPTION
|
||||
static final MethodHandle THROW_EXCEPTION;
|
||||
static {
|
||||
try {
|
||||
THROW_EXCEPTION
|
||||
= IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
|
||||
MethodType.methodType(Empty.class, Throwable.class));
|
||||
} catch (NoAccessException 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) {
|
||||
|
@ -25,9 +25,7 @@
|
||||
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.CallSite;
|
||||
import java.dyn.MethodHandle;
|
||||
import java.dyn.MethodType;
|
||||
import java.dyn.*;
|
||||
import java.dyn.MethodHandles.Lookup;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
@ -324,18 +322,24 @@ class MethodHandleNatives {
|
||||
*/
|
||||
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
|
||||
Class<?> defc, String name, Object type) {
|
||||
Lookup lookup = IMPL_LOOKUP.in(callerClass);
|
||||
switch (refKind) {
|
||||
case REF_getField: return lookup.findGetter( defc, name, (Class<?>) type );
|
||||
case REF_getStatic: return lookup.findStaticGetter( defc, name, (Class<?>) type );
|
||||
case REF_putField: return lookup.findSetter( defc, name, (Class<?>) type );
|
||||
case REF_putStatic: return lookup.findStaticSetter( defc, name, (Class<?>) type );
|
||||
case REF_invokeVirtual: return lookup.findVirtual( defc, name, (MethodType) type );
|
||||
case REF_invokeStatic: return lookup.findStatic( defc, name, (MethodType) type );
|
||||
case REF_invokeSpecial: return lookup.findSpecial( defc, name, (MethodType) type, callerClass );
|
||||
case REF_newInvokeSpecial: return lookup.findConstructor( defc, (MethodType) type );
|
||||
case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type );
|
||||
try {
|
||||
Lookup lookup = IMPL_LOOKUP.in(callerClass);
|
||||
switch (refKind) {
|
||||
case REF_getField: return lookup.findGetter( defc, name, (Class<?>) type );
|
||||
case REF_getStatic: return lookup.findStaticGetter( defc, name, (Class<?>) type );
|
||||
case REF_putField: return lookup.findSetter( defc, name, (Class<?>) type );
|
||||
case REF_putStatic: return lookup.findStaticSetter( defc, name, (Class<?>) type );
|
||||
case REF_invokeVirtual: return lookup.findVirtual( defc, name, (MethodType) type );
|
||||
case REF_invokeStatic: return lookup.findStatic( defc, name, (MethodType) type );
|
||||
case REF_invokeSpecial: return lookup.findSpecial( defc, name, (MethodType) type, callerClass );
|
||||
case REF_newInvokeSpecial: return lookup.findConstructor( defc, (MethodType) type );
|
||||
case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type );
|
||||
}
|
||||
throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type);
|
||||
} catch (NoAccessException ex) {
|
||||
Error err = new IncompatibleClassChangeError();
|
||||
err.initCause(ex);
|
||||
throw err;
|
||||
}
|
||||
throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type);
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,7 @@
|
||||
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.JavaMethodHandle;
|
||||
import java.dyn.MethodHandle;
|
||||
import java.dyn.MethodHandles;
|
||||
import java.dyn.MethodType;
|
||||
import java.dyn.NoAccessException;
|
||||
import java.dyn.*;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -25,11 +25,7 @@
|
||||
|
||||
package sun.dyn;
|
||||
|
||||
import java.dyn.JavaMethodHandle;
|
||||
import java.dyn.MethodHandle;
|
||||
import java.dyn.MethodHandles;
|
||||
import java.dyn.MethodType;
|
||||
import java.dyn.NoAccessException;
|
||||
import java.dyn.*;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import sun.dyn.util.ValueConversions;
|
||||
|
@ -34,6 +34,7 @@ import java.util.List;
|
||||
import sun.dyn.Access;
|
||||
import sun.dyn.AdapterMethodHandle;
|
||||
import sun.dyn.MethodHandleImpl;
|
||||
import static sun.dyn.MemberName.uncaughtException;
|
||||
|
||||
public class ValueConversions {
|
||||
private static final Access IMPL_TOKEN = Access.getToken();
|
||||
@ -148,11 +149,16 @@ public class ValueConversions {
|
||||
// look up the method
|
||||
String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
|
||||
MethodType type = unboxType(wrap, raw);
|
||||
if (!exact)
|
||||
// actually, type is wrong; the Java method takes Object
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
|
||||
else
|
||||
if (!exact) {
|
||||
try {
|
||||
// actually, type is wrong; the Java method takes Object
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
|
||||
} catch (NoAccessException ex) {
|
||||
mh = null;
|
||||
}
|
||||
} else {
|
||||
mh = retype(type, unbox(wrap, !exact, raw));
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
@ -280,10 +286,15 @@ public class ValueConversions {
|
||||
// look up the method
|
||||
String name = "box" + wrap.simpleName() + (raw ? "Raw" : "");
|
||||
MethodType type = boxType(wrap, raw);
|
||||
if (exact)
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
|
||||
else
|
||||
if (exact) {
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
|
||||
} catch (NoAccessException ex) {
|
||||
mh = null;
|
||||
}
|
||||
} else {
|
||||
mh = retype(type.erase(), box(wrap, !exact, raw));
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
@ -394,10 +405,15 @@ public class ValueConversions {
|
||||
// look up the method
|
||||
String name = "reboxRaw" + wrap.simpleName();
|
||||
MethodType type = reboxType(wrap);
|
||||
if (exact)
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
|
||||
else
|
||||
if (exact) {
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
|
||||
} catch (NoAccessException ex) {
|
||||
mh = null;
|
||||
}
|
||||
} else {
|
||||
mh = retype(IDENTITY.type(), rebox(wrap, !exact));
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
@ -474,7 +490,11 @@ public class ValueConversions {
|
||||
mh = EMPTY;
|
||||
break;
|
||||
case INT: case LONG: case FLOAT: case DOUBLE:
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
|
||||
} catch (NoAccessException ex) {
|
||||
mh = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (mh != null) {
|
||||
@ -549,8 +569,8 @@ public class ValueConversions {
|
||||
ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType);
|
||||
IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType);
|
||||
EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1));
|
||||
} catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
164
jdk/test/java/dyn/ClassValueTest.java
Normal file
164
jdk/test/java/dyn/ClassValueTest.java
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @summary tests for class-specific values
|
||||
* @compile ClassValueTest.java
|
||||
* @run junit/othervm test.java.dyn.ClassValueTest
|
||||
*/
|
||||
|
||||
/*
|
||||
Manually:
|
||||
$ $JAVA7X_HOME/bin/javac -d foo -cp $JUNIT4_JAR test/java/dyn/ClassValueTest.java
|
||||
$ $JAVA7X_HOME/bin/java -cp foo:$JUNIT4_JAR org.junit.runner.JUnitCore test.java.dyn.ClassValueTest
|
||||
Output: .testAdd => 1000 : Integer
|
||||
*/
|
||||
|
||||
package test.java.dyn;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import java.dyn.*;
|
||||
|
||||
import org.junit.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author jrose
|
||||
*/
|
||||
public class ClassValueTest {
|
||||
static String nameForCV1(Class<?> type) {
|
||||
return "CV1:" + type.getName();
|
||||
}
|
||||
static int countForCV1;
|
||||
static final ClassValue<String> CV1 = new ClassValue<String>() {
|
||||
protected String computeValue(Class<?> type) {
|
||||
countForCV1++;
|
||||
return nameForCV1(type);
|
||||
}
|
||||
};
|
||||
|
||||
static final Class[] CLASSES = {
|
||||
String.class,
|
||||
Integer.class,
|
||||
int.class,
|
||||
boolean[].class,
|
||||
char[][].class,
|
||||
ClassValueTest.class
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testGet() {
|
||||
countForCV1 = 0;
|
||||
for (Class c : CLASSES) {
|
||||
assertEquals(nameForCV1(c), CV1.get(c));
|
||||
}
|
||||
assertEquals(CLASSES.length, countForCV1);
|
||||
for (Class c : CLASSES) {
|
||||
assertEquals(nameForCV1(c), CV1.get(c));
|
||||
}
|
||||
assertEquals(CLASSES.length, countForCV1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove() {
|
||||
for (Class c : CLASSES) {
|
||||
CV1.get(c);
|
||||
}
|
||||
countForCV1 = 0;
|
||||
int REMCOUNT = 3;
|
||||
for (int i = 0; i < REMCOUNT; i++) {
|
||||
CV1.remove(CLASSES[i]);
|
||||
}
|
||||
assertEquals(0, countForCV1); // no change
|
||||
for (Class c : CLASSES) {
|
||||
assertEquals(nameForCV1(c), CV1.get(c));
|
||||
}
|
||||
assertEquals(REMCOUNT, countForCV1);
|
||||
}
|
||||
|
||||
static String nameForCVN(Class<?> type, int n) {
|
||||
return "CV[" + n + "]" + type.getName();
|
||||
}
|
||||
static int countForCVN;
|
||||
static class CVN extends ClassValue<String> {
|
||||
final int n;
|
||||
CVN(int n) { this.n = n; }
|
||||
protected String computeValue(Class<?> type) {
|
||||
countForCVN++;
|
||||
return nameForCVN(type, n);
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testGetMany() {
|
||||
int CVN_COUNT1 = 100, CVN_COUNT2 = 100;
|
||||
CVN cvns[] = new CVN[CVN_COUNT1 * CVN_COUNT2];
|
||||
for (int n = 0; n < cvns.length; n++) {
|
||||
cvns[n] = new CVN(n);
|
||||
}
|
||||
countForCVN = 0;
|
||||
for (int pass = 0; pass <= 2; pass++) {
|
||||
for (int i1 = 0; i1 < CVN_COUNT1; i1++) {
|
||||
eachClass:
|
||||
for (Class c : CLASSES) {
|
||||
for (int i2 = 0; i2 < CVN_COUNT2; i2++) {
|
||||
int n = i1*CVN_COUNT2 + i2;
|
||||
assertEquals(0, countForCVN);
|
||||
assertEquals(nameForCVN(c, n), cvns[n].get(c));
|
||||
cvns[n].get(c); //get it again
|
||||
//System.out.println("getting "+n+":"+cvns[n].get(c));
|
||||
boolean doremove = (((i1 + i2) & 3) == 0);
|
||||
switch (pass) {
|
||||
case 0:
|
||||
assertEquals(1, countForCVN);
|
||||
break;
|
||||
case 1:
|
||||
// remove on middle pass
|
||||
assertEquals(0, countForCVN);
|
||||
if (doremove) {
|
||||
//System.out.println("removing "+n+":"+cvns[n].get(c));
|
||||
cvns[n].remove(c);
|
||||
assertEquals(0, countForCVN);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
assertEquals(doremove ? 1 : 0, countForCVN);
|
||||
break;
|
||||
}
|
||||
countForCVN = 0;
|
||||
if (i1 > i2 && i1 < i2+5) continue eachClass; // leave diagonal gap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(countForCVN, 0);
|
||||
for (int n = 0; n < cvns.length; n++) {
|
||||
for (Class c : CLASSES) {
|
||||
assertEquals(nameForCVN(c, n), cvns[n].get(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
128
jdk/test/java/dyn/JavaDocExamples.java
Normal file
128
jdk/test/java/dyn/JavaDocExamples.java
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 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.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @summary example code used in javadoc for java.dyn API
|
||||
* @compile -XDallowTransitionalJSR292=no JavaDocExamples.java
|
||||
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.JavaDocExamples
|
||||
*/
|
||||
|
||||
/*
|
||||
---- To run outside jtreg:
|
||||
$ $JAVA7X_HOME/bin/javac -cp $JUNIT4_JAR -d /tmp/Classes \
|
||||
$DAVINCI/sources/jdk/test/java/dyn/JavaDocExamples.java
|
||||
$ $JAVA7X_HOME/bin/java -cp $JUNIT4_JAR:/tmp/Classes \
|
||||
-XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles \
|
||||
-Dtest.java.dyn.JavaDocExamples.verbosity=1 \
|
||||
test.java.dyn.JavaDocExamples
|
||||
----
|
||||
*/
|
||||
|
||||
package test.java.dyn;
|
||||
|
||||
import java.dyn.*;
|
||||
import static java.dyn.MethodHandles.*;
|
||||
import static java.dyn.MethodType.*;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author jrose
|
||||
*/
|
||||
public class JavaDocExamples {
|
||||
/** Wrapper for running the JUnit tests in this module.
|
||||
* Put JUnit on the classpath!
|
||||
*/
|
||||
public static void main(String... ignore) {
|
||||
org.junit.runner.JUnitCore.runClasses(JavaDocExamples.class);
|
||||
}
|
||||
// How much output?
|
||||
static int verbosity = Integer.getInteger("test.java.dyn.JavaDocExamples.verbosity", 0);
|
||||
|
||||
{}
|
||||
static final private Lookup LOOKUP = lookup();
|
||||
// static final private MethodHandle CONCAT_1 = LOOKUP.findVirtual(String.class,
|
||||
// "concat", methodType(String.class, String.class));
|
||||
// static final private MethodHandle HASHCODE_1 = LOOKUP.findVirtual(Object.class,
|
||||
// "hashCode", methodType(int.class));
|
||||
|
||||
// form required if NoAccessException is intercepted:
|
||||
static final private MethodHandle CONCAT_2, HASHCODE_2;
|
||||
static {
|
||||
try {
|
||||
CONCAT_2 = LOOKUP.findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
HASHCODE_2 = LOOKUP.findVirtual(Object.class,
|
||||
"hashCode", methodType(int.class));
|
||||
} catch (NoAccessException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@Test public void testFindVirtual() throws Throwable {
|
||||
{}
|
||||
MethodHandle CONCAT_3 = LOOKUP.findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
MethodHandle HASHCODE_3 = LOOKUP.findVirtual(Object.class,
|
||||
"hashCode", methodType(int.class));
|
||||
//assertEquals("xy", (String) CONCAT_1.invokeExact("x", "y"));
|
||||
assertEquals("xy", (String) CONCAT_2.<String>invokeExact("x", "y"));
|
||||
assertEquals("xy", (String) CONCAT_3.<String>invokeExact("x", "y"));
|
||||
//assertEquals("xy".hashCode(), (int) HASHCODE_1.<int>invokeExact((Object)"xy"));
|
||||
assertEquals("xy".hashCode(), (int) HASHCODE_2.<int>invokeExact((Object)"xy"));
|
||||
assertEquals("xy".hashCode(), (int) HASHCODE_3.<int>invokeExact((Object)"xy"));
|
||||
{}
|
||||
}
|
||||
@Test public void testDropArguments() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
cat = cat.asType(methodType(Object.class, String.class, String.class)); /*(String)*/
|
||||
assertEquals("xy", /*(String)*/ cat.invokeExact("x", "y"));
|
||||
MethodHandle d0 = dropArguments(cat, 0, String.class);
|
||||
assertEquals("yz", /*(String)*/ d0.invokeExact("x", "y", "z"));
|
||||
MethodHandle d1 = dropArguments(cat, 1, String.class);
|
||||
assertEquals("xz", /*(String)*/ d1.invokeExact("x", "y", "z"));
|
||||
MethodHandle d2 = dropArguments(cat, 2, String.class);
|
||||
assertEquals("xy", /*(String)*/ d2.invokeExact("x", "y", "z"));
|
||||
MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
|
||||
assertEquals("xz", /*(String)*/ d12.invokeExact("x", 12, true, "z"));
|
||||
}}
|
||||
}
|
||||
|
||||
static void assertEquals(Object exp, Object act) {
|
||||
if (verbosity > 0)
|
||||
System.out.println("result: "+act);
|
||||
Assert.assertEquals(exp, act);
|
||||
}
|
||||
}
|
@ -265,6 +265,12 @@ public class MethodHandlesTest {
|
||||
// wrap = Wrapper.forWrapperType(dst);
|
||||
// if (wrap != Wrapper.OBJECT)
|
||||
// return wrap.wrap(nextArg++);
|
||||
if (param.isInterface()) {
|
||||
for (Class<?> c : param.getClasses()) {
|
||||
if (param.isAssignableFrom(c) && !c.isInterface())
|
||||
{ param = c; break; }
|
||||
}
|
||||
}
|
||||
if (param.isInterface() || param.isAssignableFrom(String.class))
|
||||
return "#"+nextArg();
|
||||
else
|
||||
@ -380,7 +386,7 @@ public class MethodHandlesTest {
|
||||
}
|
||||
public static interface IntExample {
|
||||
public void v0();
|
||||
static class Impl implements IntExample {
|
||||
public static class Impl implements IntExample {
|
||||
public void v0() { called("Int/v0", this); }
|
||||
final String name;
|
||||
public Impl() { name = "Impl#"+nextArg(); }
|
||||
@ -449,7 +455,7 @@ public class MethodHandlesTest {
|
||||
countTest(positive);
|
||||
MethodType type = MethodType.methodType(ret, params);
|
||||
MethodHandle target = null;
|
||||
RuntimeException noAccess = null;
|
||||
Exception noAccess = null;
|
||||
try {
|
||||
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
|
||||
target = lookup.findStatic(defc, name, type);
|
||||
@ -513,7 +519,7 @@ public class MethodHandlesTest {
|
||||
String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
|
||||
MethodType type = MethodType.methodType(ret, params);
|
||||
MethodHandle target = null;
|
||||
RuntimeException noAccess = null;
|
||||
Exception noAccess = null;
|
||||
try {
|
||||
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
|
||||
target = lookup.findVirtual(defc, methodName, type);
|
||||
@ -567,7 +573,7 @@ public class MethodHandlesTest {
|
||||
countTest(positive);
|
||||
MethodType type = MethodType.methodType(ret, params);
|
||||
MethodHandle target = null;
|
||||
RuntimeException noAccess = null;
|
||||
Exception noAccess = null;
|
||||
try {
|
||||
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
|
||||
target = lookup.findSpecial(defc, name, type, specialCaller);
|
||||
@ -623,7 +629,7 @@ public class MethodHandlesTest {
|
||||
MethodType type = MethodType.methodType(ret, params);
|
||||
Object receiver = randomArg(defc);
|
||||
MethodHandle target = null;
|
||||
RuntimeException noAccess = null;
|
||||
Exception noAccess = null;
|
||||
try {
|
||||
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
|
||||
target = lookup.bind(receiver, methodName, type);
|
||||
@ -688,7 +694,7 @@ public class MethodHandlesTest {
|
||||
MethodType type = MethodType.methodType(ret, params);
|
||||
Method rmethod = null;
|
||||
MethodHandle target = null;
|
||||
RuntimeException noAccess = null;
|
||||
Exception noAccess = null;
|
||||
try {
|
||||
rmethod = defc.getDeclaredMethod(name, params);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
@ -1088,7 +1094,11 @@ public class MethodHandlesTest {
|
||||
if (rtype != Object.class)
|
||||
pfx = rtype.getSimpleName().substring(0, 1).toLowerCase();
|
||||
String name = pfx+"id";
|
||||
return PRIVATE.findStatic(Callee.class, name, type);
|
||||
try {
|
||||
return PRIVATE.findStatic(Callee.class, name, type);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1327,7 +1337,8 @@ public class MethodHandlesTest {
|
||||
MethodHandle result = MethodHandles.spreadArguments(target2, newType);
|
||||
Object[] returnValue;
|
||||
if (pos == 0) {
|
||||
returnValue = (Object[]) result.invokeExact(args);
|
||||
Object rawRetVal = result.invokeExact(args);
|
||||
returnValue = (Object[]) rawRetVal;
|
||||
} else {
|
||||
Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
|
||||
args1[pos] = Arrays.copyOfRange(args, pos, args.length);
|
||||
@ -1817,8 +1828,13 @@ public class MethodHandlesTest {
|
||||
testCastFailure("unbox/return", 11000);
|
||||
}
|
||||
|
||||
static class Surprise extends JavaMethodHandle {
|
||||
Surprise() { super("value"); }
|
||||
static class Surprise implements MethodHandleProvider {
|
||||
public MethodHandle asMethodHandle() {
|
||||
return VALUE.bindTo(this);
|
||||
}
|
||||
public MethodHandle asMethodHandle(MethodType type) {
|
||||
return asMethodHandle().asType(type);
|
||||
}
|
||||
Object value(Object x) {
|
||||
trace("value", x);
|
||||
if (boo != null) return boo;
|
||||
@ -1833,22 +1849,32 @@ public class MethodHandlesTest {
|
||||
static Object refIdentity(Object x) { trace("ref.x", x); return x; }
|
||||
static Integer boxIdentity(Integer x) { trace("box.x", x); return x; }
|
||||
static int intIdentity(int x) { trace("int.x", x); return x; }
|
||||
static MethodHandle REF_IDENTITY = PRIVATE.findStatic(
|
||||
Surprise.class, "refIdentity",
|
||||
MethodType.methodType(Object.class, Object.class));
|
||||
static MethodHandle BOX_IDENTITY = PRIVATE.findStatic(
|
||||
Surprise.class, "boxIdentity",
|
||||
MethodType.methodType(Integer.class, Integer.class));
|
||||
static MethodHandle INT_IDENTITY = PRIVATE.findStatic(
|
||||
Surprise.class, "intIdentity",
|
||||
MethodType.methodType(int.class, int.class));
|
||||
static MethodHandle VALUE, REF_IDENTITY, BOX_IDENTITY, INT_IDENTITY;
|
||||
static {
|
||||
try {
|
||||
VALUE = PRIVATE.findVirtual(
|
||||
Surprise.class, "value",
|
||||
MethodType.methodType(Object.class, Object.class));
|
||||
REF_IDENTITY = PRIVATE.findStatic(
|
||||
Surprise.class, "refIdentity",
|
||||
MethodType.methodType(Object.class, Object.class));
|
||||
BOX_IDENTITY = PRIVATE.findStatic(
|
||||
Surprise.class, "boxIdentity",
|
||||
MethodType.methodType(Integer.class, Integer.class));
|
||||
INT_IDENTITY = PRIVATE.findStatic(
|
||||
Surprise.class, "intIdentity",
|
||||
MethodType.methodType(int.class, int.class));
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testCastFailure(String mode, int okCount) throws Throwable {
|
||||
countTest(false);
|
||||
if (verbosity > 2) System.out.println("mode="+mode);
|
||||
Surprise boo = new Surprise();
|
||||
MethodHandle identity = Surprise.REF_IDENTITY, surprise = boo;
|
||||
MethodHandle identity = Surprise.REF_IDENTITY, surprise0 = boo.asMethodHandle(), surprise = surprise0;
|
||||
if (mode.endsWith("/return")) {
|
||||
if (mode.equals("unbox/return")) {
|
||||
// fail on return to ((Integer)surprise).intValue
|
||||
@ -1874,7 +1900,7 @@ public class MethodHandlesTest {
|
||||
identity = MethodHandles.filterArguments(callee, identity);
|
||||
}
|
||||
}
|
||||
assertNotSame(mode, surprise, boo);
|
||||
assertNotSame(mode, surprise, surprise0);
|
||||
identity = MethodHandles.convertArguments(identity, MethodType.genericMethodType(1));
|
||||
surprise = MethodHandles.convertArguments(surprise, MethodType.genericMethodType(1));
|
||||
Object x = 42;
|
||||
@ -1936,6 +1962,107 @@ public class MethodHandlesTest {
|
||||
mh.invokeVarargs(args);
|
||||
assertCalled(name, args);
|
||||
}
|
||||
|
||||
static void runForRunnable() {
|
||||
called("runForRunnable");
|
||||
}
|
||||
private interface Fooable {
|
||||
Object foo(Fooable x, Object y);
|
||||
// this is for randomArg:
|
||||
public class Impl implements Fooable {
|
||||
public Object foo(Fooable x, Object y) {
|
||||
throw new RuntimeException("do not call");
|
||||
}
|
||||
final String name;
|
||||
public Impl() { name = "Fooable#"+nextArg(); }
|
||||
@Override public String toString() { return name; }
|
||||
}
|
||||
}
|
||||
static Object fooForFooable(Fooable x, Object y) {
|
||||
return called("fooForFooable", x, y);
|
||||
}
|
||||
private static class MyCheckedException extends Exception {
|
||||
}
|
||||
private interface WillThrow {
|
||||
void willThrow() throws MyCheckedException;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsInstance() throws Throwable {
|
||||
if (CAN_SKIP_WORKING) return;
|
||||
Lookup lookup = MethodHandles.lookup();
|
||||
{
|
||||
MethodType mt = MethodType.methodType(void.class);
|
||||
MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "runForRunnable", mt);
|
||||
Runnable proxy = MethodHandles.asInstance(mh, Runnable.class);
|
||||
proxy.run();
|
||||
assertCalled("runForRunnable");
|
||||
}
|
||||
{
|
||||
MethodType mt = MethodType.methodType(Object.class, Fooable.class, Object.class);
|
||||
MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "fooForFooable", mt);
|
||||
Fooable proxy = MethodHandles.asInstance(mh, Fooable.class);
|
||||
Object[] args = randomArgs(mt.parameterArray());
|
||||
Object result = proxy.foo((Fooable) args[0], args[1]);
|
||||
assertCalled("fooForFooable", args);
|
||||
assertEquals(result, logEntry("fooForFooable", args));
|
||||
}
|
||||
for (Throwable ex : new Throwable[] { new NullPointerException("ok"),
|
||||
new InternalError("ok"),
|
||||
new Throwable("fail"),
|
||||
new Exception("fail"),
|
||||
new MyCheckedException()
|
||||
}) {
|
||||
MethodHandle mh = MethodHandles.throwException(void.class, Throwable.class);
|
||||
mh = MethodHandles.insertArguments(mh, 0, ex);
|
||||
WillThrow proxy = MethodHandles.asInstance(mh, WillThrow.class);
|
||||
try {
|
||||
proxy.willThrow();
|
||||
System.out.println("Failed to throw: "+ex);
|
||||
assertTrue(false);
|
||||
} catch (Throwable ex1) {
|
||||
if (verbosity > 2) {
|
||||
System.out.println("throw "+ex);
|
||||
System.out.println("catch "+(ex == ex1 ? "UNWRAPPED" : ex1));
|
||||
}
|
||||
if (ex instanceof RuntimeException ||
|
||||
ex instanceof Error) {
|
||||
assertSame("must pass unchecked exception out without wrapping", ex, ex1);
|
||||
} else if (ex instanceof MyCheckedException) {
|
||||
assertSame("must pass declared exception out without wrapping", ex, ex1);
|
||||
} else {
|
||||
assertNotSame("must pass undeclared checked exception with wrapping", ex, ex1);
|
||||
UndeclaredThrowableException utex = (UndeclaredThrowableException) ex1;
|
||||
assertSame(ex, utex.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
System.out.println("Failed to throw");
|
||||
assertTrue(false);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Local abbreviated copy of sun.dyn.util.ValueConversions
|
||||
class ValueConversions {
|
||||
|
Loading…
x
Reference in New Issue
Block a user