提交 5150d263 编写于 作者: J jrose

6981777: implement JSR 292 EG adjustments from summer 2010

Reviewed-by: twisti
上级 70e0218c
/*
* 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>
&#064;BootstrapMethod(value=MyLanguageRuntime.class, name="bootstrapDynamic")
String x = (String) InvokeDynamic.greet();
//BSM => MyLanguageRuntime.bootstrapDynamic(Here.class, "greet", methodType(String.class))
&#064;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};
}
...@@ -26,40 +26,34 @@ ...@@ -26,40 +26,34 @@
package java.dyn; package java.dyn;
import sun.dyn.*; import sun.dyn.*;
import sun.dyn.empty.Empty;
import sun.misc.Unsafe;
import java.util.Collection; import java.util.Collection;
/** /**
* A {@code CallSite} is a holder for a variable {@link MethodHandle}, * A {@code CallSite} is a holder for a variable {@link MethodHandle},
* which is called its {@code target}. * which is called its {@code target}.
* Every call to a {@code CallSite} is delegated to the site's current target. * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
* all calls to the site's current target.
* <p> * <p>
* A call site is initially created in an <em>unlinked</em> state, * If a mutable target is not required, the {@code invokedynamic} instruction
* which is distinguished by a null target variable. * should be linked to a {@linkplain ConstantCallSite constant call site}.
* Before the call site may be invoked (and before certain other * If a volatile target is required, because updates to the target must be
* operations are attempted), the call site must be linked to * reliably witnessed by other threads, the {@code invokedynamic} instruction
* a non-null target. * should be linked to a {@linkplain VolatileCallSite volatile call site}.
* <p> * <p>
* A call site may be <em>relinked</em> by changing its target. * A call site may be <em>relinked</em> by changing its target.
* The new target must be non-null and must have the same * The new target must have the same {@linkplain MethodHandle#type() type}
* {@linkplain MethodHandle#type() type}
* as the previous target. * as the previous target.
* Thus, though a call site can be relinked to a series of * Thus, though a call site can be relinked to a series of
* successive targets, it cannot change its type. * successive targets, it cannot change its type.
* <p> * <p>
* Linkage happens once in the lifetime of any given {@code CallSite} object.
* Because of call site invalidation, this linkage can be repeated for
* a single {@code invokedynamic} instruction, with multiple {@code CallSite} objects.
* When a {@code CallSite} is unlinked from an {@code invokedynamic} instruction,
* the instruction is reset so that it is no longer associated with
* the {@code CallSite} object, but the {@code CallSite} does not change
* state.
* <p>
* Here is a sample use of call sites and bootstrap methods which links every * Here is a sample use of call sites and bootstrap methods which links every
* dynamic call site to print its arguments: * dynamic call site to print its arguments:
<blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java --> <blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java -->
&#064;BootstrapMethod(value=PrintArgsDemo.class, name="bootstrapDynamic")
static void test() throws Throwable { static void test() throws Throwable {
InvokeDynamic.baz("baz arg", 2, 3.14); // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
} }
private static void printArgs(Object... args) { private static void printArgs(Object... args) {
System.out.println(java.util.Arrays.deepToString(args)); System.out.println(java.util.Arrays.deepToString(args));
...@@ -71,16 +65,14 @@ static { ...@@ -71,16 +65,14 @@ static {
printArgs = lookup.findStatic(thisClass, printArgs = lookup.findStatic(thisClass,
"printArgs", MethodType.methodType(void.class, Object[].class)); "printArgs", MethodType.methodType(void.class, Object[].class));
} }
private static CallSite bootstrapDynamic(Class caller, String name, MethodType type) { private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
// ignore caller and name, but match the type: // ignore caller and name, but match the type:
return new CallSite(MethodHandles.collectArguments(printArgs, type)); return new ConstantCallSite(MethodHandles.collectArguments(printArgs, type));
} }
</pre></blockquote> </pre></blockquote>
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
public class CallSite public class CallSite {
implements MethodHandleProvider
{
private static final Access IMPL_TOKEN = Access.getToken(); private static final Access IMPL_TOKEN = Access.getToken();
// Fields used only by the JVM. Do not use or change. // Fields used only by the JVM. Do not use or change.
...@@ -88,61 +80,44 @@ public class CallSite ...@@ -88,61 +80,44 @@ public class CallSite
private int vmindex; // supplied by the JVM (BCI within calling method) private int vmindex; // supplied by the JVM (BCI within calling method)
// The actual payload of this call site: // The actual payload of this call site:
private MethodHandle target; /*package-private*/
MethodHandle target;
// Remove this field for PFD and delete deprecated methods: // Remove this field for PFD and delete deprecated methods:
private MemberName calleeNameRemoveForPFD; private MemberName calleeNameRemoveForPFD;
/** /**
* Make a blank call site object. * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* Before it is returned from a bootstrap method, this {@code CallSite} object * Make a blank call site object with the given method type.
* must be provided with * An initial target method is supplied which will throw
* a target method via a call to {@link CallSite#setTarget(MethodHandle) setTarget}, * an {@link IllegalStateException} if called.
* or by a subclass override of {@link CallSite#initialTarget(Class,String,MethodType) initialTarget}. * <p>
* Before this {@code CallSite} object is returned from a bootstrap method,
* it is usually provided with a more useful target method,
* via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
*/ */
public CallSite() { public CallSite(MethodType type) {
target = MethodHandles.invokers(type).uninitializedCallSite();
} }
/** /**
* Make a blank call site object, possibly equipped with an initial target method handle. * Make a blank call site object, possibly equipped with an initial target method handle.
* The initial target reference may be null, in which case the {@code CallSite} object * @param target the method handle which will be the initial target of the call site
* must be provided with a target method via a call to {@link CallSite#setTarget},
* or by a subclass override of {@link CallSite#initialTarget}.
* @param target the method handle which will be the initial target of the call site, or null if there is none yet
*/ */
public CallSite(MethodHandle target) { public CallSite(MethodHandle target) {
target.type(); // null check
this.target = target; this.target = target;
} }
/** @deprecated transitional form defined in EDR but removed in PFD */ /**
public CallSite(Class<?> caller, String name, MethodType type) { * Report the type of this call site's target.
this.calleeNameRemoveForPFD = new MemberName(caller, name, type); * Although targets may change, the call site's type can never change.
} * The {@code setTarget} method enforces this invariant by refusing any new target that does
/** @deprecated transitional form defined in EDR but removed in PFD */ * not have the previous target's type.
public Class<?> callerClass() { * @return the type of the current target, which is also the type of any future target
MemberName callee = this.calleeNameRemoveForPFD;
return callee == null ? null : callee.getDeclaringClass();
}
/** @deprecated transitional form defined in EDR but removed in PFD */
public String name() {
MemberName callee = this.calleeNameRemoveForPFD;
return callee == null ? null : callee.getName();
}
/** @deprecated transitional form defined in EDR but removed in PFD */
public MethodType type() {
MemberName callee = this.calleeNameRemoveForPFD;
return callee == null ? (target == null ? null : target.type()) : callee.getMethodType();
}
/** @deprecated transitional form defined in EDR but removed in PFD */
protected MethodHandle initialTarget() {
return initialTarget(callerClass(), name(), type());
}
/** Report if the JVM has linked this {@code CallSite} object to a dynamic call site instruction.
* Once it is linked, it is never unlinked.
*/ */
private boolean isLinked() { public MethodType type() {
return vmmethod != null; return target.type();
} }
/** Called from JVM (or low-level Java code) after the BSM returns the newly created CallSite. /** Called from JVM (or low-level Java code) after the BSM returns the newly created CallSite.
...@@ -152,68 +127,66 @@ public class CallSite ...@@ -152,68 +127,66 @@ public class CallSite
MethodType type, MethodType type,
MemberName callerMethod, MemberName callerMethod,
int callerBCI) { int callerBCI) {
if (this.isLinked()) { if (this.vmmethod != null) {
// FIXME
throw new InvokeDynamicBootstrapError("call site has already been linked to an invokedynamic instruction"); throw new InvokeDynamicBootstrapError("call site has already been linked to an invokedynamic instruction");
} }
MethodHandle target = this.target; if (!this.type().equals(type)) {
if (target == null) {
this.target = target = this.initialTarget(callerMethod.getDeclaringClass(), name, type);
}
if (!target.type().equals(type)) {
throw wrongTargetType(target, type); throw wrongTargetType(target, type);
} }
this.vmindex = callerBCI; this.vmindex = callerBCI;
this.vmmethod = callerMethod; this.vmmethod = callerMethod;
assert(this.isLinked());
} }
/** /**
* Just after a call site is created by a bootstrap method handle, * Report the current linkage state of the call site, a value which may change over time.
* if the target has not been initialized by the factory method itself,
* the method {@code initialTarget} is called to produce an initial
* non-null target. (Live call sites must never have null targets.)
* <p>
* The arguments are the same as those passed to the bootstrap method.
* Thus, a bootstrap method is free to ignore the arguments and simply
* create a "blank" {@code CallSite} object of an appropriate subclass.
* <p> * <p>
* If the bootstrap method itself does not initialize the call site, * If a {@code CallSite} object is returned
* this method must be overridden, because it just raises an * from the bootstrap method of the {@code invokedynamic} instruction,
* {@code InvokeDynamicBootstrapError}, which in turn causes the * the {@code CallSite} is permanently bound to that instruction.
* linkage of the {@code invokedynamic} instruction to terminate * When the {@code invokedynamic} instruction is executed, the target method
* abnormally. * of its associated call site object is invoked directly.
* @deprecated transitional form defined in EDR but removed in PFD * It is as if the instruction calls {@code getTarget} and then
*/ * calls {@link MethodHandle#invokeExact invokeExact} on the result.
protected MethodHandle initialTarget(Class<?> callerClass, String name, MethodType type) {
throw new InvokeDynamicBootstrapError("target must be initialized before call site is linked: "+name+type);
}
/**
* Report the current linkage state of the call site. (This is mutable.)
* The value may not be null after the {@code CallSite} object is returned
* from the bootstrap method of the {@code invokedynamic} instruction.
* When an {@code invokedynamic} instruction is executed, the target method
* of its associated {@code call site} object is invoked directly,
* as if via {@link MethodHandle}{@code .invoke}.
* <p> * <p>
* The interactions of {@code getTarget} with memory are the same * Unless specified differently by a subclass,
* the interactions of {@code getTarget} with memory are the same
* as of a read from an ordinary variable, such as an array element or a * as of a read from an ordinary variable, such as an array element or a
* non-volatile, non-final field. * non-volatile, non-final field.
* <p> * <p>
* In particular, the current thread may choose to reuse the result * In particular, the current thread may choose to reuse the result
* of a previous read of the target from memory, and may fail to see * of a previous read of the target from memory, and may fail to see
* a recent update to the target by another thread. * a recent update to the target by another thread.
* @return the current linkage state of the call site * <p>
* In a {@linkplain ConstantCallSite constant call site}, the {@code getTarget} method behaves
* like a read from a {@code final} field of the {@code CallSite}.
* <p>
* In a {@linkplain VolatileCallSite volatile call site}, the {@code getTarget} method behaves
* like a read from a {@code volatile} field of the {@code CallSite}.
* <p>
* This method may not be overridden by application code.
* @return the current linkage state of the call site, its target method handle
* @see ConstantCallSite
* @see VolatileCallSite
* @see #setTarget * @see #setTarget
*/ */
public MethodHandle getTarget() { public final MethodHandle getTarget() {
return getTarget0();
}
/**
* Privileged implementations can override this to force final or volatile semantics on getTarget.
*/
/*package-private*/
MethodHandle getTarget0() {
return target; return target;
} }
/** /**
* Set the target method of this call site. * Set the target method of this call site.
* <p> * <p>
* The interactions of {@code setTarget} with memory are the same * Unless a subclass of CallSite documents otherwise,
* the interactions of {@code setTarget} with memory are the same
* as of a write to an ordinary variable, such as an array element or a * as of a write to an ordinary variable, such as an array element or a
* non-volatile, non-final field. * non-volatile, non-final field.
* <p> * <p>
...@@ -224,27 +197,23 @@ public class CallSite ...@@ -224,27 +197,23 @@ public class CallSite
* at any given call site. * at any given call site.
* @param newTarget the new target * @param newTarget the new target
* @throws NullPointerException if the proposed new target is null * @throws NullPointerException if the proposed new target is null
* @throws WrongMethodTypeException if the call site is linked and the proposed new target * @throws WrongMethodTypeException if the proposed new target
* has a method type that differs from the previous target * has a method type that differs from the previous target
*/ */
public void setTarget(MethodHandle newTarget) { public void setTarget(MethodHandle newTarget) {
MethodType newType = newTarget.type(); // null check! checkTargetChange(this.target, newTarget);
MethodHandle oldTarget = this.target; setTargetNormal(newTarget);
if (oldTarget == null) { }
// CallSite is not yet linked.
assert(!isLinked()); void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
this.target = newTarget; // might be null!
return;
}
MethodType oldType = oldTarget.type(); MethodType oldType = oldTarget.type();
if (!newTarget.type().equals(oldType)) MethodType newType = newTarget.type(); // null check!
if (!newType.equals(oldType))
throw wrongTargetType(newTarget, oldType); throw wrongTargetType(newTarget, oldType);
if (oldTarget != newTarget)
CallSiteImpl.setCallSiteTarget(IMPL_TOKEN, this, newTarget);
} }
private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) { private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
return new WrongMethodTypeException(String.valueOf(target)+target.type()+" should be of type "+type); return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
} }
/** Produce a printed representation that displays information about this call site /** Produce a printed representation that displays information about this call site
...@@ -252,15 +221,14 @@ public class CallSite ...@@ -252,15 +221,14 @@ public class CallSite
*/ */
@Override @Override
public String toString() { public String toString() {
return "CallSite"+(target == null ? "" : target.type()); return super.toString() + type();
} }
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* Produce a method handle equivalent to an invokedynamic instruction * Produce a method handle equivalent to an invokedynamic instruction
* which has been linked to this call site. * which has been linked to this call site.
* <p>If this call site is a {@link ConstantCallSite}, this method * <p>If this call site is a {@linkplain ConstantCallSite constant call site},
* simply returns the call site's target, since that will not change. * this method simply returns the call site's target, since that will never change.
* <p>Otherwise, this method is equivalent to the following code: * <p>Otherwise, this method is equivalent to the following code:
* <p><blockquote><pre> * <p><blockquote><pre>
* MethodHandle getTarget, invoker, result; * MethodHandle getTarget, invoker, result;
...@@ -271,8 +239,9 @@ public class CallSite ...@@ -271,8 +239,9 @@ public class CallSite
* @return a method handle which always invokes this call site's current target * @return a method handle which always invokes this call site's current target
*/ */
public final MethodHandle dynamicInvoker() { public final MethodHandle dynamicInvoker() {
if (this instanceof ConstantCallSite) if (this instanceof ConstantCallSite) {
return getTarget(); // will not change dynamically return target; // will not change dynamically
}
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this); MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type()); MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget); return MethodHandles.foldArguments(invoker, getTarget);
...@@ -287,9 +256,34 @@ public class CallSite ...@@ -287,9 +256,34 @@ public class CallSite
} }
} }
/** Implementation of {@link MethodHandleProvider} which returns {@code this.dynamicInvoker()}. */ /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
public final MethodHandle asMethodHandle() { return dynamicInvoker(); } /*package-private*/
static Empty uninitializedCallSite() {
throw new IllegalStateException("uninitialized call site");
}
// unsafe stuff:
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long TARGET_OFFSET;
static {
try {
TARGET_OFFSET = unsafe.objectFieldOffset(CallSite.class.getDeclaredField("target"));
} catch (Exception ex) { throw new Error(ex); }
}
/** Implementation of {@link MethodHandleProvider}, which returns {@code this.dynamicInvoker().asType(type)}. */ /*package-private*/
public final MethodHandle asMethodHandle(MethodType type) { return dynamicInvoker().asType(type); } void setTargetNormal(MethodHandle newTarget) {
target = newTarget;
//CallSiteImpl.setCallSiteTarget(IMPL_TOKEN, this, newTarget);
}
/*package-private*/
MethodHandle getTargetVolatile() {
return (MethodHandle) unsafe.getObjectVolatile(this, TARGET_OFFSET);
}
/*package-private*/
void setTargetVolatile(MethodHandle newTarget) {
unsafe.putObjectVolatile(this, TARGET_OFFSET, newTarget);
//CallSiteImpl.setCallSiteTarget(IMPL_TOKEN, this, newTarget);
}
} }
...@@ -27,8 +27,8 @@ package java.dyn; ...@@ -27,8 +27,8 @@ package java.dyn;
/** /**
* A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed. * 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 * An {@code invokedynamic} instruction linked to a {@code ConstantCallSite} is permanently
* to invalidate the instruction as a whole. * bound to the call site's target.
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
public class ConstantCallSite extends CallSite { public class ConstantCallSite extends CallSite {
...@@ -36,7 +36,9 @@ public class ConstantCallSite extends CallSite { ...@@ -36,7 +36,9 @@ public class ConstantCallSite extends CallSite {
public ConstantCallSite(MethodHandle target) { public ConstantCallSite(MethodHandle target) {
super(target); super(target);
} }
/** Throw an {@link IllegalArgumentException}, because this kind of call site cannot change its target. */ /**
* Throw an {@link IllegalArgumentException}, because this kind of call site cannot change its target.
*/
@Override public final void setTarget(MethodHandle ignore) { @Override public final void setTarget(MethodHandle ignore) {
throw new IllegalArgumentException("ConstantCallSite"); throw new IllegalArgumentException("ConstantCallSite");
} }
......
/*
* Copyright (c) 2008, 2009, 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;
/**
* {@code InvokeDynamic} is a class with neither methods nor instances,
* which serves only as a syntactic marker in Java source code for
* an {@code invokedynamic} instruction.
* (See <a href="package-summary.html#jvm_mods">the package information</a> for specifics on this instruction.)
* <p>
* The {@code invokedynamic} instruction is incomplete without a target method.
* 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 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
* class or interface supertype, or an object type; it can never be instantiated.
* Logically, it denotes a source of all dynamically typed methods.
* It may be viewed as a pure syntactic marker of static calls.
* It may be imported for ease of use.
* <p>
* Here are some examples:
<blockquote><pre><!-- see indy-demo/src/JavaDocExamples.java -->
&#064;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 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 declared for the enclosing class or method.
* @author John Rose, JSR 292 EG
*/
@MethodHandle.PolymorphicSignature
public final class InvokeDynamic {
private InvokeDynamic() { throw new InternalError(); } // do not instantiate
// no statically defined static methods
}
...@@ -29,15 +29,16 @@ import java.dyn.MethodHandles.Lookup; ...@@ -29,15 +29,16 @@ import java.dyn.MethodHandles.Lookup;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import sun.dyn.Access; import sun.dyn.Access;
import sun.dyn.MethodHandleImpl; import sun.dyn.MethodHandleImpl;
import sun.dyn.util.VerifyAccess;
import sun.reflect.Reflection; import sun.reflect.Reflection;
import static sun.dyn.util.VerifyAccess.checkBootstrapPrivilege;
import static sun.dyn.MemberName.newIllegalArgumentException; import static sun.dyn.MemberName.newIllegalArgumentException;
/** /**
* This class consists exclusively of static methods that control * <em>CLASS WILL BE REMOVED FOR PFD:</em>
* the linkage of {@code invokedynamic} instructions, and specifically * Static routines for controlling invokedynamic behavior.
* their reification as {@link CallSite} objects. * Replaced by non-static APIs.
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
* @deprecated This class will be removed in the Public Final Draft.
*/ */
public class Linkage { public class Linkage {
private static final Access IMPL_TOKEN = Access.getToken(); private static final Access IMPL_TOKEN = Access.getToken();
...@@ -45,68 +46,24 @@ public class Linkage { ...@@ -45,68 +46,24 @@ public class Linkage {
private Linkage() {} // do not instantiate private Linkage() {} // do not instantiate
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em> * <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Register a <em>bootstrap method</em> to use when linking dynamic call sites within * Register a <em>bootstrap method</em> to use when linking dynamic call sites within
* a given caller class. * a given caller class.
* <p> * @deprecated Use @{@link BootstrapMethod} annotations instead.
* A bootstrap method must be a method handle with a return type of {@link CallSite}
* and the following arguments:
* <ul>
* <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})
* </ul>
* 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>
* The registration must take place exactly once, either before the class has begun
* being initialized, or from within the class's static initializer.
* Registration will fail with an exception if any of the following conditions hold:
* <ul>
* <li>The immediate caller of this method is in a different package than the given caller class,
* and there is a security manager, and its {@code checkPermission} call throws
* when passed {@link LinkagePermission}("registerBootstrapMethod",callerClass).
* <li>The given caller class already has a bootstrap method registered.
* <li>The given caller class is already fully initialized.
* <li>The given caller class is in the process of initialization, in another thread.
* </ul>
* Because of these rules, a class may install its own bootstrap method in
* a static initializer.
* @param callerClass a class that may have {@code invokedynamic} sites
* @param bootstrapMethod the method to use to bootstrap all such sites
* @exception IllegalArgumentException if the class argument is null or
* a primitive class, or if the bootstrap method is the wrong type
* @exception IllegalStateException if the class already has a bootstrap
* method, or if the its static initializer has already run
* 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 public static
void registerBootstrapMethod(Class callerClass, MethodHandle bootstrapMethod) { void registerBootstrapMethod(Class callerClass, MethodHandle bootstrapMethod) {
Class callc = Reflection.getCallerClass(2); Class callc = Reflection.getCallerClass(2);
checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod"); if (callc != null && !VerifyAccess.isSamePackage(callerClass, callc))
checkBSM(bootstrapMethod); throw new IllegalArgumentException("cannot set bootstrap method on "+callerClass);
MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod); MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod);
} }
static private void checkBSM(MethodHandle mh) {
if (mh == null) throw newIllegalArgumentException("null bootstrap method");
if (mh.type() == BOOTSTRAP_METHOD_TYPE) return;
throw new WrongMethodTypeException(mh.toString());
}
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em> * <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Simplified version of {@code registerBootstrapMethod} for self-registration, * Simplified version of {@code registerBootstrapMethod} for self-registration,
* to be called from a static initializer. * to be called from a static initializer.
* Finds a static method of the required type in the * @deprecated Use @{@link BootstrapMethod} annotations instead.
* given runtime class, and installs it on the caller class.
* @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 public static
void registerBootstrapMethod(Class<?> runtime, String name) { void registerBootstrapMethod(Class<?> runtime, String name) {
...@@ -115,15 +72,9 @@ public class Linkage { ...@@ -115,15 +72,9 @@ public class Linkage {
} }
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em> * <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Simplified version of {@code registerBootstrapMethod} for self-registration, * Simplified version of {@code registerBootstrapMethod} for self-registration,
* to be called from a static initializer. * @deprecated Use @{@link BootstrapMethod} annotations instead.
* Finds a static method of the required type in the
* caller class itself, and installs it on the caller class.
* @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 public static
void registerBootstrapMethod(String name) { void registerBootstrapMethod(String name) {
...@@ -140,82 +91,33 @@ public class Linkage { ...@@ -140,82 +91,33 @@ public class Linkage {
} catch (NoAccessException ex) { } 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); MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod);
} }
/** private static final MethodType BOOTSTRAP_METHOD_TYPE
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* Report the bootstrap method registered for a given caller class.
* Returns null if the class has never yet registered a bootstrap method.
* Only callers privileged to set the bootstrap method may inquire
* about it, because a bootstrap method is potentially a back-door entry
* point into its class.
* @exception IllegalArgumentException if the argument is null or
* a primitive class
* @exception SecurityException if there is a security manager installed,
* 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) {
Class callc = Reflection.getCallerClass(2);
checkBootstrapPrivilege(callc, callerClass, "getBootstrapMethod");
return MethodHandleImpl.getBootstrap(IMPL_TOKEN, callerClass);
}
/**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* The type of any bootstrap method is a three-argument method
* {@code (Class, String, MethodType)} returning a {@code CallSite}.
*/
public static final MethodType BOOTSTRAP_METHOD_TYPE
= MethodType.methodType(CallSite.class, = MethodType.methodType(CallSite.class,
Class.class, String.class, MethodType.class); Class.class, String.class, MethodType.class);
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em> * <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Invalidate all <code>invokedynamic</code> call sites everywhere. * Invalidate all <code>invokedynamic</code> call sites everywhere.
* <p> * @deprecated Use {@linkplain CallSite#setTarget call site target setting}
* When this method returns, every <code>invokedynamic</code> instruction * and {@link VolatileCallSite#invalidateAll call site invalidation} instead.
* will invoke its bootstrap method on next call.
* <p>
* It is unspecified whether call sites already known to the Java
* code will continue to be associated with <code>invokedynamic</code>
* instructions. If any call site is still so associated, its
* {@link CallSite#getTarget()} method is guaranteed to return null
* the invalidation operation completes.
* <p>
* Invalidation operations are likely to be slow. Use them sparingly.
*/ */
public static public static
Object invalidateAll() { Object invalidateAll() {
SecurityManager security = System.getSecurityManager(); throw new UnsupportedOperationException();
if (security != null) {
security.checkPermission(new LinkagePermission("invalidateAll"));
}
throw new UnsupportedOperationException("NYI");
} }
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em> * <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Invalidate all {@code invokedynamic} call sites in the bytecodes * Invalidate all {@code invokedynamic} call sites in the bytecodes
* of any methods of the given class. * of any methods of the given class.
* <p> * @deprecated Use {@linkplain CallSite#setTarget call site target setting}
* When this method returns, every matching <code>invokedynamic</code> * and {@link VolatileCallSite#invalidateAll call site invalidation} instead.
* instruction will invoke its bootstrap method on next call.
* <p>
* For additional semantics of call site invalidation,
* see {@link #invalidateAll()}.
*/ */
public static public static
Object invalidateCallerClass(Class<?> callerClass) { Object invalidateCallerClass(Class<?> callerClass) {
SecurityManager security = System.getSecurityManager(); throw new UnsupportedOperationException();
if (security != null) {
security.checkPermission(new LinkagePermission("invalidateAll", callerClass));
}
throw new UnsupportedOperationException("NYI");
} }
} }
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.dyn;
import java.security.*;
import java.util.Enumeration;
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,
* a {@code LinkagePermission} contains a target name but
* no actions list; you either have the named permission
* or you don't.
* <p>
* The following table lists all the possible {@code LinkagePermission} target names,
* and for each provides a description of what the permission allows
* and a discussion of the risks of granting code the permission.
* <p>
*
* <table border=1 cellpadding=5 summary="permission target name,
* what the target allows,and associated risks">
* <tr>
* <th>Permission Target Name</th>
* <th>What the Permission Allows</th>
* <th>Risks of Allowing this Permission</th>
* </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,
* or perhaps expose timing bugs in a dynamic language implementations,
* by forcing redundant relinking operations.</td>
* </tr>
*
*
* <tr>
* <td>invalidateCallerClass.{class name}</td>
* <td>Force the relinking of invokedynamic call sites in the given class.</td>
* <td>See {@code invalidateAll}.</td>
* </tr>
* </table>
* <p>ISSUE: Is this still needed?
*
* @see java.lang.RuntimePermission
* @see java.lang.SecurityManager
*
* @author John Rose, JSR 292 EG
*/
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
* "invalidateCallerClass.*", etc. An asterisk
* may appear at the end of the name, following a ".", or by itself, to
* signify a wildcard match.
*
* @param name the name of the LinkagePermission
*/
public LinkagePermission(String name) {
super(name);
}
/**
* Create a new LinkagePermission with the given name on the given class.
* Equivalent to {@code LinkagePermission(name+"."+clazz.getName())}.
*
* @param name the name of the LinkagePermission
* @param clazz the class affected by the permission
*/
public LinkagePermission(String name, Class<?> clazz) {
super(name + "." + clazz.getName());
}
}
/*
* 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;
}
...@@ -119,7 +119,7 @@ class MethodType implements java.lang.reflect.Type { ...@@ -119,7 +119,7 @@ class MethodType implements java.lang.reflect.Type {
for (Class<?> ptype : ptypes) { for (Class<?> ptype : ptypes) {
ptype.equals(ptype); // null check ptype.equals(ptype); // null check
if (ptype == void.class) if (ptype == void.class)
throw newIllegalArgumentException("void parameter: "+this); throw newIllegalArgumentException("parameter type cannot be void");
} }
} }
...@@ -139,10 +139,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -139,10 +139,6 @@ class MethodType implements java.lang.reflect.Type {
MethodType methodType(Class<?> rtype, Class<?>[] ptypes) { MethodType methodType(Class<?> rtype, Class<?>[] ptypes) {
return makeImpl(rtype, ptypes, false); return makeImpl(rtype, ptypes, false);
} }
@Deprecated public static
MethodType make(Class<?> rtype, Class<?>[] ptypes) {
return methodType(rtype, ptypes);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. */ /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. */
public static public static
...@@ -150,10 +146,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -150,10 +146,6 @@ class MethodType implements java.lang.reflect.Type {
boolean notrust = false; // random List impl. could return evil ptypes array boolean notrust = false; // random List impl. could return evil ptypes array
return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust); return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust);
} }
@Deprecated public static
MethodType make(Class<?> rtype, List<? extends Class<?>> ptypes) {
return methodType(rtype, ptypes);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* The leading parameter type is prepended to the remaining array. * The leading parameter type is prepended to the remaining array.
...@@ -165,10 +157,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -165,10 +157,6 @@ class MethodType implements java.lang.reflect.Type {
System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length); System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length);
return makeImpl(rtype, ptypes1, true); return makeImpl(rtype, ptypes1, true);
} }
@Deprecated public static
MethodType make(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
return methodType(rtype, ptype0, ptypes);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* The resulting method has no parameter types. * The resulting method has no parameter types.
...@@ -177,10 +165,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -177,10 +165,6 @@ class MethodType implements java.lang.reflect.Type {
MethodType methodType(Class<?> rtype) { MethodType methodType(Class<?> rtype) {
return makeImpl(rtype, NO_PTYPES, true); return makeImpl(rtype, NO_PTYPES, true);
} }
@Deprecated public static
MethodType make(Class<?> rtype) {
return methodType(rtype);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* The resulting method has the single given parameter type. * The resulting method has the single given parameter type.
...@@ -189,10 +173,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -189,10 +173,6 @@ class MethodType implements java.lang.reflect.Type {
MethodType methodType(Class<?> rtype, Class<?> ptype0) { MethodType methodType(Class<?> rtype, Class<?> ptype0) {
return makeImpl(rtype, new Class<?>[]{ ptype0 }, true); return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
} }
@Deprecated public static
MethodType make(Class<?> rtype, Class<?> ptype0) {
return methodType(rtype, ptype0);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* The resulting method has the same parameter types as {@code ptypes}, * The resulting method has the same parameter types as {@code ptypes},
...@@ -202,10 +182,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -202,10 +182,6 @@ class MethodType implements java.lang.reflect.Type {
MethodType methodType(Class<?> rtype, MethodType ptypes) { MethodType methodType(Class<?> rtype, MethodType ptypes) {
return makeImpl(rtype, ptypes.ptypes, true); return makeImpl(rtype, ptypes.ptypes, true);
} }
@Deprecated public static
MethodType make(Class<?> rtype, MethodType ptypes) {
return methodType(rtype, ptypes);
}
/** /**
* Sole factory method to find or create an interned method type. * Sole factory method to find or create an interned method type.
...@@ -275,10 +251,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -275,10 +251,6 @@ class MethodType implements java.lang.reflect.Type {
} }
return mt; return mt;
} }
@Deprecated public static
MethodType makeGeneric(int objectArgCount, boolean varargs) {
return genericMethodType(objectArgCount, varargs);
}
/** /**
* All parameters and the return type will be Object. * All parameters and the return type will be Object.
...@@ -290,10 +262,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -290,10 +262,6 @@ class MethodType implements java.lang.reflect.Type {
MethodType genericMethodType(int objectArgCount) { MethodType genericMethodType(int objectArgCount) {
return genericMethodType(objectArgCount, false); return genericMethodType(objectArgCount, false);
} }
@Deprecated public static
MethodType makeGeneric(int objectArgCount) {
return genericMethodType(objectArgCount);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param num the index (zero-based) of the parameter type to change * @param num the index (zero-based) of the parameter type to change
...@@ -307,18 +275,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -307,18 +275,6 @@ class MethodType implements java.lang.reflect.Type {
return makeImpl(rtype, nptypes, true); return makeImpl(rtype, nptypes, true);
} }
/** Convenience method for {@link #insertParameterTypes}.
* @deprecated Use {@link #insertParameterTypes} instead.
*/
@Deprecated
public MethodType insertParameterType(int num, Class<?> nptype) {
int len = ptypes.length;
Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+1);
System.arraycopy(nptypes, num, nptypes, num+1, len-num);
nptypes[num] = nptype;
return makeImpl(rtype, nptypes, true);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param num the position (zero-based) of the inserted parameter type(s) * @param num the position (zero-based) of the inserted parameter type(s)
* @param ptypesToInsert zero or more a new parameter types to insert into the parameter list * @param ptypesToInsert zero or more a new parameter types to insert into the parameter list
...@@ -336,6 +292,14 @@ class MethodType implements java.lang.reflect.Type { ...@@ -336,6 +292,14 @@ class MethodType implements java.lang.reflect.Type {
return makeImpl(rtype, nptypes, true); return makeImpl(rtype, nptypes, true);
} }
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list
* @return the same type, except with the selected parameter(s) appended
*/
public MethodType appendParameterTypes(Class<?>... ptypesToInsert) {
return insertParameterTypes(parameterCount(), ptypesToInsert);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param num the position (zero-based) of the inserted parameter type(s) * @param num the position (zero-based) of the inserted parameter type(s)
* @param ptypesToInsert zero or more a new parameter types to insert into the parameter list * @param ptypesToInsert zero or more a new parameter types to insert into the parameter list
...@@ -377,14 +341,6 @@ class MethodType implements java.lang.reflect.Type { ...@@ -377,14 +341,6 @@ class MethodType implements java.lang.reflect.Type {
return makeImpl(rtype, nptypes, true); return makeImpl(rtype, nptypes, true);
} }
/** Convenience method for {@link #dropParameterTypes}.
* @deprecated Use {@link #dropParameterTypes} instead.
*/
@Deprecated
public MethodType dropParameterType(int num) {
return dropParameterTypes(num, num+1);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param nrtype a return parameter type to replace the old one with * @param nrtype a return parameter type to replace the old one with
* @return the same type, except with the return type change * @return the same type, except with the return type change
...@@ -690,14 +646,4 @@ class MethodType implements java.lang.reflect.Type { ...@@ -690,14 +646,4 @@ class MethodType implements java.lang.reflect.Type {
public String toMethodDescriptorString() { public String toMethodDescriptorString() {
return BytecodeDescriptor.unparse(this); return BytecodeDescriptor.unparse(this);
} }
/** Temporary alias for toMethodDescriptorString; delete after M3. */
public String toBytecodeString() {
return toMethodDescriptorString();
}
/** Temporary alias for fromMethodDescriptorString; delete after M3. */
public static MethodType fromBytecodeString(String descriptor, ClassLoader loader)
throws IllegalArgumentException, TypeNotPresentException {
return fromMethodDescriptorString(descriptor, loader);
}
} }
/*
* 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.List;
/**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* A {@code VolatileCallSite} is a {@link CallSite} whose target acts like a volatile variable.
* An {@code invokedynamic} instruction linked to a {@code VolatileCallSite} sees updates
* to its call site target immediately, even if the update occurs in another thread.
* <p>
* Also, a volatile call site has the ability to be <em>invalidated</em>,
* or reset to a well-defined fallback state.
* <p>
* A volatile call site can be used as a switch to control the behavior
* of another method handle. For example:
* <blockquote><pre>
MethodHandle strcat = MethodHandles.lookup()
.findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
MethodHandle trueCon = MethodHandles.constant(boolean.class, true);
MethodHandle falseCon = MethodHandles.constant(boolean.class, false);
VolatileCallSite switcher = new VolatileCallSite(trueCon, falseCon);
// following steps may be repeated to re-use the same switcher:
MethodHandle worker1 = strcat;
MethodHandle worker2 = MethodHandles.permuteArguments(strcat, strcat.type(), 1, 0);
MethodHandle worker = MethodHandles.guardWithTest(switcher.dynamicInvoker(), worker1, worker2);
System.out.println((String) worker.invokeExact("met", "hod")); // method
switcher.invalidate();
System.out.println((String) worker.invokeExact("met", "hod")); // hodmet
* </pre></blockquote>
* In this case, the fallback path (worker2) does not cause a state change.
* In a real application, the fallback path could cause call sites to relink
* themselves in response to a global data structure change.
* Thus, volatile call sites can be used to build dependency mechanisms.
* @author John Rose, JSR 292 EG
*/
public class VolatileCallSite extends CallSite {
volatile MethodHandle fallback;
/** Create a call site with a volatile target.
* The initial target and fallback are both set to a method handle
* of the given type which will throw {@code IllegalStateException}.
*/
public VolatileCallSite(MethodType type) {
super(type);
fallback = target;
}
/** Create a call site with a volatile target.
* The fallback and target are both set to the same initial value.
*/
public VolatileCallSite(MethodHandle target) {
super(target);
fallback = target;
}
/** Create a call site with a volatile target.
* The fallback and target are set to the given initial values.
*/
public VolatileCallSite(MethodHandle target, MethodHandle fallback) {
this(target);
checkTargetChange(target, fallback); // make sure they have the same type
this.fallback = fallback;
}
/** Internal override to nominally final getTarget. */
@Override
MethodHandle getTarget0() {
return getTargetVolatile();
}
/**
* Set the target method of this call site, as a volatile variable.
* Has the same effect as {@link CallSite#setTarget}, with the additional
* effects associated with volatiles, in the Java Memory Model.
*/
@Override public void setTarget(MethodHandle newTarget) {
checkTargetChange(getTargetVolatile(), newTarget);
setTargetVolatile(newTarget);
}
/**
* Return the fallback target for this call site.
* It is initialized to the target the call site had when it was constructed,
* but it may be changed by {@link setFallbackTarget}.
* <p>
* Like the regular target of a volatile call site,
* the fallback target also has the behavior of a volatile variable.
*/
public MethodHandle getFallbackTarget() {
return fallback;
}
/**
* Update the fallback target for this call site.
* @see #getFallbackTarget
*/
public void setFallbackTarget(MethodHandle newFallbackTarget) {
checkTargetChange(fallback, newFallbackTarget);
fallback = newFallbackTarget;
}
/**
* Reset this call site to a known state by changing the target to the fallback target value.
* Equivalent to {@code setTarget(getFallbackTarget())}.
*/
public void invalidate() {
setTargetVolatile(getFallbackTarget());
}
/**
* Reset all call sites in a list by changing the target of each to its fallback value.
*/
public static void invalidateAll(List<VolatileCallSite> sites) {
for (VolatileCallSite site : sites) {
site.invalidate();
}
}
}
...@@ -487,7 +487,7 @@ public class AdapterMethodHandle extends BoundMethodHandle { ...@@ -487,7 +487,7 @@ public class AdapterMethodHandle extends BoundMethodHandle {
static class WithTypeHandler extends AdapterMethodHandle { static class WithTypeHandler extends AdapterMethodHandle {
final MethodHandle target, typeHandler; final MethodHandle target, typeHandler;
WithTypeHandler(MethodHandle target, MethodHandle typeHandler) { WithTypeHandler(MethodHandle target, MethodHandle typeHandler) {
super(target, target.type(), OP_RETYPE_ONLY); super(target, target.type(), makeConv(OP_RETYPE_ONLY));
this.target = target; this.target = target;
this.typeHandler = typeHandler.asType(TYPE_HANDLER_TYPE); this.typeHandler = typeHandler.asType(TYPE_HANDLER_TYPE);
} }
......
...@@ -106,18 +106,17 @@ public class BoundMethodHandle extends MethodHandle { ...@@ -106,18 +106,17 @@ public class BoundMethodHandle extends MethodHandle {
assert(this instanceof AdapterMethodHandle); assert(this instanceof AdapterMethodHandle);
} }
/** Initialize the current object as a Java method handle, binding it /** Initialize the current object as a self-bound method handle, binding it
* as the first argument of the method handle {@code entryPoint}. * as the first argument of the method handle {@code entryPoint}.
* The invocation type of the resulting method handle will be the * The invocation type of the resulting method handle will be the
* same as {@code entryPoint}, except that the first argument * same as {@code entryPoint}, except that the first argument
* type will be dropped. * type will be dropped.
*/ */
protected BoundMethodHandle(MethodHandle entryPoint) { protected BoundMethodHandle(Access token, MethodHandle entryPoint) {
super(Access.TOKEN, entryPoint.type().dropParameterTypes(0, 1)); super(token, entryPoint.type().dropParameterTypes(0, 1));
this.argument = this; // kludge; get rid of this.argument = this; // kludge; get rid of
this.vmargslot = this.type().parameterSlotDepth(0); this.vmargslot = this.type().parameterSlotDepth(0);
initTarget(entryPoint, 0); initTarget(entryPoint, 0);
assert(this instanceof JavaMethodHandle);
} }
/** Make sure the given {@code argument} can be used as {@code argnum}-th /** Make sure the given {@code argument} can be used as {@code argnum}-th
...@@ -173,6 +172,11 @@ public class BoundMethodHandle extends MethodHandle { ...@@ -173,6 +172,11 @@ public class BoundMethodHandle extends MethodHandle {
@Override @Override
public String toString() { public String toString() {
return MethodHandleImpl.addTypeString(baseName(), this);
}
/** Component of toString() before the type string. */
protected String baseName() {
MethodHandle mh = this; MethodHandle mh = this;
while (mh instanceof BoundMethodHandle) { while (mh instanceof BoundMethodHandle) {
Object info = MethodHandleNatives.getTargetInfo(mh); Object info = MethodHandleNatives.getTargetInfo(mh);
...@@ -185,12 +189,16 @@ public class BoundMethodHandle extends MethodHandle { ...@@ -185,12 +189,16 @@ public class BoundMethodHandle extends MethodHandle {
if (name != null) if (name != null)
return name; return name;
else else
return super.toString(); // <unknown>, probably return noParens(super.toString()); // "invoke", probably
} }
assert(mh != this); assert(mh != this);
if (mh instanceof JavaMethodHandle)
break; // access JMH.toString(), not BMH.toString()
} }
return mh.toString(); return noParens(mh.toString());
}
private static String noParens(String str) {
int paren = str.indexOf('(');
if (paren >= 0) str = str.substring(0, paren);
return str;
} }
} }
...@@ -41,25 +41,39 @@ public class CallSiteImpl { ...@@ -41,25 +41,39 @@ public class CallSiteImpl {
Object info, Object info,
// Caller information: // Caller information:
MemberName callerMethod, int callerBCI) { MemberName callerMethod, int callerBCI) {
Class<?> caller = callerMethod.getDeclaringClass(); Class<?> callerClass = callerMethod.getDeclaringClass();
Object caller;
if (bootstrapMethod.type().parameterType(0) == Class.class)
caller = callerClass; // remove for PFD
else
caller = MethodHandleImpl.IMPL_LOOKUP.in(callerClass);
if (bootstrapMethod == null) { if (bootstrapMethod == null) {
// If there is no bootstrap method, throw IncompatibleClassChangeError. // If there is no bootstrap method, throw IncompatibleClassChangeError.
// This is a valid generic error type for resolution (JLS 12.3.3). // This is a valid generic error type for resolution (JLS 12.3.3).
throw new IncompatibleClassChangeError throw new IncompatibleClassChangeError
("Class "+caller.getName()+" has not declared a bootstrap method for invokedynamic"); ("Class "+callerClass.getName()+" has not declared a bootstrap method for invokedynamic");
} }
CallSite site; CallSite site;
try { try {
Object binding; Object binding;
if (false) // switch when invokeGeneric works if (info == null) {
binding = bootstrapMethod.invokeGeneric(caller, name, type); if (false) // switch when invokeGeneric works
else binding = bootstrapMethod.invokeGeneric(caller, name, type);
binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type }); else
binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type });
} else {
info = maybeReBox(info);
if (false) // switch when invokeGeneric works
binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
else
binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type, info });
}
//System.out.println("BSM for "+name+type+" => "+binding); //System.out.println("BSM for "+name+type+" => "+binding);
if (binding instanceof CallSite) { if (binding instanceof CallSite) {
site = (CallSite) binding; site = (CallSite) binding;
} else if (binding instanceof MethodHandleProvider) { } else if (binding instanceof MethodHandle) {
MethodHandle target = ((MethodHandleProvider) binding).asMethodHandle(); // Transitional!
MethodHandle target = (MethodHandle) binding;
site = new ConstantCallSite(target); site = new ConstantCallSite(target);
} else { } else {
throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite"); throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite");
...@@ -79,6 +93,24 @@ public class CallSiteImpl { ...@@ -79,6 +93,24 @@ public class CallSiteImpl {
return site; return site;
} }
private static Object maybeReBox(Object x) {
if (x instanceof Integer) {
int xi = (int) x;
if (xi == (byte) xi)
x = xi; // must rebox; see JLS 5.1.7
return x;
} else if (x instanceof Object[]) {
Object[] xa = (Object[]) x;
for (int i = 0; i < xa.length; i++) {
if (xa[i] instanceof Integer)
xa[i] = maybeReBox(xa[i]);
}
return xa;
} else {
return x;
}
}
// This method is private in CallSite because it touches private fields in CallSite. // This method is private in CallSite because it touches private fields in CallSite.
// These private fields (vmmethod, vmindex) are specific to the JVM. // 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;
......
...@@ -115,7 +115,7 @@ class FilterGeneric { ...@@ -115,7 +115,7 @@ class FilterGeneric {
static MethodHandle make(Kind kind, int pos, MethodHandle filter, MethodHandle target) { static MethodHandle make(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
FilterGeneric fgen = of(kind, pos, filter.type(), target.type()); FilterGeneric fgen = of(kind, pos, filter.type(), target.type());
return fgen.makeInstance(kind, pos, filter, target).asMethodHandle(); return fgen.makeInstance(kind, pos, filter, target);
} }
/** Return the adapter information for this target and filter type. */ /** Return the adapter information for this target and filter type. */
...@@ -225,13 +225,13 @@ class FilterGeneric { ...@@ -225,13 +225,13 @@ class FilterGeneric {
* The invoker is kept separate from the target because it can be * The invoker is kept separate from the target because it can be
* generated once per type erasure family, and reused across adapters. * generated once per type erasure family, and reused across adapters.
*/ */
static abstract class Adapter extends JavaMethodHandle { static abstract class Adapter extends BoundMethodHandle {
protected final MethodHandle filter; // transforms one or more arguments protected final MethodHandle filter; // transforms one or more arguments
protected final MethodHandle target; // ultimate target protected final MethodHandle target; // ultimate target
@Override @Override
public String toString() { public String toString() {
return target.toString(); return MethodHandleImpl.addTypeString(target, this);
} }
protected boolean isPrototype() { return target == null; } protected boolean isPrototype() { return target == null; }
...@@ -246,7 +246,7 @@ class FilterGeneric { ...@@ -246,7 +246,7 @@ class FilterGeneric {
protected Adapter(MethodHandle entryPoint, protected Adapter(MethodHandle entryPoint,
MethodHandle filter, MethodHandle target) { MethodHandle filter, MethodHandle target) {
super(entryPoint); super(Access.TOKEN, entryPoint);
this.filter = filter; this.filter = filter;
this.target = target; this.target = target;
} }
......
...@@ -36,7 +36,7 @@ import static sun.dyn.MemberName.uncaughtException; ...@@ -36,7 +36,7 @@ import static sun.dyn.MemberName.uncaughtException;
* final method type is the responsibility of a JVM-level adapter. * final method type is the responsibility of a JVM-level adapter.
* @author jrose * @author jrose
*/ */
public class FilterOneArgument extends JavaMethodHandle { public class FilterOneArgument extends BoundMethodHandle {
protected final MethodHandle filter; // Object -> Object protected final MethodHandle filter; // Object -> Object
protected final MethodHandle target; // Object -> Object protected final MethodHandle target; // Object -> Object
...@@ -62,7 +62,7 @@ public class FilterOneArgument extends JavaMethodHandle { ...@@ -62,7 +62,7 @@ public class FilterOneArgument extends JavaMethodHandle {
} }
protected FilterOneArgument(MethodHandle filter, MethodHandle target) { protected FilterOneArgument(MethodHandle filter, MethodHandle target) {
super(INVOKE); super(Access.TOKEN, INVOKE);
this.filter = filter; this.filter = filter;
this.target = target; this.target = target;
} }
......
...@@ -241,7 +241,7 @@ class FromGeneric { ...@@ -241,7 +241,7 @@ class FromGeneric {
* The invoker is kept separate from the target because it can be * The invoker is kept separate from the target because it can be
* generated once per type erasure family, and reused across adapters. * generated once per type erasure family, and reused across adapters.
*/ */
static abstract class Adapter extends JavaMethodHandle { static abstract class Adapter extends BoundMethodHandle {
/* /*
* class X<<R,int N>> extends Adapter { * class X<<R,int N>> extends Adapter {
* (MH, Object**N)=>raw(R) invoker; * (MH, Object**N)=>raw(R) invoker;
...@@ -256,7 +256,7 @@ class FromGeneric { ...@@ -256,7 +256,7 @@ class FromGeneric {
@Override @Override
public String toString() { public String toString() {
return target.toString(); return MethodHandleImpl.addTypeString(target, this);
} }
protected boolean isPrototype() { return target == null; } protected boolean isPrototype() { return target == null; }
...@@ -271,7 +271,7 @@ class FromGeneric { ...@@ -271,7 +271,7 @@ class FromGeneric {
protected Adapter(MethodHandle entryPoint, protected Adapter(MethodHandle entryPoint,
MethodHandle invoker, MethodHandle convert, MethodHandle target) { MethodHandle invoker, MethodHandle convert, MethodHandle target) {
super(entryPoint); super(Access.TOKEN, entryPoint);
this.invoker = invoker; this.invoker = invoker;
this.convert = convert; this.convert = convert;
this.target = target; this.target = target;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
package sun.dyn; package sun.dyn;
import java.dyn.*; import java.dyn.*;
import sun.dyn.empty.Empty;
/** /**
* Construction and caching of often-used invokers. * Construction and caching of often-used invokers.
...@@ -48,6 +49,9 @@ public class Invokers { ...@@ -48,6 +49,9 @@ public class Invokers {
// generic (untyped) invoker for the outgoing call; accepts a single Object[] // generic (untyped) invoker for the outgoing call; accepts a single Object[]
private final /*lazy*/ MethodHandle[] varargsInvokers; private final /*lazy*/ MethodHandle[] varargsInvokers;
// invoker for an unbound callsite
private /*lazy*/ MethodHandle uninitializedCallSite;
/** Compute and cache information common to all collecting adapters /** Compute and cache information common to all collecting adapters
* that implement members of the erasure-family of the given erased type. * that implement members of the erasure-family of the given erased type.
*/ */
...@@ -107,6 +111,35 @@ public class Invokers { ...@@ -107,6 +111,35 @@ public class Invokers {
return vaInvoker; return vaInvoker;
} }
private static MethodHandle THROW_UCS = null;
public MethodHandle uninitializedCallSite() {
MethodHandle invoker = uninitializedCallSite;
if (invoker != null) return invoker;
if (targetType.parameterCount() > 0) {
MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount());
Invokers invokers0 = MethodTypeImpl.invokers(Access.TOKEN, type0);
invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(),
0, targetType.parameterList());
assert(invoker.type().equals(targetType));
uninitializedCallSite = invoker;
return invoker;
}
if (THROW_UCS == null) {
try {
THROW_UCS = MethodHandleImpl.IMPL_LOOKUP
.findStatic(CallSite.class, "uninitializedCallSite",
MethodType.methodType(Empty.class));
} catch (NoAccessException ex) {
throw new RuntimeException(ex);
}
}
invoker = AdapterMethodHandle.makeRetypeRaw(Access.TOKEN, targetType, THROW_UCS);
assert(invoker.type().equals(targetType));
uninitializedCallSite = invoker;
return invoker;
}
public String toString() { public String toString() {
return "Invokers"+targetType; return "Invokers"+targetType;
} }
......
/*
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.dyn;
import java.dyn.*;
import sun.dyn.Access;
/**
* A Java method handle is a deprecated proposal for extending
* the basic method handle type with additional
* programmer defined methods and fields.
* Its behavior as a method handle is determined at instance creation time,
* by providing the new instance with an "entry point" method handle
* to handle calls. This entry point must accept a leading argument
* whose type is the Java method handle itself or a supertype, and the
* entry point is always called with the Java method handle itself as
* the first argument. This is similar to ordinary virtual methods, which also
* accept the receiver object {@code this} as an implicit leading argument.
* The {@code MethodType} of the Java method handle is the same as that
* of the entry point method handle, with the leading parameter type
* omitted.
* <p>
* Here is an example of usage, creating a hybrid object/functional datum:
* <p><blockquote><pre>
* class Greeter extends JavaMethodHandle {
* private String greeting = "hello";
* public void setGreeting(String s) { greeting = s; }
* public void run() { System.out.println(greeting+", "+greetee); }
* private final String greetee;
* Greeter(String greetee) {
* super(RUN); // alternatively, super("run")
* this.greetee = greetee;
* }
* // the entry point function is computed once:
* private static final MethodHandle RUN
* = MethodHandles.lookup().findVirtual(Greeter.class, "run",
* MethodType.make(void.class));
* }
* // class Main { public static void main(String... av) { ...
* Greeter greeter = new Greeter("world");
* greeter.run(); // prints "hello, world"
* // Statically typed method handle invocation (most direct):
* MethodHandle mh = greeter;
* mh.&lt;void&gt;invokeExact(); // also prints "hello, world"
* // Dynamically typed method handle invocation:
* MethodHandles.invokeExact(greeter); // also prints "hello, world"
* greeter.setGreeting("howdy");
* mh.invokeExact(); // prints "howdy, world" (object-like mutable behavior)
* </pre></blockquote>
* <p>
* In the example of {@code Greeter}, the method {@code run} provides the entry point.
* The entry point need not be a constant value; it may be independently
* computed in each call to the constructor. The entry point does not
* even need to be a method on the {@code Greeter} class, though
* that is the typical case.
* <p>
* The entry point may also be provided symbolically, in which case the the
* {@code JavaMethodHandle} constructor performs the lookup of the entry point.
* This makes it possible to use {@code JavaMethodHandle} to create an anonymous
* inner class:
* <p><blockquote><pre>
* // We can also do this with symbolic names and/or inner classes:
* MethodHandles.invokeExact(new JavaMethodHandle("yow") {
* void yow() { System.out.println("yow, world"); }
* });
* </pre></blockquote>
* <p>
* Here is similar lower-level code which works in terms of a bound method handle.
* <p><blockquote><pre>
* class Greeter {
* public void run() { System.out.println("hello, "+greetee); }
* private final String greetee;
* Greeter(String greetee) { this.greetee = greetee; }
* // the entry point function is computed once:
* private static final MethodHandle RUN
* = MethodHandles.findVirtual(Greeter.class, "run",
* MethodType.make(void.class));
* }
* // class Main { public static void main(String... av) { ...
* Greeter greeter = new Greeter("world");
* greeter.run(); // prints "hello, world"
* MethodHandle mh = MethodHanndles.insertArgument(Greeter.RUN, 0, greeter);
* mh.invokeExact(); // also prints "hello, world"
* </pre></blockquote>
* Note that the method handle must be separately created as a view on the base object.
* This increases footprint, complexity, and dynamic indirections.
* <p>
* Here is a pure functional value expressed most concisely as an anonymous inner class:
* <p><blockquote><pre>
* // class Main { public static void main(String... av) { ...
* final String greetee = "world";
* MethodHandle greeter = new JavaMethodHandle("run") {
* private void run() { System.out.println("hello, "+greetee); }
* }
* greeter.invokeExact(); // prints "hello, world"
* </pre></blockquote>
* <p>
* Here is an abstract parameterized lvalue, efficiently expressed as a subtype of MethodHandle,
* and instantiated as an anonymous class. The data structure is a handle to 1-D array,
* with a specialized index type (long). It is created by inner class, and uses
* signature-polymorphic APIs throughout.
* <p><blockquote><pre>
* abstract class AssignableMethodHandle extends JavaMethodHandle {
* private final MethodHandle setter;
* public MethodHandle setter() { return setter; }
* public AssignableMethodHandle(String get, String set) {
* super(get);
* MethodType getType = this.type();
* MethodType setType = getType.insertParameterType(getType.parameterCount(), getType.returnType()).changeReturnType(void.class);
* this.setter = MethodHandles.publicLookup().bind(this, set, setType);
* }
* }
* // class Main { public static void main(String... av) { ...
* final Number[] stuff = { 123, 456 };
* AssignableMethodHandle stuffPtr = new AssignableMethodHandle("get", "set") {
* public Number get(long i) { return stuff[(int)i]; }
* public void set(long i, Object x) { stuff[(int)i] = x; }
* }
* int x = (Integer) stuffPtr.&lt;Number&gt;invokeExact(1L); // 456
* stuffPtr.setter().&lt;void&gt;invokeExact(0L, (Number) 789); // replaces 123 with 789
* </pre></blockquote>
* @see MethodHandle
* @deprecated The JSR 292 EG intends to replace {@code JavaMethodHandle} with
* an interface-based API for mixing method handle behavior with other classes.
* @author John Rose, JSR 292 EG
*/
public abstract class JavaMethodHandle
// Note: This is an implementation inheritance hack, and will be removed
// with a JVM change which moves the required hidden behavior onto this class.
extends sun.dyn.BoundMethodHandle
{
private static final Access IMPL_TOKEN = Access.getToken();
/**
* When creating a {@code JavaMethodHandle}, the actual method handle
* invocation behavior will be delegated to the specified {@code entryPoint}.
* This may be any method handle which can take the newly constructed object
* as a leading parameter.
* <p>
* The method handle type of {@code this} (i.e, the fully constructed object)
* will be {@code entryPoint}, minus the leading argument.
* The leading argument will be bound to {@code this} on every method
* handle invocation.
* @param entryPoint the method handle to handle calls
*/
protected JavaMethodHandle(MethodHandle entryPoint) {
super(entryPoint);
}
}
...@@ -199,7 +199,7 @@ public abstract class MethodHandleImpl { ...@@ -199,7 +199,7 @@ public abstract class MethodHandleImpl {
return allocator; return allocator;
} }
static final class AllocateObject<C> extends JavaMethodHandle { static final class AllocateObject<C> extends BoundMethodHandle {
private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final Unsafe unsafe = Unsafe.getUnsafe();
private final Class<C> allocateClass; private final Class<C> allocateClass;
...@@ -207,7 +207,7 @@ public abstract class MethodHandleImpl { ...@@ -207,7 +207,7 @@ public abstract class MethodHandleImpl {
private AllocateObject(MethodHandle invoker, private AllocateObject(MethodHandle invoker,
Class<C> allocateClass, MethodHandle rawConstructor) { Class<C> allocateClass, MethodHandle rawConstructor) {
super(invoker); super(Access.TOKEN, invoker);
this.allocateClass = allocateClass; this.allocateClass = allocateClass;
this.rawConstructor = rawConstructor; this.rawConstructor = rawConstructor;
} }
...@@ -237,7 +237,7 @@ public abstract class MethodHandleImpl { ...@@ -237,7 +237,7 @@ public abstract class MethodHandleImpl {
} }
@Override @Override
public String toString() { public String toString() {
return allocateClass.getSimpleName(); return addTypeString(allocateClass.getSimpleName(), this);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private C allocate() throws InstantiationException { private C allocate() throws InstantiationException {
...@@ -369,19 +369,19 @@ public abstract class MethodHandleImpl { ...@@ -369,19 +369,19 @@ public abstract class MethodHandleImpl {
return mhs[isSetter ? 1 : 0]; return mhs[isSetter ? 1 : 0];
} }
static final class FieldAccessor<C,V> extends JavaMethodHandle { static final class FieldAccessor<C,V> extends BoundMethodHandle {
private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final Unsafe unsafe = Unsafe.getUnsafe();
final Object base; // for static refs only final Object base; // for static refs only
final long offset; final long offset;
final String name; final String name;
public FieldAccessor(Access token, MemberName field, boolean isSetter) { public FieldAccessor(Access token, MemberName field, boolean isSetter) {
super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic())); super(Access.TOKEN, fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic()));
this.offset = (long) field.getVMIndex(token); this.offset = (long) field.getVMIndex(token);
this.name = field.getName(); this.name = field.getName();
this.base = staticBase(field); this.base = staticBase(field);
} }
public String toString() { return name; } public String toString() { return addTypeString(name, this); }
int getFieldI(C obj) { return unsafe.getInt(obj, offset); } int getFieldI(C obj) { return unsafe.getInt(obj, offset); }
void setFieldI(C obj, int x) { unsafe.putInt(obj, offset, x); } void setFieldI(C obj, int x) { unsafe.putInt(obj, offset, x); }
...@@ -910,11 +910,11 @@ public abstract class MethodHandleImpl { ...@@ -910,11 +910,11 @@ public abstract class MethodHandleImpl {
throw new UnsupportedOperationException("NYI"); throw new UnsupportedOperationException("NYI");
} }
private static class GuardWithTest extends JavaMethodHandle { private static class GuardWithTest extends BoundMethodHandle {
private final MethodHandle test, target, fallback; private final MethodHandle test, target, fallback;
private GuardWithTest(MethodHandle invoker, private GuardWithTest(MethodHandle invoker,
MethodHandle test, MethodHandle target, MethodHandle fallback) { MethodHandle test, MethodHandle target, MethodHandle fallback) {
super(invoker); super(Access.TOKEN, invoker);
this.test = test; this.test = test;
this.target = target; this.target = target;
this.fallback = fallback; this.fallback = fallback;
...@@ -948,7 +948,7 @@ public abstract class MethodHandleImpl { ...@@ -948,7 +948,7 @@ public abstract class MethodHandleImpl {
} }
@Override @Override
public String toString() { public String toString() {
return target.toString(); return addTypeString(target, this);
} }
private Object invoke_V(Object... av) throws Throwable { private Object invoke_V(Object... av) throws Throwable {
if (test.<boolean>invokeExact(av)) if (test.<boolean>invokeExact(av))
...@@ -1038,7 +1038,7 @@ public abstract class MethodHandleImpl { ...@@ -1038,7 +1038,7 @@ public abstract class MethodHandleImpl {
return GuardWithTest.make(token, test, target, fallback); return GuardWithTest.make(token, test, target, fallback);
} }
private static class GuardWithCatch extends JavaMethodHandle { private static class GuardWithCatch extends BoundMethodHandle {
private final MethodHandle target; private final MethodHandle target;
private final Class<? extends Throwable> exType; private final Class<? extends Throwable> exType;
private final MethodHandle catcher; private final MethodHandle catcher;
...@@ -1047,14 +1047,14 @@ public abstract class MethodHandleImpl { ...@@ -1047,14 +1047,14 @@ public abstract class MethodHandleImpl {
} }
public GuardWithCatch(MethodHandle invoker, public GuardWithCatch(MethodHandle invoker,
MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
super(invoker); super(Access.TOKEN, invoker);
this.target = target; this.target = target;
this.exType = exType; this.exType = exType;
this.catcher = catcher; this.catcher = catcher;
} }
@Override @Override
public String toString() { public String toString() {
return target.toString(); return addTypeString(target, this);
} }
private Object invoke_V(Object... av) throws Throwable { private Object invoke_V(Object... av) throws Throwable {
try { try {
...@@ -1219,21 +1219,24 @@ public abstract class MethodHandleImpl { ...@@ -1219,21 +1219,24 @@ public abstract class MethodHandleImpl {
if (target != null) if (target != null)
name = MethodHandleNatives.getMethodName(target); name = MethodHandleNatives.getMethodName(target);
if (name == null) if (name == null)
return "<unknown>"; return "invoke" + target.type();
return name.getName(); return name.getName() + target.type();
} }
public static String addTypeString(MethodHandle target) { static String addTypeString(Object obj, MethodHandle target) {
if (target == null) return "null"; String str = String.valueOf(obj);
return target.toString() + target.type(); if (target == null) return str;
int paren = str.indexOf('(');
if (paren >= 0) str = str.substring(0, paren);
return str + target.type();
} }
public static void checkSpreadArgument(Object av, int n) { static void checkSpreadArgument(Object av, int n) {
if (av == null ? n != 0 : ((Object[])av).length != n) if (av == null ? n != 0 : ((Object[])av).length != n)
throw newIllegalArgumentException("Array is not of length "+n); throw newIllegalArgumentException("Array is not of length "+n);
} }
public static void raiseException(int code, Object actual, Object required) { static void raiseException(int code, Object actual, Object required) {
String message; String message;
// disregard the identity of the actual object, if it is not a class: // disregard the identity of the actual object, if it is not a class:
if (!(actual instanceof Class) && !(actual instanceof MethodType)) if (!(actual instanceof Class) && !(actual instanceof MethodType))
......
...@@ -208,7 +208,7 @@ class SpreadGeneric { ...@@ -208,7 +208,7 @@ class SpreadGeneric {
* The invoker is kept separate from the target because it can be * The invoker is kept separate from the target because it can be
* generated once per type erasure family, and reused across adapters. * generated once per type erasure family, and reused across adapters.
*/ */
static abstract class Adapter extends JavaMethodHandle { static abstract class Adapter extends BoundMethodHandle {
/* /*
* class X<<R,int M,int N>> extends Adapter { * class X<<R,int M,int N>> extends Adapter {
* (Object**N)=>R target; * (Object**N)=>R target;
...@@ -221,21 +221,21 @@ class SpreadGeneric { ...@@ -221,21 +221,21 @@ class SpreadGeneric {
@Override @Override
public String toString() { public String toString() {
return target.toString(); return MethodHandleImpl.addTypeString(target, this);
} }
static final MethodHandle NO_ENTRY = ValueConversions.identity(); static final MethodHandle NO_ENTRY = ValueConversions.identity();
protected boolean isPrototype() { return target == null; } protected boolean isPrototype() { return target == null; }
protected Adapter(SpreadGeneric outer) { protected Adapter(SpreadGeneric outer) {
super(NO_ENTRY); super(Access.TOKEN, NO_ENTRY);
this.outer = outer; this.outer = outer;
this.target = null; this.target = null;
assert(isPrototype()); assert(isPrototype());
} }
protected Adapter(SpreadGeneric outer, MethodHandle target) { protected Adapter(SpreadGeneric outer, MethodHandle target) {
super(outer.entryPoint); super(Access.TOKEN, outer.entryPoint);
this.outer = outer; this.outer = outer;
this.target = target; this.target = target;
} }
......
...@@ -323,7 +323,7 @@ class ToGeneric { ...@@ -323,7 +323,7 @@ class ToGeneric {
* via another method handle {@code convert}, which is responsible for * via another method handle {@code convert}, which is responsible for
* converting the object result into the raw return value. * converting the object result into the raw return value.
*/ */
static abstract class Adapter extends JavaMethodHandle { static abstract class Adapter extends BoundMethodHandle {
/* /*
* class X<<R,A...>> extends Adapter { * class X<<R,A...>> extends Adapter {
* Object...=>Object target; * Object...=>Object target;
...@@ -337,13 +337,13 @@ class ToGeneric { ...@@ -337,13 +337,13 @@ class ToGeneric {
@Override @Override
public String toString() { public String toString() {
return target == null ? "prototype:"+convert : target.toString(); return target == null ? "prototype:"+convert : MethodHandleImpl.addTypeString(target, this);
} }
protected boolean isPrototype() { return target == null; } protected boolean isPrototype() { return target == null; }
/* Prototype constructor. */ /* Prototype constructor. */
protected Adapter(MethodHandle entryPoint) { protected Adapter(MethodHandle entryPoint) {
super(entryPoint); super(Access.TOKEN, entryPoint);
this.invoker = null; this.invoker = null;
this.convert = entryPoint; this.convert = entryPoint;
this.target = null; this.target = null;
...@@ -355,7 +355,7 @@ class ToGeneric { ...@@ -355,7 +355,7 @@ class ToGeneric {
} }
protected Adapter(MethodHandle entryPoint, MethodHandle invoker, MethodHandle convert, MethodHandle target) { protected Adapter(MethodHandle entryPoint, MethodHandle invoker, MethodHandle convert, MethodHandle target) {
super(entryPoint); super(Access.TOKEN, entryPoint);
this.invoker = invoker; this.invoker = invoker;
this.convert = convert; this.convert = convert;
this.target = target; this.target = target;
......
...@@ -649,7 +649,9 @@ public class ValueConversions { ...@@ -649,7 +649,9 @@ public class ValueConversions {
return mh; return mh;
} }
// slow path // slow path
MethodType type = MethodType.methodType(wrap.primitiveType(), wrap.primitiveType()); MethodType type = MethodType.methodType(wrap.primitiveType());
if (wrap != Wrapper.VOID)
type = type.appendParameterTypes(wrap.primitiveType());
try { try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type); mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type);
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
...@@ -677,7 +679,7 @@ public class ValueConversions { ...@@ -677,7 +679,7 @@ public class ValueConversions {
} }
private static MethodHandle retype(MethodType type, MethodHandle mh) { private static MethodHandle retype(MethodType type, MethodHandle mh) {
return AdapterMethodHandle.makeRetypeOnly(IMPL_TOKEN, type, mh); return AdapterMethodHandle.makeRetypeRaw(IMPL_TOKEN, type, mh);
} }
private static final Object[] NO_ARGS_ARRAY = {}; private static final Object[] NO_ARGS_ARRAY = {};
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
package sun.dyn.util; package sun.dyn.util;
import java.dyn.LinkagePermission;
import java.dyn.NoAccessException; import java.dyn.NoAccessException;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import sun.dyn.MemberName; import sun.dyn.MemberName;
...@@ -43,6 +42,7 @@ public class VerifyAccess { ...@@ -43,6 +42,7 @@ public class VerifyAccess {
private static final int PACKAGE_ONLY = 0; private static final int PACKAGE_ONLY = 0;
private static final int ALL_ACCESS_MODES = (PUBLIC|PRIVATE|PROTECTED|PACKAGE_ONLY); private static final int ALL_ACCESS_MODES = (PUBLIC|PRIVATE|PROTECTED|PACKAGE_ONLY);
private static final boolean ALLOW_NESTMATE_ACCESS = false;
/** /**
* Evaluate the JVM linkage rules for access to the given method * Evaluate the JVM linkage rules for access to the given method
...@@ -102,6 +102,8 @@ public class VerifyAccess { ...@@ -102,6 +102,8 @@ public class VerifyAccess {
// a superclass of the lookup class. // a superclass of the lookup class.
} }
} }
if (defc == lookupClass)
return true; // easy check; all self-access is OK
switch (mods & ALL_ACCESS_MODES) { switch (mods & ALL_ACCESS_MODES) {
case PUBLIC: case PUBLIC:
if (refc != defc) return true; // already checked above if (refc != defc) return true; // already checked above
...@@ -112,7 +114,8 @@ public class VerifyAccess { ...@@ -112,7 +114,8 @@ public class VerifyAccess {
return isSamePackage(defc, lookupClass); return isSamePackage(defc, lookupClass);
case PRIVATE: case PRIVATE:
// Loosened rules for privates follows access rules for inner classes. // Loosened rules for privates follows access rules for inner classes.
return isSamePackageMember(defc, lookupClass); return (ALLOW_NESTMATE_ACCESS &&
isSamePackageMember(defc, lookupClass));
default: default:
throw new IllegalArgumentException("bad modifiers: "+Modifier.toString(mods)); throw new IllegalArgumentException("bad modifiers: "+Modifier.toString(mods));
} }
...@@ -206,24 +209,4 @@ public class VerifyAccess { ...@@ -206,24 +209,4 @@ public class VerifyAccess {
} }
return false; return false;
} }
/**
* Ensure the requesting class have privileges to perform invokedynamic
* linkage operations on subjectClass. True if requestingClass is
* Access.class (meaning the request originates from the JVM) or if the
* classes are in the same package and have consistent class loaders.
* (The subject class loader must be identical with or be a child of
* the requesting class loader.)
* @param requestingClass
* @param subjectClass
*/
public static void checkBootstrapPrivilege(Class requestingClass, Class subjectClass,
String permissionName) {
if (requestingClass == null) return;
if (requestingClass == subjectClass) return;
SecurityManager security = System.getSecurityManager();
if (security == null) return; // open season
if (isSamePackage(requestingClass, subjectClass)) return;
security.checkPermission(new LinkagePermission(permissionName, requestingClass));
}
} }
...@@ -26,17 +26,19 @@ ...@@ -26,17 +26,19 @@
package sun.dyn.util; package sun.dyn.util;
public enum Wrapper { public enum Wrapper {
INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)), BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)),
LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)), // These must be in the order defined for widening primitive conversions in JLS 5.1.2
BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)), BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)),
SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)), SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)),
CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)), CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)),
BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)), INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)),
LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)),
FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)), FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)),
DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)), DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)),
VOID(Void.class, void.class, 'V', null, Format.other(0)),
//NULL(Null.class, null.class, 'N', null, Format.other(1)), //NULL(Null.class, null.class, 'N', null, Format.other(1)),
OBJECT(Object.class, Object.class, 'L', null, Format.other(1)), OBJECT(Object.class, Object.class, 'L', null, Format.other(1)),
// VOID must be the last type, since it is "assignable" from any other type:
VOID(Void.class, void.class, 'V', null, Format.other(0)),
; ;
private final Class<?> wrapperType; private final Class<?> wrapperType;
...@@ -76,9 +78,11 @@ public enum Wrapper { ...@@ -76,9 +78,11 @@ public enum Wrapper {
false); false);
return kind | (size << SIZE_SHIFT) | (slots << SLOT_SHIFT); return kind | (size << SIZE_SHIFT) | (slots << SLOT_SHIFT);
} }
static int static final int
INT = SIGNED | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT), INT = SIGNED | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
SHORT = SIGNED | (16 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
BOOLEAN = UNSIGNED | (1 << SIZE_SHIFT) | (1 << SLOT_SHIFT), BOOLEAN = UNSIGNED | (1 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
CHAR = UNSIGNED | (16 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
FLOAT = FLOATING | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT), FLOAT = FLOATING | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
VOID = UNSIGNED | (0 << SIZE_SHIFT) | (0 << SLOT_SHIFT), VOID = UNSIGNED | (0 << SIZE_SHIFT) | (0 << SLOT_SHIFT),
NUM_MASK = (-1) << SIZE_SHIFT; NUM_MASK = (-1) << SIZE_SHIFT;
...@@ -111,6 +115,29 @@ public enum Wrapper { ...@@ -111,6 +115,29 @@ public enum Wrapper {
/** Is the wrapped type either float or double? */ /** Is the wrapped type either float or double? */
public boolean isFloating() { return format >= Format.FLOAT; } public boolean isFloating() { return format >= Format.FLOAT; }
/** Does the JVM verifier allow a variable of this wrapper's
* primitive type to be assigned from a value of the given wrapper's primitive type?
* Cases:
* <ul>
* <li>unboxing followed by widening primitive conversion
* <li>any type converted to {@code void}
* <li>boxing conversion followed by widening reference conversion to {@code Object}
* <li>conversion of {@code boolean} to any type
* </ul>
*/
public boolean isConvertibleFrom(Wrapper source) {
if (this == source) return true;
if (this.compareTo(source) < 0) {
// At best, this is a narrowing conversion.
return false;
}
if ((this.format ^ source.format) == (Format.SHORT ^ Format.CHAR)) {
assert (this == SHORT && source == CHAR) || (this == CHAR && source == SHORT);
return false;
}
return true;
}
/** Produce a zero value for the given wrapper type. /** Produce a zero value for the given wrapper type.
* This will be a numeric zero for a number or character, * This will be a numeric zero for a number or character,
* false for a boolean, and null for a reference or void. * false for a boolean, and null for a reference or void.
...@@ -122,10 +149,10 @@ public enum Wrapper { ...@@ -122,10 +149,10 @@ public enum Wrapper {
public Object zero() { return zero; } public Object zero() { return zero; }
/** Produce a zero value for the given wrapper type T. /** Produce a zero value for the given wrapper type T.
* The optinoal argument must a type compatible with this wrapper. * The optional argument must a type compatible with this wrapper.
* Equivalent to {@code this.cast(this.zero(), type)}. * Equivalent to {@code this.cast(this.zero(), type)}.
*/ */
public <T> T zero(Class<T> type) { return cast(zero, type); } public <T> T zero(Class<T> type) { return convert(zero, type); }
// /** Produce a wrapper for the given wrapper or primitive type. */ // /** Produce a wrapper for the given wrapper or primitive type. */
// public static Wrapper valueOf(Class<?> type) { // public static Wrapper valueOf(Class<?> type) {
...@@ -264,7 +291,11 @@ public enum Wrapper { ...@@ -264,7 +291,11 @@ public enum Wrapper {
exampleType.isInterface()) { exampleType.isInterface()) {
return forceType(wrapperType, exampleType); return forceType(wrapperType, exampleType);
} }
throw new ClassCastException(exampleType + " not <:" + wrapperType); throw newClassCastException(exampleType, primitiveType);
}
private static ClassCastException newClassCastException(Class<?> actual, Class<?> expected) {
return new ClassCastException(actual + " is not compatible with " + expected);
} }
/** If {@code type} is a primitive type, return the corresponding /** If {@code type} is a primitive type, return the corresponding
...@@ -325,17 +356,55 @@ public enum Wrapper { ...@@ -325,17 +356,55 @@ public enum Wrapper {
// } // }
/** Cast a wrapped value to the given type, which may be either a primitive or wrapper type. /** Cast a wrapped value to the given type, which may be either a primitive or wrapper type.
* The given target type must be this wrapper's primitive or wrapper type.
* If this wrapper is OBJECT, the target type may also be an interface, perform no runtime check.
* Performs standard primitive conversions, including truncation and float conversions. * Performs standard primitive conversions, including truncation and float conversions.
* The given type must be compatible with this wrapper. That is, it must either * The given type must be compatible with this wrapper. That is, it must either
* be the wrapper type (or a subtype, in the case of {@code OBJECT}) or else * be the wrapper type (or a subtype, in the case of {@code OBJECT}) or else
* it must be the wrapper's primitive type. * it must be the wrapper's primitive type.
* Primitive conversions are only performed if the given type is itself a primitive.
* @throws ClassCastException if the given type is not compatible with this wrapper * @throws ClassCastException if the given type is not compatible with this wrapper
*/ */
public <T> T cast(Object x, Class<T> type) { public <T> T cast(Object x, Class<T> type) {
return convert(x, type, true);
}
/** Convert a wrapped value to the given type.
* The given target type must be this wrapper's primitive or wrapper type.
* This is equivalent to {@link #cast}, except that it refuses to perform
* narrowing primitive conversions.
*/
public <T> T convert(Object x, Class<T> type) {
return convert(x, type, false);
}
private <T> T convert(Object x, Class<T> type, boolean isCast) {
if (this == OBJECT) {
// If the target wrapper is OBJECT, just do a reference cast.
// If the target type is an interface, perform no runtime check.
// (This loophole is safe, and is allowed by the JVM verifier.)
// If the target type is a primitive, change it to a wrapper.
@SuppressWarnings("unchecked")
T result = (T) x; // unchecked warning is expected here
return result;
}
Class<T> wtype = wrapperType(type); Class<T> wtype = wrapperType(type);
if (wtype.isInstance(x)) if (wtype.isInstance(x)) {
return wtype.cast(x); @SuppressWarnings("unchecked")
return wtype.cast(wrap(x)); T result = (T) x; // unchecked warning is expected here
return result;
}
Class<?> sourceType = x.getClass(); // throw NPE if x is null
if (!isCast) {
Wrapper source = findWrapperType(sourceType);
if (source == null || !this.isConvertibleFrom(source)) {
throw newClassCastException(wtype, sourceType);
}
}
@SuppressWarnings("unchecked")
T result = (T) wrap(x); // unchecked warning is expected here
assert result.getClass() == wtype;
return result;
} }
/** Cast a reference type to another reference type. /** Cast a reference type to another reference type.
......
...@@ -398,7 +398,7 @@ public class InvokeGenericTest { ...@@ -398,7 +398,7 @@ public class InvokeGenericTest {
case 4: case 4:
junk = target.invokeGeneric(args[0], args[1], args[2], args[3]); break; junk = target.invokeGeneric(args[0], args[1], args[2], args[3]); break;
default: default:
junk = MethodHandles.invokeVarargs(target, args); break; junk = target.invokeWithArguments(args); break;
} }
} catch (WrongMethodTypeException ex) { } catch (WrongMethodTypeException ex) {
return; return;
......
...@@ -25,18 +25,18 @@ ...@@ -25,18 +25,18 @@
/* @test /* @test
* @summary example code used in javadoc for java.dyn API * @summary example code used in javadoc for java.dyn API
* @compile -XDallowTransitionalJSR292=no JavaDocExamples.java * @compile -XDallowTransitionalJSR292=no JavaDocExamplesTest.java
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.JavaDocExamples * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.JavaDocExamplesTest
*/ */
/* /*
---- To run outside jtreg: ---- To run outside jtreg:
$ $JAVA7X_HOME/bin/javac -cp $JUNIT4_JAR -d /tmp/Classes \ $ $JAVA7X_HOME/bin/javac -cp $JUNIT4_JAR -d /tmp/Classes \
$DAVINCI/sources/jdk/test/java/dyn/JavaDocExamples.java $DAVINCI/sources/jdk/test/java/dyn/JavaDocExamplesTest.java
$ $JAVA7X_HOME/bin/java -cp $JUNIT4_JAR:/tmp/Classes \ $ $JAVA7X_HOME/bin/java -cp $JUNIT4_JAR:/tmp/Classes \
-XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles \ -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles \
-Dtest.java.dyn.JavaDocExamples.verbosity=1 \ -Dtest.java.dyn.JavaDocExamplesTest.verbosity=1 \
test.java.dyn.JavaDocExamples test.java.dyn.JavaDocExamplesTest
---- ----
*/ */
...@@ -57,15 +57,15 @@ import static org.junit.Assume.*; ...@@ -57,15 +57,15 @@ import static org.junit.Assume.*;
/** /**
* @author jrose * @author jrose
*/ */
public class JavaDocExamples { public class JavaDocExamplesTest {
/** Wrapper for running the JUnit tests in this module. /** Wrapper for running the JUnit tests in this module.
* Put JUnit on the classpath! * Put JUnit on the classpath!
*/ */
public static void main(String... ignore) { public static void main(String... ignore) {
org.junit.runner.JUnitCore.runClasses(JavaDocExamples.class); org.junit.runner.JUnitCore.runClasses(JavaDocExamplesTest.class);
} }
// How much output? // How much output?
static int verbosity = Integer.getInteger("test.java.dyn.JavaDocExamples.verbosity", 0); static int verbosity = Integer.getInteger("test.java.dyn.JavaDocExamplesTest.verbosity", 0);
{} {}
static final private Lookup LOOKUP = lookup(); static final private Lookup LOOKUP = lookup();
...@@ -120,9 +120,79 @@ assertEquals("xz", /*(String)*/ d12.invokeExact("x", 12, true, "z")); ...@@ -120,9 +120,79 @@ assertEquals("xz", /*(String)*/ d12.invokeExact("x", 12, true, "z"));
}} }}
} }
@Test public void testFilterArguments() 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)*/
MethodHandle upcase = lookup().findVirtual(String.class,
"toUpperCase", methodType(String.class));
assertEquals("xy", /*(String)*/ cat.invokeExact("x", "y")); // xy
MethodHandle f0 = filterArguments(cat, 0, upcase);
assertEquals("Xy", /*(String)*/ f0.invokeExact("x", "y")); // Xy
MethodHandle f1 = filterArguments(cat, 1, upcase);
assertEquals("xY", /*(String)*/ f1.invokeExact("x", "y")); // xY
MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
assertEquals("XY", /*(String)*/ f2.invokeExact("x", "y")); // XY
}}
}
static void assertEquals(Object exp, Object act) { static void assertEquals(Object exp, Object act) {
if (verbosity > 0) if (verbosity > 0)
System.out.println("result: "+act); System.out.println("result: "+act);
Assert.assertEquals(exp, act); Assert.assertEquals(exp, act);
} }
@Test public void testVolatileCallSite() throws Throwable {
{{
{} /// JAVADOC
MethodHandle strcat = MethodHandles.lookup()
.findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
MethodHandle trueCon = MethodHandles.constant(boolean.class, true);
MethodHandle falseCon = MethodHandles.constant(boolean.class, false);
VolatileCallSite switcher = new VolatileCallSite(trueCon, falseCon);
// following steps may be repeated to re-use the same switcher:
MethodHandle worker1 = strcat;
MethodHandle worker2 = MethodHandles.permuteArguments(strcat, strcat.type(), 1, 0);
MethodHandle worker = MethodHandles.guardWithTest(switcher.dynamicInvoker(), worker1, worker2);
System.out.println((String) worker.invokeExact("met", "hod")); // method
switcher.invalidate();
System.out.println((String) worker.invokeExact("met", "hod")); // hodmet
}}
}
static MethodHandle asList;
@Test public void testWithTypeHandler() throws Throwable {
{{
{} /// JAVADOC
MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList());
MethodHandle asList = lookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
JavaDocExamplesTest.asList = asList;
/*
static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
return asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
}
*/
MethodHandle collectingTypeHandler = lookup()
.findStatic(lookup().lookupClass(), "collectingTypeHandler",
methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
System.out.println(makeAnyList.invokeGeneric());
System.out.println(makeAnyList.invokeGeneric(1));
System.out.println(makeAnyList.invokeGeneric("two", "too"));
}}
}
static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
//System.out.println("Converting "+asList+" to "+newType);
MethodHandle conv = asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
//System.out.println(" =>"+conv);
return conv;
}
} }
...@@ -62,7 +62,6 @@ public class MethodHandlesTest { ...@@ -62,7 +62,6 @@ public class MethodHandlesTest {
// lookups, without exercising the actual method handle. // lookups, without exercising the actual method handle.
static boolean DO_MORE_CALLS = true; static boolean DO_MORE_CALLS = true;
@Test @Test
public void testFirst() throws Throwable { public void testFirst() throws Throwable {
verbosity += 9; try { verbosity += 9; try {
...@@ -458,7 +457,7 @@ public class MethodHandlesTest { ...@@ -458,7 +457,7 @@ public class MethodHandlesTest {
Exception noAccess = null; Exception noAccess = null;
try { try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.findStatic(defc, name, type); target = lookup.in(defc).findStatic(defc, name, type);
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
noAccess = ex; noAccess = ex;
} }
...@@ -469,16 +468,22 @@ public class MethodHandlesTest { ...@@ -469,16 +468,22 @@ public class MethodHandlesTest {
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected if (!positive) return; // negative test failed as expected
assertEquals(type, target.type()); assertEquals(type, target.type());
assertTrue(target.toString().contains(name)); // rough check assertNameStringContains(target, name);
if (!DO_MORE_CALLS && lookup != PRIVATE) return; if (!DO_MORE_CALLS && lookup != PRIVATE) return;
Object[] args = randomArgs(params); Object[] args = randomArgs(params);
printCalled(target, name, args); printCalled(target, name, args);
target.invokeVarargs(args); target.invokeWithArguments(args);
assertCalled(name, args); assertCalled(name, args);
if (verbosity >= 1) if (verbosity >= 1)
System.out.print(':'); System.out.print(':');
} }
// rough check of name string
static void assertNameStringContains(Object x, String s) {
if (x.toString().contains(s)) return;
assertEquals(s, x);
}
@Test @Test
public void testFindVirtual() throws Throwable { public void testFindVirtual() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
...@@ -522,7 +527,7 @@ public class MethodHandlesTest { ...@@ -522,7 +527,7 @@ public class MethodHandlesTest {
Exception noAccess = null; Exception noAccess = null;
try { try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.findVirtual(defc, methodName, type); target = lookup.in(defc).findVirtual(defc, methodName, type);
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
noAccess = ex; noAccess = ex;
} }
...@@ -535,12 +540,12 @@ public class MethodHandlesTest { ...@@ -535,12 +540,12 @@ public class MethodHandlesTest {
Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params); Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params);
MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf); MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
assertEquals(typeWithSelf, target.type()); assertEquals(typeWithSelf, target.type());
assertTrue(target.toString().contains(methodName)); // rough check assertNameStringContains(target, methodName);
if (!DO_MORE_CALLS && lookup != PRIVATE) return; if (!DO_MORE_CALLS && lookup != PRIVATE) return;
Object[] argsWithSelf = randomArgs(paramsWithSelf); Object[] argsWithSelf = randomArgs(paramsWithSelf);
if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc); if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc);
printCalled(target, name, argsWithSelf); printCalled(target, name, argsWithSelf);
target.invokeVarargs(argsWithSelf); target.invokeWithArguments(argsWithSelf);
assertCalled(name, argsWithSelf); assertCalled(name, argsWithSelf);
if (verbosity >= 1) if (verbosity >= 1)
System.out.print(':'); System.out.print(':');
...@@ -576,7 +581,8 @@ public class MethodHandlesTest { ...@@ -576,7 +581,8 @@ public class MethodHandlesTest {
Exception noAccess = null; Exception noAccess = null;
try { try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.findSpecial(defc, name, type, specialCaller); if (verbosity >= 5) System.out.println(" lookup => "+lookup.in(specialCaller));
target = lookup.in(specialCaller).findSpecial(defc, name, type, specialCaller);
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
noAccess = ex; noAccess = ex;
} }
...@@ -591,11 +597,11 @@ public class MethodHandlesTest { ...@@ -591,11 +597,11 @@ public class MethodHandlesTest {
assertEquals(type, target.type().dropParameterTypes(0,1)); assertEquals(type, target.type().dropParameterTypes(0,1));
Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params); Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params);
MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf); MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf);
assertTrue(target.toString().contains(name)); // rough check assertNameStringContains(target, name);
if (!DO_MORE_CALLS && lookup != PRIVATE && lookup != EXAMPLE) return; if (!DO_MORE_CALLS && lookup != PRIVATE && lookup != EXAMPLE) return;
Object[] args = randomArgs(paramsWithSelf); Object[] args = randomArgs(paramsWithSelf);
printCalled(target, name, args); printCalled(target, name, args);
target.invokeVarargs(args); target.invokeWithArguments(args);
assertCalled(name, args); assertCalled(name, args);
} }
...@@ -632,7 +638,7 @@ public class MethodHandlesTest { ...@@ -632,7 +638,7 @@ public class MethodHandlesTest {
Exception noAccess = null; Exception noAccess = null;
try { try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.bind(receiver, methodName, type); target = lookup.in(defc).bind(receiver, methodName, type);
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
noAccess = ex; noAccess = ex;
} }
...@@ -645,7 +651,7 @@ public class MethodHandlesTest { ...@@ -645,7 +651,7 @@ public class MethodHandlesTest {
assertEquals(type, target.type()); assertEquals(type, target.type());
Object[] args = randomArgs(params); Object[] args = randomArgs(params);
printCalled(target, name, args); printCalled(target, name, args);
target.invokeVarargs(args); target.invokeWithArguments(args);
Object[] argsWithReceiver = cat(array(Object[].class, receiver), args); Object[] argsWithReceiver = cat(array(Object[].class, receiver), args);
assertCalled(name, argsWithReceiver); assertCalled(name, argsWithReceiver);
if (verbosity >= 1) if (verbosity >= 1)
...@@ -705,9 +711,9 @@ public class MethodHandlesTest { ...@@ -705,9 +711,9 @@ public class MethodHandlesTest {
try { try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
if (isSpecial) if (isSpecial)
target = lookup.unreflectSpecial(rmethod, specialCaller); target = lookup.in(specialCaller).unreflectSpecial(rmethod, specialCaller);
else else
target = lookup.unreflect(rmethod); target = lookup.in(defc).unreflect(rmethod);
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
noAccess = ex; noAccess = ex;
} }
...@@ -737,7 +743,7 @@ public class MethodHandlesTest { ...@@ -737,7 +743,7 @@ public class MethodHandlesTest {
} }
Object[] argsMaybeWithSelf = randomArgs(paramsMaybeWithSelf); Object[] argsMaybeWithSelf = randomArgs(paramsMaybeWithSelf);
printCalled(target, name, argsMaybeWithSelf); printCalled(target, name, argsMaybeWithSelf);
target.invokeVarargs(argsMaybeWithSelf); target.invokeWithArguments(argsMaybeWithSelf);
assertCalled(name, argsMaybeWithSelf); assertCalled(name, argsMaybeWithSelf);
if (verbosity >= 1) if (verbosity >= 1)
System.out.print(':'); System.out.print(':');
...@@ -875,7 +881,7 @@ public class MethodHandlesTest { ...@@ -875,7 +881,7 @@ public class MethodHandlesTest {
if (isStatic) expType = expType.dropParameterTypes(0, 1); if (isStatic) expType = expType.dropParameterTypes(0, 1);
MethodHandle mh = lookup.unreflectGetter(f); MethodHandle mh = lookup.unreflectGetter(f);
assertSame(mh.type(), expType); assertSame(mh.type(), expType);
assertEquals(mh.toString(), fname); assertNameStringContains(mh, fname);
HasFields fields = new HasFields(); HasFields fields = new HasFields();
Object sawValue; Object sawValue;
Class<?> rtype = type; Class<?> rtype = type;
...@@ -947,7 +953,7 @@ public class MethodHandlesTest { ...@@ -947,7 +953,7 @@ public class MethodHandlesTest {
mh = lookup.findStaticSetter(fclass, fname, ftype); mh = lookup.findStaticSetter(fclass, fname, ftype);
else throw new InternalError(); else throw new InternalError();
assertSame(mh.type(), expType); assertSame(mh.type(), expType);
assertEquals(mh.toString(), fname); assertNameStringContains(mh, fname);
HasFields fields = new HasFields(); HasFields fields = new HasFields();
Object sawValue; Object sawValue;
Class<?> vtype = type; Class<?> vtype = type;
...@@ -1102,6 +1108,18 @@ public class MethodHandlesTest { ...@@ -1102,6 +1108,18 @@ public class MethodHandlesTest {
} }
} }
static MethodHandle typeHandler2(MethodHandle target, MethodType newType) {
MethodType oldType = target.type();
int oldArity = oldType.parameterCount();
int newArity = newType.parameterCount();
if (newArity < oldArity)
return MethodHandles.insertArguments(target, oldArity, "OPTIONAL");
else if (newArity > oldArity)
return MethodHandles.dropArguments(target, oldArity-1, newType.parameterType(oldArity-1));
else
return target; // attempt no further conversions
}
@Test @Test
public void testConvertArguments() throws Throwable { public void testConvertArguments() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
...@@ -1115,10 +1133,29 @@ public class MethodHandlesTest { ...@@ -1115,10 +1133,29 @@ public class MethodHandlesTest {
} }
void testConvert(MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable { void testConvert(MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
testConvert(true, id, rtype, name, params); testConvert(true, false, id, rtype, name, params);
testConvert(true, true, id, rtype, name, params);
} }
void testConvert(boolean positive, MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable { @Test
public void testTypeHandler() throws Throwable {
MethodHandle id = Callee.ofType(1);
MethodHandle th2 = PRIVATE.findStatic(MethodHandlesTest.class, "typeHandler2",
MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
MethodHandle id2 = id.withTypeHandler(th2);
testConvert(true, false, id2, null, "id", Object.class);
testConvert(true, true, id2, null, "id", Object.class);
if (true) return; //FIXME
testConvert(true, false, id2, null, "id", String.class); // FIXME: throws WMT
testConvert(false, true, id2, null, "id", String.class); // FIXME: should not succeed
testConvert(false, false, id2, null, "id", Object.class, String.class); //FIXME: array[1] line 1164
testConvert(true, true, id2, null, "id", Object.class, String.class);
testConvert(false, false, id2, null, "id");
testConvert(true, true, id2, null, "id");
}
void testConvert(boolean positive, boolean useAsType,
MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
countTest(positive); countTest(positive);
MethodType idType = id.type(); MethodType idType = id.type();
if (rtype == null) rtype = idType.returnType(); if (rtype == null) rtype = idType.returnType();
...@@ -1135,7 +1172,7 @@ public class MethodHandlesTest { ...@@ -1135,7 +1172,7 @@ public class MethodHandlesTest {
if (src != dst) if (src != dst)
convArgs[i] = castToWrapper(convArgs[i], dst); convArgs[i] = castToWrapper(convArgs[i], dst);
} }
Object convResult = id.invokeVarargs(convArgs); Object convResult = id.invokeWithArguments(convArgs);
{ {
Class<?> dst = newType.returnType(); Class<?> dst = newType.returnType();
Class<?> src = idType.returnType(); Class<?> src = idType.returnType();
...@@ -1145,7 +1182,10 @@ public class MethodHandlesTest { ...@@ -1145,7 +1182,10 @@ public class MethodHandlesTest {
MethodHandle target = null; MethodHandle target = null;
RuntimeException error = null; RuntimeException error = null;
try { try {
target = MethodHandles.convertArguments(id, newType); if (useAsType)
target = MethodHandles.convertArguments(id, newType);
else
target = id.asType(newType);
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
error = ex; error = ex;
} }
...@@ -1157,7 +1197,7 @@ public class MethodHandlesTest { ...@@ -1157,7 +1197,7 @@ public class MethodHandlesTest {
if (!positive) return; // negative test failed as expected if (!positive) return; // negative test failed as expected
assertEquals(newType, target.type()); assertEquals(newType, target.type());
printCalled(target, id.toString(), args); printCalled(target, id.toString(), args);
Object result = target.invokeVarargs(args); Object result = target.invokeWithArguments(args);
assertCalled(name, convArgs); assertCalled(name, convArgs);
assertEquals(convResult, result); assertEquals(convResult, result);
if (verbosity >= 1) if (verbosity >= 1)
...@@ -1279,7 +1319,7 @@ public class MethodHandlesTest { ...@@ -1279,7 +1319,7 @@ public class MethodHandlesTest {
MethodType outType = MethodType.methodType(Object.class, permTypes); MethodType outType = MethodType.methodType(Object.class, permTypes);
MethodHandle target = MethodHandles.convertArguments(ValueConversions.varargsList(outargs), outType); MethodHandle target = MethodHandles.convertArguments(ValueConversions.varargsList(outargs), outType);
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder); MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
Object result = newTarget.invokeVarargs(args); Object result = newTarget.invokeWithArguments(args);
Object expected = Arrays.asList(permArgs); Object expected = Arrays.asList(permArgs);
assertEquals(expected, result); assertEquals(expected, result);
} }
...@@ -1311,7 +1351,7 @@ public class MethodHandlesTest { ...@@ -1311,7 +1351,7 @@ public class MethodHandlesTest {
Object[] args = randomArgs(target2.type().parameterArray()); Object[] args = randomArgs(target2.type().parameterArray());
// make sure the target does what we think it does: // make sure the target does what we think it does:
if (pos == 0 && nargs < 5) { if (pos == 0 && nargs < 5) {
Object[] check = (Object[]) target.invokeVarargs(args); Object[] check = (Object[]) target.invokeWithArguments(args);
assertArrayEquals(args, check); assertArrayEquals(args, check);
switch (nargs) { switch (nargs) {
case 0: case 0:
...@@ -1342,7 +1382,7 @@ public class MethodHandlesTest { ...@@ -1342,7 +1382,7 @@ public class MethodHandlesTest {
} else { } else {
Object[] args1 = Arrays.copyOfRange(args, 0, pos+1); Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
args1[pos] = Arrays.copyOfRange(args, pos, args.length); args1[pos] = Arrays.copyOfRange(args, pos, args.length);
returnValue = (Object[]) result.invokeVarargs(args1); returnValue = (Object[]) result.invokeWithArguments(args1);
} }
assertArrayEquals(args, returnValue); assertArrayEquals(args, returnValue);
} }
...@@ -1379,7 +1419,7 @@ public class MethodHandlesTest { ...@@ -1379,7 +1419,7 @@ public class MethodHandlesTest {
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]"); System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]");
MethodHandle result = MethodHandles.collectArguments(target, newType); MethodHandle result = MethodHandles.collectArguments(target, newType);
Object[] returnValue = (Object[]) result.invokeVarargs(args); Object[] returnValue = (Object[]) result.invokeWithArguments(args);
// assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]); // assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]);
// returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]); // returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]);
// collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]); // collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]);
...@@ -1412,7 +1452,7 @@ public class MethodHandlesTest { ...@@ -1412,7 +1452,7 @@ public class MethodHandlesTest {
MethodHandle target2 = MethodHandles.insertArguments(target, pos, MethodHandle target2 = MethodHandles.insertArguments(target, pos,
(Object[]) argsToInsert.toArray()); (Object[]) argsToInsert.toArray());
argsToInsert.clear(); // remove from argsToInsert argsToInsert.clear(); // remove from argsToInsert
Object res2 = target2.invokeVarargs(argsToPass); Object res2 = target2.invokeWithArguments(argsToPass);
Object res2List = Arrays.asList((Object[])res2); Object res2List = Arrays.asList((Object[])res2);
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("result: "+res2List); System.out.println("result: "+res2List);
...@@ -1440,14 +1480,12 @@ public class MethodHandlesTest { ...@@ -1440,14 +1480,12 @@ public class MethodHandlesTest {
Object[] argsToPass = randomArgs(nargs, Object.class); Object[] argsToPass = randomArgs(nargs, Object.class);
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("filter "+target+" at "+pos+" with "+filter); System.out.println("filter "+target+" at "+pos+" with "+filter);
MethodHandle[] filters = new MethodHandle[pos*2+1]; MethodHandle target2 = MethodHandles.filterArguments(target, pos, filter);
filters[pos] = filter;
MethodHandle target2 = MethodHandles.filterArguments(target, filters);
// Simulate expected effect of filter on arglist: // Simulate expected effect of filter on arglist:
Object[] filteredArgs = argsToPass.clone(); Object[] filteredArgs = argsToPass.clone();
filteredArgs[pos] = filter.invokeExact(filteredArgs[pos]); filteredArgs[pos] = filter.invokeExact(filteredArgs[pos]);
List<Object> expected = Arrays.asList(filteredArgs); List<Object> expected = Arrays.asList(filteredArgs);
Object result = target2.invokeVarargs(argsToPass); Object result = target2.invokeWithArguments(argsToPass);
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("result: "+result); System.out.println("result: "+result);
if (!expected.equals(result)) if (!expected.equals(result))
...@@ -1482,9 +1520,9 @@ public class MethodHandlesTest { ...@@ -1482,9 +1520,9 @@ public class MethodHandlesTest {
List<Object> argsToFold = expected.subList(pos, pos + fold); List<Object> argsToFold = expected.subList(pos, pos + fold);
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("fold: "+argsToFold+" into "+target2); System.out.println("fold: "+argsToFold+" into "+target2);
Object foldedArgs = combine.invokeVarargs(argsToFold); Object foldedArgs = combine.invokeWithArguments(argsToFold);
argsToFold.add(0, foldedArgs); argsToFold.add(0, foldedArgs);
Object result = target2.invokeVarargs(argsToPass); Object result = target2.invokeWithArguments(argsToPass);
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("result: "+result); System.out.println("result: "+result);
if (!expected.equals(result)) if (!expected.equals(result))
...@@ -1516,7 +1554,7 @@ public class MethodHandlesTest { ...@@ -1516,7 +1554,7 @@ public class MethodHandlesTest {
for (int i = drop; i > 0; i--) { for (int i = drop; i > 0; i--) {
argsToDrop.add(pos, "blort#"+i); argsToDrop.add(pos, "blort#"+i);
} }
Object res2 = target2.invokeVarargs(argsToDrop); Object res2 = target2.invokeWithArguments(argsToDrop);
Object res2List = Arrays.asList((Object[])res2); Object res2List = Arrays.asList((Object[])res2);
//if (!resList.equals(res2List)) //if (!resList.equals(res2List))
// System.out.println("*** fail at n/p/d = "+nargs+"/"+pos+"/"+drop+": "+argsToDrop+" => "+res2List); // System.out.println("*** fail at n/p/d = "+nargs+"/"+pos+"/"+drop+": "+argsToDrop+" => "+res2List);
...@@ -1572,7 +1610,7 @@ public class MethodHandlesTest { ...@@ -1572,7 +1610,7 @@ public class MethodHandlesTest {
countTest(); countTest();
calledLog.clear(); calledLog.clear();
inv = MethodHandles.exactInvoker(type); inv = MethodHandles.exactInvoker(type);
result = inv.invokeVarargs(targetPlusArgs); result = inv.invokeWithArguments(targetPlusArgs);
if (testRetCode) assertEquals(code, result); if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args); assertCalled("invokee", args);
// generic invoker // generic invoker
...@@ -1598,7 +1636,7 @@ public class MethodHandlesTest { ...@@ -1598,7 +1636,7 @@ public class MethodHandlesTest {
assertCalled("invokee", args); assertCalled("invokee", args);
} }
calledLog.clear(); calledLog.clear();
result = inv.invokeVarargs(targetPlusArgs); result = inv.invokeWithArguments(targetPlusArgs);
if (testRetCode) assertEquals(code, result); if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args); assertCalled("invokee", args);
// varargs invoker #0 // varargs invoker #0
...@@ -1640,17 +1678,29 @@ public class MethodHandlesTest { ...@@ -1640,17 +1678,29 @@ public class MethodHandlesTest {
List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs); List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
Object[] tail = tailList.toArray(); Object[] tail = tailList.toArray();
tailList.clear(); tailList.add(tail); tailList.clear(); tailList.add(tail);
result = inv.invokeVarargs(targetPlusVarArgs); result = inv.invokeWithArguments(targetPlusVarArgs);
if (testRetCode) assertEquals(code, result); if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args); assertCalled("invokee", args);
} }
// dynamic invoker // dynamic invoker
countTest(); countTest();
CallSite site = new CallSite(MethodHandlesTest.class, "foo", type); CallSite site = new CallSite(type);
inv = MethodHandles.dynamicInvoker(site); inv = MethodHandles.dynamicInvoker(site);
// see if we get the result of the original target:
try {
result = inv.invokeWithArguments(args);
assertTrue("should not reach here", false);
} catch (IllegalStateException ex) {
String msg = ex.getMessage();
assertTrue(msg, msg.contains("site"));
}
// set new target after invoker is created, to make sure we track target
site.setTarget(target); site.setTarget(target);
calledLog.clear(); calledLog.clear();
result = inv.invokeVarargs(args); result = inv.invokeWithArguments(args);
if (testRetCode) assertEquals(code, result); if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args); assertCalled("invokee", args);
} }
...@@ -1734,7 +1784,7 @@ public class MethodHandlesTest { ...@@ -1734,7 +1784,7 @@ public class MethodHandlesTest {
String willCall = (equals ? "targetIfEquals" : "fallbackIfNotEquals"); String willCall = (equals ? "targetIfEquals" : "fallbackIfNotEquals");
if (verbosity >= 3) if (verbosity >= 3)
System.out.println(logEntry(willCall, argList)); System.out.println(logEntry(willCall, argList));
Object result = mh.invokeVarargs(argList); Object result = mh.invokeWithArguments(argList);
assertCalled(willCall, argList); assertCalled(willCall, argList);
} }
} }
...@@ -1776,7 +1826,7 @@ public class MethodHandlesTest { ...@@ -1776,7 +1826,7 @@ public class MethodHandlesTest {
//System.out.println("catching with "+target+" : "+throwOrReturn); //System.out.println("catching with "+target+" : "+throwOrReturn);
Object[] args = randomArgs(nargs, Object.class); Object[] args = randomArgs(nargs, Object.class);
args[1] = (throwIt ? thrown : null); args[1] = (throwIt ? thrown : null);
Object returned = target.invokeVarargs(args); Object returned = target.invokeWithArguments(args);
//System.out.println("return from "+target+" : "+returned); //System.out.println("return from "+target+" : "+returned);
if (!throwIt) { if (!throwIt) {
assertSame(args[0], returned); assertSame(args[0], returned);
...@@ -1828,13 +1878,10 @@ public class MethodHandlesTest { ...@@ -1828,13 +1878,10 @@ public class MethodHandlesTest {
testCastFailure("unbox/return", 11000); testCastFailure("unbox/return", 11000);
} }
static class Surprise implements MethodHandleProvider { static class Surprise {
public MethodHandle asMethodHandle() { public MethodHandle asMethodHandle() {
return VALUE.bindTo(this); return VALUE.bindTo(this);
} }
public MethodHandle asMethodHandle(MethodType type) {
return asMethodHandle().asType(type);
}
Object value(Object x) { Object value(Object x) {
trace("value", x); trace("value", x);
if (boo != null) return boo; if (boo != null) return boo;
...@@ -1896,8 +1943,8 @@ public class MethodHandlesTest { ...@@ -1896,8 +1943,8 @@ public class MethodHandlesTest {
} }
if (callee != null) { if (callee != null) {
callee = MethodHandles.convertArguments(callee, MethodType.genericMethodType(1)); callee = MethodHandles.convertArguments(callee, MethodType.genericMethodType(1));
surprise = MethodHandles.filterArguments(callee, surprise); surprise = MethodHandles.filterArguments(callee, 0, surprise);
identity = MethodHandles.filterArguments(callee, identity); identity = MethodHandles.filterArguments(callee, 0, identity);
} }
} }
assertNotSame(mode, surprise, surprise0); assertNotSame(mode, surprise, surprise0);
...@@ -1949,7 +1996,7 @@ public class MethodHandlesTest { ...@@ -1949,7 +1996,7 @@ public class MethodHandlesTest {
assertEquals(mt, mh.type()); assertEquals(mt, mh.type());
assertEquals(Example.class, mh.type().returnType()); assertEquals(Example.class, mh.type().returnType());
args = randomArgs(mh.type().parameterArray()); args = randomArgs(mh.type().parameterArray());
mh.invokeVarargs(args); mh.invokeWithArguments(args);
assertCalled(name, args); assertCalled(name, args);
// Try a virtual method. // Try a virtual method.
...@@ -1959,7 +2006,7 @@ public class MethodHandlesTest { ...@@ -1959,7 +2006,7 @@ public class MethodHandlesTest {
assertEquals(mt, mh.type().dropParameterTypes(0,1)); assertEquals(mt, mh.type().dropParameterTypes(0,1));
assertTrue(mh.type().parameterList().contains(Example.class)); assertTrue(mh.type().parameterList().contains(Example.class));
args = randomArgs(mh.type().parameterArray()); args = randomArgs(mh.type().parameterArray());
mh.invokeVarargs(args); mh.invokeWithArguments(args);
assertCalled(name, args); assertCalled(name, args);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册