提交 4eb43f64 编写于 作者: J jrose

6914665: update jdk code for JSR 292 (post 6858164)

Summary: Fill in missing API implementations, fix numerous bugs, adjust APIs towards EG design.
Reviewed-by: twisti
上级 6145be84
......@@ -26,6 +26,9 @@
package java.dyn;
import sun.dyn.util.BytecodeName;
import sun.dyn.Access;
import sun.dyn.CallSiteImpl;
import sun.dyn.MethodHandleImpl;
/**
* An {@code invokedynamic} call site, as reified by the
......@@ -52,15 +55,25 @@ import sun.dyn.util.BytecodeName;
* @see Linkage#registerBootstrapMethod(java.lang.Class, java.dyn.MethodHandle)
* @author John Rose, JSR 292 EG
*/
public class CallSite {
public class CallSite
// Note: This is an implementation inheritance hack, and will be removed
// with a JVM change which moves the required hidden state onto this class.
extends CallSiteImpl
{
private static final Access IMPL_TOKEN = Access.getToken();
/*
// Fields used only by the JVM. Do not use or change.
private Object vmmethod;
int callerMID, callerBCI; // supplied by the JVM
MethodHandle target;
private MethodHandle target;
final Object caller; // usually a class
final String name;
final MethodType type;
*/
/**
* Make a call site given the parameters from a call to the bootstrap method.
......@@ -72,16 +85,21 @@ public class CallSite {
* @param type the method handle type derived from descriptor of the {@code invokedynamic} instruction
*/
public CallSite(Object caller, String name, MethodType type) {
this.caller = caller;
this.name = name;
this.type = type;
super(IMPL_TOKEN, caller, name, type);
}
private static void privateInitializeCallSite(CallSite site, int callerMID, int callerBCI) {
site.callerMID = callerMID;
site.callerBCI = callerBCI;
if (site.target == null)
site.setTarget(site.initialTarget());
site.ensureTarget();
}
private void ensureTarget() {
// Note use of super, which accesses the field directly,
// without deferring to possible subclass overrides.
if (super.getTarget() == null) {
super.setTarget(this.initialTarget());
super.getTarget().type(); // provoke NPE if still null
}
}
/**
......@@ -102,10 +120,11 @@ public class CallSite {
/**
* Report the current linkage state of the call site. (This is mutable.)
* The value maybe null only if the call site is currently unlinked.
* When a linked call site is invoked, the target method is used directly.
* When an unlinked call site is invoked, its bootstrap method receives
* the call, as if via {@link Linkage#bootstrapInvokeDynamic}.
* 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>
* 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
......@@ -118,7 +137,7 @@ public class CallSite {
* @see #setTarget
*/
public MethodHandle getTarget() {
return target;
return super.getTarget();
}
/**
......@@ -140,13 +159,13 @@ public class CallSite {
*/
public void setTarget(MethodHandle target) {
checkTarget(target);
this.target = target;
super.setTarget(target);
}
protected void checkTarget(MethodHandle target) {
target.type(); // provoke NPE
if (!canSetTarget(target))
throw new WrongMethodTypeException(String.valueOf(target));
throw new WrongMethodTypeException(String.valueOf(target)+target.type()+" should be of type "+type());
}
protected boolean canSetTarget(MethodHandle target) {
......@@ -219,6 +238,10 @@ public class CallSite {
@Override
public String toString() {
return "CallSite#"+hashCode()+"["+name+type+" => "+target+"]";
return "CallSite#"+hashCode()+"["+name+type+" => "+getTarget()+"]";
}
// Package-local constant:
static final MethodHandle GET_TARGET = MethodHandleImpl.getLookup(IMPL_TOKEN).
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
}
......@@ -45,6 +45,24 @@ package java.dyn;
* 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 (an importable one) of static calls.
* <p>
* Here are some examples of usage:
* <p><blockquote><pre>
* Object x; String s; int i;
* x = InvokeDynamic.greet("world"); // greet(Ljava/lang/String;)Ljava/lang/Object;
* s = InvokeDynamic.&lt;String&gt;hail(x); // hail(Ljava/lang/Object;)Ljava/lang/String;
* InvokeDynamic.&lt;void&gt;cogito(); // cogito()V
* i = InvokeDynamic.&lt;int&gt;#"op:+"(2, 3); // "op:+"(II)I
* </pre></blockquote>
* Each of the above calls generates a single invokedynamic instruction
* with the name-and-type descriptors indicated in the comments.
* The argument types are taken directly from the actual arguments,
* while the return type is taken from the type parameter.
* (This type parameter may be a primtive, and it defaults to {@code Object}.)
* 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.
* None of these calls is complete without a bootstrap method,
* which must be registered by the static initializer of the enclosing class.
* @author John Rose, JSR 292 EG
*/
public final class InvokeDynamic {
......
......@@ -52,4 +52,16 @@ public class InvokeDynamicBootstrapError extends LinkageError {
public InvokeDynamicBootstrapError(String s) {
super(s);
}
/**
* Constructs a {@code InvokeDynamicBootstrapError} with the specified
* detail message and cause.
*
* @param s the detail message.
* @param cause the cause.
*/
public InvokeDynamicBootstrapError(String s, Throwable cause) {
super(s);
this.initCause(cause);
}
}
......@@ -25,6 +25,8 @@
package java.dyn;
import sun.dyn.Access;
/**
* A Java method handle extends the basic method handle type with additional
* programmer defined methods and fields.
......@@ -39,31 +41,105 @@ package java.dyn;
* of the entry point method handle, with the leading parameter type
* omitted.
* <p>
* Here is an example of usage:
* 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;invoke(); // also prints "hello, world"
* // Dynamically typed method handle invocation:
* MethodHandles.invoke(greeter); // also prints "hello, world"
* greeter.setGreeting("howdy");
* mh.invoke(); // 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>
* class Greeter extends JavaMethodHandle {
* // We can also do this with symbolic names and/or inner classes:
* MethodHandles.invoke(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) {
* super(RUN);
* this.greetee = greetee;
* }
* Greeter(String greetee) { this.greetee = greetee; }
* // the entry point function is computed once:
* private static final MethodHandle RUN
* = MethodHandles.findVirtual(MyMethodHandle.class, "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 = greeter;
* MethodHandle mh = MethodHanndles.insertArgument(Greeter.RUN, 0, greeter);
* mh.invoke(); // 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>
* In this example, 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 Java method handle class, though
* that is the typical case.
* 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.invoke(); // 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;invoke(1L); // 456
* stuffPtr.setter().&lt;void&gt;invoke(0L, (Number) 789); // replaces 123 with 789
* </pre></blockquote>
* @see MethodHandle
* @author John Rose, JSR 292 EG
*/
......@@ -72,12 +148,87 @@ public abstract class JavaMethodHandle
// 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, pass in {@code entryPoint}, any method handle which
* can take the current object
* @param entryPoint
* 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, 0);
super(entryPoint);
}
/**
* Create a method handle whose entry point is a non-static method
* visible in the exact (most specific) class of
* the newly constructed object.
* <p>
* The method is specified by name and type, as if via this expression:
* {@code MethodHandles.lookup().findVirtual(this.getClass(), name, type)}.
* The class defining the method might be an anonymous inner class.
* <p>
* The method handle type of {@code this} (i.e, the fully constructed object)
* will be the given method handle type.
* A call to {@code this} will invoke the selected method.
* The receiver argument will be bound to {@code this} on every method
* handle invocation.
* <p>
* <i>Rationale:</i>
* Although this constructor may seem to be a mere luxury,
* it is not subsumed by the more general constructor which
* takes any {@code MethodHandle} as the entry point argument.
* In order to convert an entry point name to a method handle,
* the self-class of the object is required (in order to do
* the lookup). The self-class, in turn, is generally not
* available at the time of the constructor invocation,
* due to the rules of Java and the JVM verifier.
* One cannot call {@code this.getClass()}, because
* the value of {@code this} is inaccessible at the point
* of the constructor call. (Changing this would require
* change to the Java language, verifiers, and compilers.)
* In particular, this constructor allows {@code JavaMethodHandle}s
* to be created in combination with the anonymous inner class syntax.
* @param entryPointName the name of the entry point method
* @param type (optional) the desired type of the method handle
*/
protected JavaMethodHandle(String entryPointName, MethodType type) {
super(entryPointName, type, true);
}
/**
* Create a method handle whose entry point is a non-static method
* visible in the exact (most specific) class of
* the newly constructed object.
* <p>
* The method is specified only by name.
* There must be exactly one method of that name visible in the object class,
* either inherited or locally declared.
* (That is, the method must not be overloaded.)
* <p>
* The method handle type of {@code this} (i.e, the fully constructed object)
* will be the same as the type of the selected non-static method.
* The receiver argument will be bound to {@code this} on every method
* handle invocation.
* <p>ISSUE: This signature wildcarding feature does not correspond to
* any MethodHandles.Lookup API element. Can we eliminate it?
* Alternatively, it is useful for naming non-overloaded methods.
* Shall we make type arguments optional in the Lookup methods,
* throwing an error in cases of ambiguity?
* <p>
* For this method's rationale, see the documentation
* for {@link #JavaMethodHandle(String,MethodType)}.
* @param entryPointName the name of the entry point method
*/
protected JavaMethodHandle(String entryPointName) {
super(entryPointName, (MethodType) null, false);
}
}
......@@ -25,7 +25,9 @@
package java.dyn;
import java.dyn.MethodHandles.Lookup;
import java.util.WeakHashMap;
import sun.dyn.Access;
import sun.reflect.Reflection;
import static sun.dyn.util.VerifyAccess.checkBootstrapPrivilege;
......@@ -34,6 +36,8 @@ import static sun.dyn.util.VerifyAccess.checkBootstrapPrivilege;
* @author John Rose, JSR 292 EG
*/
public class Linkage {
private static final Access IMPL_TOKEN = Access.getToken();
private Linkage() {} // do not instantiate
/**
......@@ -53,19 +57,23 @@ public class Linkage {
* call to this method.
* <li>The given class is already fully initialized.
* <li>The given class is in the process of initialization, in another thread.
* <li>The same {@code CallSite} object has already been returned from
* a bootstrap method call to another {@code invokedynamic} call site.
* </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
*/
public static
void registerBootstrapMethod(Class callerClass, MethodHandle mh) {
void registerBootstrapMethod(Class callerClass, MethodHandle bootstrapMethod) {
Class callc = Reflection.getCallerClass(2);
checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod");
checkBSM(mh);
checkBSM(bootstrapMethod);
synchronized (bootstrapMethods) {
if (bootstrapMethods.containsKey(callerClass))
throw new IllegalStateException("bootstrap method already declared in "+callerClass);
bootstrapMethods.put(callerClass, mh);
bootstrapMethods.put(callerClass, bootstrapMethod);
}
}
......@@ -88,8 +96,9 @@ public class Linkage {
public static
void registerBootstrapMethod(Class<?> runtime, String name) {
Class callc = Reflection.getCallerClass(2);
Lookup lookup = new Lookup(IMPL_TOKEN, callc);
MethodHandle bootstrapMethod =
MethodHandles.findStaticFrom(callc, runtime, name, BOOTSTRAP_METHOD_TYPE);
lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE);
// FIXME: exception processing wrong here
checkBSM(bootstrapMethod);
Linkage.registerBootstrapMethod(callc, bootstrapMethod);
......@@ -106,8 +115,9 @@ public class Linkage {
public static
void registerBootstrapMethod(String name) {
Class callc = Reflection.getCallerClass(2);
Lookup lookup = new Lookup(IMPL_TOKEN, callc);
MethodHandle bootstrapMethod =
MethodHandles.findStaticFrom(callc, callc, name, BOOTSTRAP_METHOD_TYPE);
lookup.findStatic(callc, name, BOOTSTRAP_METHOD_TYPE);
// FIXME: exception processing wrong here
checkBSM(bootstrapMethod);
Linkage.registerBootstrapMethod(callc, bootstrapMethod);
......@@ -116,8 +126,7 @@ public class Linkage {
/**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* Report the bootstrap method registered for a given class.
* Returns null if the class has never yet registered a bootstrap method,
* or if the class has explicitly registered a null bootstrap method.
* 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.
......@@ -137,12 +146,12 @@ public class Linkage {
* {@code (Class, String, MethodType)} returning a {@code CallSite}.
*/
public static final MethodType BOOTSTRAP_METHOD_TYPE
= MethodType.make(CallSite.class,
Class.class, String.class, MethodType.class);
= MethodType.methodType(CallSite.class,
Class.class, String.class, MethodType.class);
private static final MethodType OLD_BOOTSTRAP_METHOD_TYPE
= MethodType.make(Object.class,
CallSite.class, Object[].class);
= MethodType.methodType(Object.class,
CallSite.class, Object[].class);
private static final WeakHashMap<Class, MethodHandle> bootstrapMethods =
new WeakHashMap<Class, MethodHandle>();
......@@ -173,8 +182,8 @@ public class Linkage {
/**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* Invalidate all <code>invokedynamic</code> call sites associated
* with the given class.
* Invalidate all <code>invokedynamic</code> call sites in the bytecodes
* of any methods of the given class.
* (These are exactly those sites which report the given class
* via the {@link CallSite#callerClass()} method.)
* <p>
......
......@@ -88,7 +88,7 @@ public final class LinkagePermission extends BasicPermission {
/**
* Create a new LinkagePermission with the given name.
* The name is the symbolic name of the LinkagePermission, such as
* "registerBootstrapMethod", "invalidateClass.*", etc. An asterisk
* "registerBootstrapMethod", "invalidateCallerClass.*", etc. An asterisk
* may appear at the end of the name, following a ".", or by itself, to
* signify a wildcard match.
*
......
......@@ -32,7 +32,7 @@ import java.util.List;
import sun.dyn.Access;
import sun.dyn.Invokers;
import sun.dyn.MethodTypeImpl;
import sun.dyn.util.BytecodeSignature;
import sun.dyn.util.BytecodeDescriptor;
import static sun.dyn.MemberName.newIllegalArgumentException;
/**
......@@ -63,7 +63,7 @@ class MethodType {
static {
// This hack allows the implementation package special access to
// the internals of MethodType. In particular, the Form has all sorts
// the internals of MethodType. In particular, the MTImpl has all sorts
// of cached information useful to the implementation code.
MethodTypeImpl.setMethodTypeFriend(IMPL_TOKEN, new MethodTypeImpl.MethodTypeFriend() {
public Class<?>[] ptypes(MethodType mt) { return mt.ptypes; }
......@@ -114,51 +114,76 @@ class MethodType {
* @throws IllegalArgumentException if any of the ptypes is void
*/
public static
MethodType make(Class<?> rtype, Class<?>[] ptypes) {
MethodType methodType(Class<?> rtype, Class<?>[] ptypes) {
return makeImpl(rtype, ptypes, false);
}
@Deprecated public static
MethodType make(Class<?> rtype, Class<?>[] ptypes) {
return methodType(rtype, ptypes);
}
/** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. */
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. */
public static
MethodType methodType(Class<?> rtype, List<? extends Class<?>> ptypes) {
boolean notrust = false; // random List impl. could return evil ptypes array
return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust);
}
@Deprecated public static
MethodType make(Class<?> rtype, List<? extends Class<?>> ptypes) {
return makeImpl(rtype, ptypes.toArray(NO_PTYPES), true);
return methodType(rtype, ptypes);
}
/** Convenience method for {@link #make(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.
*/
public static
MethodType make(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
Class<?>[] ptypes1 = new Class<?>[1+ptypes.length];
ptypes1[0] = ptype0;
System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length);
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 #make(java.lang.Class, java.lang.Class[])}.
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* The resulting method has no parameter types.
*/
public static
MethodType make(Class<?> rtype) {
MethodType methodType(Class<?> rtype) {
return makeImpl(rtype, NO_PTYPES, true);
}
@Deprecated public static
MethodType make(Class<?> rtype) {
return methodType(rtype);
}
/** Convenience method for {@link #make(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.
*/
public static
MethodType make(Class<?> rtype, Class<?> ptype0) {
MethodType methodType(Class<?> rtype, Class<?> ptype0) {
return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
}
@Deprecated public static
MethodType make(Class<?> rtype, Class<?> ptype0) {
return methodType(rtype, ptype0);
}
/** Convenience method for {@link #make(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},
* and the specified return type.
*/
public static
MethodType make(Class<?> rtype, MethodType ptypes) {
MethodType methodType(Class<?> rtype, MethodType ptypes) {
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.
......@@ -202,15 +227,16 @@ class MethodType {
private static final MethodType[] objectOnlyTypes = new MethodType[20];
/**
* Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
* All parameters and the return type will be Object, except the final varargs parameter if any.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* All parameters and the return type will be {@code Object},
* except the final varargs parameter if any, which will be {@code Object[]}.
* @param objectArgCount number of parameters (excluding the varargs parameter if any)
* @param varargs whether there will be a varargs parameter, of type Object[]
* @param varargs whether there will be a varargs parameter, of type {@code Object[]}
* @return a totally generic method type, given only its count of parameters and varargs
* @see #makeGeneric(int)
* @see #genericMethodType(int)
*/
public static
MethodType makeGeneric(int objectArgCount, boolean varargs) {
MethodType genericMethodType(int objectArgCount, boolean varargs) {
MethodType mt;
int ivarargs = (!varargs ? 0 : 1);
int ootIndex = objectArgCount*2 + ivarargs;
......@@ -227,19 +253,27 @@ class MethodType {
}
return mt;
}
@Deprecated public static
MethodType makeGeneric(int objectArgCount, boolean varargs) {
return genericMethodType(objectArgCount, varargs);
}
/**
* All parameters and the return type will be Object.
* @param objectArgCount number of parameters
* @return a totally generic method type, given only its count of parameters
* @see #makeGeneric(int, boolean)
* @see #genericMethodType(int, boolean)
*/
public static
MethodType genericMethodType(int objectArgCount) {
return genericMethodType(objectArgCount, false);
}
@Deprecated public static
MethodType makeGeneric(int objectArgCount) {
return makeGeneric(objectArgCount, false);
return genericMethodType(objectArgCount);
}
/** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param num the index (zero-based) of the parameter type to change
* @param nptype a new parameter type to replace the old one with
* @return the same type, except with the selected parameter changed
......@@ -251,11 +285,10 @@ class MethodType {
return makeImpl(rtype, nptypes, true);
}
/** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
* @param num the position (zero-based) of the inserted parameter type
* @param nptype a new parameter type to insert into the parameter list
* @return the same type, except with the selected parameter inserted
/** 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);
......@@ -264,23 +297,73 @@ class MethodType {
return makeImpl(rtype, nptypes, true);
}
/** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
* @param num the index (zero-based) of the parameter type to remove
* @return the same type, except with the selected parameter removed
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @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
* @return the same type, except with the selected parameter(s) inserted
*/
public MethodType dropParameterType(int num) {
public MethodType insertParameterTypes(int num, Class<?>... ptypesToInsert) {
int len = ptypes.length;
if (num < 0 || num > len)
throw newIllegalArgumentException("num="+num); //SPECME
int ilen = ptypesToInsert.length;
if (ilen == 0) return this;
Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+ilen);
System.arraycopy(nptypes, num, nptypes, num+ilen, len-num);
System.arraycopy(ptypesToInsert, 0, nptypes, num, ilen);
return makeImpl(rtype, nptypes, true);
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @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
* @return the same type, except with the selected parameter(s) inserted
*/
public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) {
return insertParameterTypes(num, ptypesToInsert.toArray(NO_PTYPES));
}
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param start the index (zero-based) of the first parameter type to remove
* @param end the index (greater than {@code start}) of the first parameter type after not to remove
* @return the same type, except with the selected parameter(s) removed
*/
public MethodType dropParameterTypes(int start, int end) {
int len = ptypes.length;
if (!(0 <= start && start <= end && end <= len))
throw newIllegalArgumentException("start="+start+" end="+end); //SPECME
if (start == end) return this;
Class<?>[] nptypes;
if (num == 0) {
nptypes = Arrays.copyOfRange(ptypes, 1, len);
if (start == 0) {
if (end == len) {
// drop all parameters
nptypes = NO_PTYPES;
} else {
// drop initial parameter(s)
nptypes = Arrays.copyOfRange(ptypes, end, len);
}
} else {
nptypes = Arrays.copyOfRange(ptypes, 0, len-1);
System.arraycopy(ptypes, num+1, nptypes, num, (len-1)-num);
if (end == len) {
// drop trailing parameter(s)
nptypes = Arrays.copyOfRange(ptypes, 0, start);
} else {
int tail = len - end;
nptypes = Arrays.copyOfRange(ptypes, 0, start + tail);
System.arraycopy(ptypes, end, nptypes, start, tail);
}
}
return makeImpl(rtype, nptypes, true);
}
/** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
/** 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[])}.
* @param nrtype a return parameter type to replace the old one with
* @return the same type, except with the return type change
*/
......@@ -291,6 +374,7 @@ class MethodType {
/** Convenience method.
* Report if this type contains a primitive argument or return value.
* The return type {@code void} counts as a primitive.
* @return true if any of the types are primitives
*/
public boolean hasPrimitives() {
......@@ -300,39 +384,47 @@ class MethodType {
/** Convenience method.
* Report if this type contains a wrapper argument or return value.
* Wrappers are types which box primitive values, such as {@link Integer}.
* The reference type {@code java.lang.Void} counts as a wrapper.
* @return true if any of the types are wrappers
*/
public boolean hasWrappers() {
return unwrap() != this;
}
/** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
* Erase all reference types to Object.
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Erase all reference types to {@code Object}.
* All primitive types (including {@code void}) will remain unchanged.
* @return a version of the original type with all reference types replaced
*/
public MethodType erase() {
return form.erasedType();
}
/** Convenience method for {@link #makeGeneric(int)}.
* Convert all types, both reference and primitive, to Object.
/** Convenience method for {@link #genericMethodType(int)}.
* Convert all types, both reference and primitive, to {@code Object}.
* The expression {@code type.wrap().erase()} produces the same value
* as {@code type.generic()}.
* @return a version of the original type with all types replaced
*/
public MethodType generic() {
return makeGeneric(parameterCount());
return genericMethodType(parameterCount());
}
/** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Convert all primitive types to their corresponding wrapper types.
* All reference types (including wrapper types) will remain unchanged.
* A {@code void} return type is changed to the type {@code java.lang.Void}.
* The expression {@code type.wrap().erase()} produces the same value
* as {@code type.generic()}.
* @return a version of the original type with all primitive types replaced
*/
public MethodType wrap() {
return hasPrimitives() ? wrapWithPrims(this) : this;
}
/** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Convert all wrapper types to their corresponding primitive types.
* All primitive types (including {@code void}) will remain unchanged.
* A return type of {@code java.lang.Void} is changed to {@code void}.
* @return a version of the original type with all wrapper types replaced
*/
......@@ -391,6 +483,7 @@ class MethodType {
/**
* Convenience method to present the arguments as an array.
* Changes to the array will not result in changes to the type.
* @return the parameter types (as a fresh copy if necessary)
*/
public Class<?>[] parameterArray() {
......@@ -491,7 +584,7 @@ class MethodType {
return form.parameterSlotCount();
}
/** Number of JVM stack slots which carry all parameters after
/** Number of JVM stack slots which carry all parameters including and after
* the given position, which must be in the range of 0 to
* {@code parameterCount} inclusive. Successive parameters are
* more shallowly stacked, and parameters are indexed in the bytecodes
......@@ -532,7 +625,7 @@ class MethodType {
return form.returnSlotCount();
}
/** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* Find or create an instance (interned) of the given method type.
* Any class or interface name embedded in the signature string
* will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
......@@ -544,16 +637,16 @@ class MethodType {
* <p>
* This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic.
* @param bytecodeSignature a bytecode-level signature string "(T...)T"
* @param descriptor a bytecode-level signature string "(T...)T"
* @param loader the class loader in which to look up the types
* @return a method type matching the bytecode-level signature
* @throws IllegalArgumentException if the string is not well-formed
* @throws TypeNotPresentException if a named type cannot be found
*/
public static MethodType fromBytecodeString(String bytecodeSignature, ClassLoader loader)
public static MethodType fromMethodDescriptorString(String descriptor, ClassLoader loader)
throws IllegalArgumentException, TypeNotPresentException
{
List<Class<?>> types = BytecodeSignature.parseMethod(bytecodeSignature, loader);
List<Class<?>> types = BytecodeDescriptor.parseMethod(descriptor, loader);
Class<?> rtype = types.remove(types.size() - 1);
Class<?>[] ptypes = types.toArray(NO_PTYPES);
return makeImpl(rtype, ptypes, true);
......@@ -565,11 +658,21 @@ class MethodType {
* <p>
* This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic.
* {@link #fromBytecodeString(java.lang.String, java.lang.ClassLoader)},
* {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader)},
* because the latter requires a suitable class loader argument.
* @return the bytecode signature representation
*/
public String toMethodDescriptorString() {
return BytecodeDescriptor.unparse(this);
}
/** Temporary alias for toMethodDescriptorString; delete after M3. */
public String toBytecodeString() {
return BytecodeSignature.unparse(this);
return toMethodDescriptorString();
}
/** Temporary alias for fromMethodDescriptorString; delete after M3. */
public static MethodType fromBytecodeString(String descriptor, ClassLoader loader)
throws IllegalArgumentException, TypeNotPresentException {
return fromMethodDescriptorString(descriptor, loader);
}
}
......@@ -24,6 +24,7 @@
*/
/**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* This package contains dynamic language support provided directly by
* the Java core class libraries and virtual machine.
* @author John Rose, JSR 292 EG
......
......@@ -28,6 +28,10 @@ package sun.dyn;
import sun.dyn.util.VerifyType;
import sun.dyn.util.Wrapper;
import java.dyn.*;
import java.util.List;
import sun.dyn.MethodHandleNatives.Constants;
import static sun.dyn.MethodHandleImpl.IMPL_LOOKUP;
import static sun.dyn.MemberName.newIllegalArgumentException;
/**
* The flavor of method handle which emulates an invoke instruction
......@@ -35,18 +39,23 @@ import java.dyn.*;
* when the handle is created, not when it is invoked.
* @author jrose
*/
public class BoundMethodHandle extends MethodHandle {
public class BoundMethodHandle extends MethodHandle {
//MethodHandle vmtarget; // next BMH or final DMH or methodOop
private final Object argument; // argument to insert
private final int vmargslot; // position at which it is inserted
private static final Access IMPL_TOKEN = Access.getToken();
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
// Constructors in this class *must* be package scoped or private.
// Exception: JavaMethodHandle constructors are protected.
// (The link between JMH and BMH is temporary.)
/** Bind a direct MH to its receiver (or first ref. argument).
* The JVM will pre-dispatch the MH if it is not already static.
*/
BoundMethodHandle(DirectMethodHandle mh, Object argument) {
super(Access.TOKEN, mh.type().dropParameterType(0));
super(Access.TOKEN, mh.type().dropParameterTypes(0, 1));
// check the type now, once for all:
this.argument = checkReferenceArgument(argument, mh, 0);
this.vmargslot = this.type().parameterSlotCount();
......@@ -56,32 +65,34 @@ public class BoundMethodHandle extends MethodHandle {
} else {
this.vmtarget = mh;
}
}
private static final int REF_ARG = 0, PRIM_ARG = 1, SELF_ARG = 2;
}
/** Insert an argument into an arbitrary method handle.
* If argnum is zero, inserts the first argument, etc.
* The argument type must be a reference.
*/
BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
this(mh, argument, argnum, mh.type().parameterType(argnum).isPrimitive() ? PRIM_ARG : REF_ARG);
this(mh.type().dropParameterTypes(argnum, argnum+1),
mh, argument, argnum);
}
/** Insert an argument into an arbitrary method handle.
* If argnum is zero, inserts the first argument, etc.
*/
BoundMethodHandle(MethodHandle mh, Object argument, int argnum, int whichArg) {
super(Access.TOKEN, mh.type().dropParameterType(argnum));
if (whichArg == PRIM_ARG)
BoundMethodHandle(MethodType type, MethodHandle mh, Object argument, int argnum) {
super(Access.TOKEN, type);
if (mh.type().parameterType(argnum).isPrimitive())
this.argument = bindPrimitiveArgument(argument, mh, argnum);
else {
if (whichArg == SELF_ARG) argument = this;
this.argument = checkReferenceArgument(argument, mh, argnum);
}
this.vmargslot = this.type().parameterSlotDepth(argnum);
this.vmargslot = type.parameterSlotDepth(argnum);
initTarget(mh, argnum);
}
private void initTarget(MethodHandle mh, int argnum) {
if (MethodHandleNatives.JVM_SUPPORT) {
this.vmtarget = null; // maybe updated by JVM
this.vmtarget = null; // maybe updated by JVM
MethodHandleNatives.init(this, mh, argnum);
} else {
this.vmtarget = mh;
......@@ -97,29 +108,65 @@ public class BoundMethodHandle extends MethodHandle {
assert(this.getClass() == AdapterMethodHandle.class);
}
/** Initialize the current object as a method handle, binding it
* as the {@code argnum}th argument of the method handle {@code entryPoint}.
/** Initialize the current object as a Java method handle, binding it
* as the first argument of the method handle {@code entryPoint}.
* The invocation type of the resulting method handle will be the
* same as {@code entryPoint}, except that the {@code argnum}th argument
* same as {@code entryPoint}, except that the first argument
* type will be dropped.
*/
public BoundMethodHandle(MethodHandle entryPoint, int argnum) {
this(entryPoint, null, argnum, SELF_ARG);
// Note: If the conversion fails, perhaps because of a bad entryPoint,
// the MethodHandle.type field will not be filled in, and therefore
// no MH.invoke call will ever succeed. The caller may retain a pointer
// to the broken method handle, but no harm can be done with it.
protected BoundMethodHandle(MethodHandle entryPoint) {
super(Access.TOKEN, entryPoint.type().dropParameterTypes(0, 1));
this.argument = this; // kludge; get rid of
this.vmargslot = this.type().parameterSlotDepth(0);
initTarget(entryPoint, 0);
assert(this instanceof JavaMethodHandle);
}
/** Initialize the current object as a method handle, binding it
* as the first argument of the method handle {@code entryPoint}.
* The invocation type of the resulting method handle will be the
* same as {@code entryPoint}, except that the first argument
* type will be dropped.
/** Initialize the current object as a Java method handle.
*/
public BoundMethodHandle(MethodHandle entryPoint) {
this(entryPoint, null, 0, SELF_ARG);
protected BoundMethodHandle(String entryPointName, MethodType type, boolean matchArity) {
super(Access.TOKEN, null);
MethodHandle entryPoint
= findJavaMethodHandleEntryPoint(this.getClass(),
entryPointName, type, matchArity);
MethodHandleImpl.initType(this, entryPoint.type().dropParameterTypes(0, 1));
this.argument = this; // kludge; get rid of
this.vmargslot = this.type().parameterSlotDepth(0);
initTarget(entryPoint, 0);
assert(this instanceof JavaMethodHandle);
}
private static
MethodHandle findJavaMethodHandleEntryPoint(Class<?> caller,
String name,
MethodType type,
boolean matchArity) {
if (matchArity) type.getClass(); // elicit NPE
List<MemberName> methods = IMPL_NAMES.getMethods(caller, true, name, null, caller);
MethodType foundType = null;
MemberName foundMethod = null;
for (MemberName method : methods) {
MethodType mtype = method.getMethodType();
if (type != null && type.parameterCount() != mtype.parameterCount())
continue;
else if (foundType == null)
foundType = mtype;
else if (foundType != mtype)
throw newIllegalArgumentException("more than one method named "+name+" in "+caller.getName());
// discard overrides
if (foundMethod == null)
foundMethod = method;
else if (foundMethod.getDeclaringClass().isAssignableFrom(method.getDeclaringClass()))
foundMethod = method;
}
if (foundMethod == null)
throw newIllegalArgumentException("no method named "+name+" in "+caller.getName());
MethodHandle entryPoint = MethodHandleImpl.findMethod(IMPL_TOKEN, foundMethod, true, caller);
if (type != null) {
MethodType epType = type.insertParameterTypes(0, entryPoint.type().parameterType(0));
entryPoint = MethodHandles.convertArguments(entryPoint, epType);
}
return entryPoint;
}
/** Make sure the given {@code argument} can be used as {@code argnum}-th
......@@ -175,6 +222,24 @@ public class BoundMethodHandle extends MethodHandle {
@Override
public String toString() {
return "Bound[" + super.toString() + "]";
MethodHandle mh = this;
while (mh instanceof BoundMethodHandle) {
Object info = MethodHandleNatives.getTargetInfo(mh);
if (info instanceof MethodHandle) {
mh = (MethodHandle) info;
} else {
String name = null;
if (info instanceof MemberName)
name = ((MemberName)info).getName();
if (name != null)
return name;
else
return super.toString(); // <unknown>, probably
}
assert(mh != this);
if (mh instanceof JavaMethodHandle)
break; // access JMH.toString(), not BMH.toString()
}
return mh.toString();
}
}
......@@ -26,34 +26,51 @@
package sun.dyn;
import java.dyn.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The CallSite privately created by the JVM at every invokedynamic instruction.
* Parts of CallSite known to the JVM.
* FIXME: Merge all this into CallSite proper.
* @author jrose
*/
class CallSiteImpl extends CallSite {
// Fields used only by the JVM. Do not use or change.
public class CallSiteImpl {
// Field used only by the JVM. Do not use or change.
private Object vmmethod;
// Values supplied by the JVM:
int callerMID, callerBCI;
protected int callerMID, callerBCI;
private CallSiteImpl(Class<?> caller, String name, MethodType type) {
super(caller, name, type);
private MethodHandle target;
protected final Object caller; // usually a class
protected final String name;
protected final MethodType type;
/** called only directly from CallSite() */
protected CallSiteImpl(Access token, Object caller, String name, MethodType type) {
Access.check(token);
this.caller = caller;
this.name = name;
this.type = type;
}
/** native version of setTarget */
protected void setTarget(MethodHandle mh) {
//System.out.println("setTarget "+this+" := "+mh);
// XXX I don't know how to fix this properly.
// if (false && MethodHandleNatives.JVM_SUPPORT) // FIXME: enable this
// MethodHandleNatives.linkCallSite(this, mh);
// else
this.target = mh;
}
@Override
public void setTarget(MethodHandle mh) {
checkTarget(mh);
if (MethodHandleNatives.JVM_SUPPORT)
MethodHandleNatives.linkCallSite(this, (MethodHandle) mh);
else
super.setTarget(mh);
protected MethodHandle getTarget() {
return target;
}
private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE =
MethodHandleImpl.IMPL_LOOKUP.findStatic(CallSite.class, "privateInitializeCallSite",
MethodType.make(void.class, CallSite.class, int.class, int.class));
MethodType.methodType(void.class, CallSite.class, int.class, int.class));
// this is the up-call from the JVM:
static CallSite makeSite(Class<?> caller, String name, MethodType type,
......@@ -61,10 +78,25 @@ class CallSiteImpl extends CallSite {
MethodHandle bsm = Linkage.getBootstrapMethod(caller);
if (bsm == null)
throw new InvokeDynamicBootstrapError("class has no bootstrap method: "+caller);
CallSite site = bsm.<CallSite>invoke(caller, name, type);
CallSite site;
try {
site = bsm.<CallSite>invoke(caller, name, type);
} catch (Throwable ex) {
throw new InvokeDynamicBootstrapError("exception thrown while linking", ex);
}
if (site == null)
throw new InvokeDynamicBootstrapError("class bootstrap method failed to create a call site: "+caller);
PRIVATE_INITIALIZE_CALL_SITE.<void>invoke(site, callerMID, callerBCI);
if (site.type() != type)
throw new InvokeDynamicBootstrapError("call site type not initialized correctly: "+site);
if (site.callerClass() != caller)
throw new InvokeDynamicBootstrapError("call site caller not initialized correctly: "+site);
if ((Object)site.name() != name)
throw new InvokeDynamicBootstrapError("call site name not initialized correctly: "+site);
try {
PRIVATE_INITIALIZE_CALL_SITE.<void>invoke(site, callerMID, callerBCI);
} catch (Throwable ex) {
throw new InvokeDynamicBootstrapError("call site initialization exception", ex);
}
return site;
}
}
......@@ -27,7 +27,6 @@ package sun.dyn;
import java.dyn.JavaMethodHandle;
import java.dyn.MethodHandle;
import java.dyn.MethodHandles;
import java.dyn.MethodType;
/**
......@@ -42,16 +41,21 @@ public class FilterOneArgument extends JavaMethodHandle {
protected final MethodHandle filter; // Object -> Object
protected final MethodHandle target; // Object -> Object
protected Object entryPoint(Object argument) {
Object filteredArgument = filter.<Object>invoke(argument);
return target.<Object>invoke(filteredArgument);
@Override
public String toString() {
return target.toString();
}
protected Object invoke(Object argument) throws Throwable {
Object filteredArgument = filter.invoke(argument);
return target.invoke(filteredArgument);
}
private static final MethodHandle entryPoint =
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "entryPoint", MethodType.makeGeneric(1));
private static final MethodHandle INVOKE =
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke", MethodType.genericMethodType(1));
protected FilterOneArgument(MethodHandle filter, MethodHandle target) {
super(entryPoint);
super(INVOKE);
this.filter = filter;
this.target = target;
}
......@@ -62,10 +66,6 @@ public class FilterOneArgument extends JavaMethodHandle {
return new FilterOneArgument(filter, target);
}
public String toString() {
return filter + "|>" + target;
}
// MethodHandle make(MethodHandle filter1, MethodHandle filter2, MethodHandle target) {
// MethodHandle filter = make(filter1, filter2);
// return make(filter, target);
......
......@@ -44,16 +44,20 @@ public class Invokers {
// generic (untyped) invoker for the outgoing call
private /*lazy*/ MethodHandle genericInvoker;
// generic (untyped) invoker for the outgoing call; accepts a single Object[]
private final /*lazy*/ MethodHandle[] varargsInvokers;
/** Compute and cache information common to all collecting adapters
* that implement members of the erasure-family of the given erased type.
*/
public Invokers(Access token, MethodType targetType) {
Access.check(token);
this.targetType = targetType;
this.varargsInvokers = new MethodHandle[targetType.parameterCount()+1];
}
public static MethodType invokerType(MethodType targetType) {
return targetType.insertParameterType(0, MethodHandle.class);
return targetType.insertParameterTypes(0, MethodHandle.class);
}
public MethodHandle exactInvoker() {
......@@ -76,8 +80,14 @@ public class Invokers {
return invoker;
}
public MethodHandle varargsInvoker() {
throw new UnsupportedOperationException("NYI");
public MethodHandle varargsInvoker(int objectArgCount) {
MethodHandle vaInvoker = varargsInvokers[objectArgCount];
if (vaInvoker != null) return vaInvoker;
MethodHandle gInvoker = genericInvoker();
MethodType vaType = MethodType.genericMethodType(objectArgCount, true);
vaInvoker = MethodHandles.spreadArguments(gInvoker, invokerType(vaType));
varargsInvokers[objectArgCount] = vaInvoker;
return vaInvoker;
}
public String toString() {
......
......@@ -25,7 +25,7 @@
package sun.dyn;
import sun.dyn.util.BytecodeSignature;
import sun.dyn.util.BytecodeDescriptor;
import java.dyn.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
......@@ -33,6 +33,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
......@@ -93,7 +94,7 @@ public final class MemberName implements Member, Cloneable {
}
if (type instanceof String) {
String sig = (String) type;
MethodType res = MethodType.fromBytecodeString(sig, getClassLoader());
MethodType res = MethodType.fromMethodDescriptorString(sig, getClassLoader());
this.type = res;
return res;
}
......@@ -101,7 +102,7 @@ public final class MemberName implements Member, Cloneable {
Object[] typeInfo = (Object[]) type;
Class<?>[] ptypes = (Class<?>[]) typeInfo[1];
Class<?> rtype = (Class<?>) typeInfo[0];
MethodType res = MethodType.make(rtype, ptypes);
MethodType res = MethodType.methodType(rtype, ptypes);
this.type = res;
return res;
}
......@@ -111,7 +112,7 @@ public final class MemberName implements Member, Cloneable {
public MethodType getInvocationType() {
MethodType itype = getMethodType();
if (!isStatic())
itype = itype.insertParameterType(0, clazz);
itype = itype.insertParameterTypes(0, clazz);
return itype;
}
......@@ -135,7 +136,7 @@ public final class MemberName implements Member, Cloneable {
}
if (type instanceof String) {
String sig = (String) type;
MethodType mtype = MethodType.fromBytecodeString("()"+sig, getClassLoader());
MethodType mtype = MethodType.fromMethodDescriptorString("()"+sig, getClassLoader());
Class<?> res = mtype.returnType();
this.type = res;
return res;
......@@ -155,9 +156,9 @@ public final class MemberName implements Member, Cloneable {
if (type instanceof String)
return (String) type;
if (isInvocable())
return BytecodeSignature.unparse(getMethodType());
return BytecodeDescriptor.unparse(getMethodType());
else
return BytecodeSignature.unparse(getFieldType());
return BytecodeDescriptor.unparse(getFieldType());
}
public int getModifiers() {
......@@ -353,6 +354,8 @@ public final class MemberName implements Member, Cloneable {
return type.toString(); // class java.lang.String
// else it is a field, method, or constructor
StringBuilder buf = new StringBuilder();
if (!isResolved())
buf.append("*.");
if (getDeclaringClass() != null) {
buf.append(getName(clazz));
buf.append('.');
......@@ -381,7 +384,7 @@ public final class MemberName implements Member, Cloneable {
private static String getName(Object obj) {
if (obj instanceof Class<?>)
return ((Class<?>)obj).getName();
return obj.toString();
return String.valueOf(obj);
}
// Queries to the JVM:
......@@ -408,6 +411,9 @@ public final class MemberName implements Member, Cloneable {
public static NoAccessException newNoAccessException(MemberName name, Class<?> lookupClass) {
return newNoAccessException("cannot access", name, lookupClass);
}
public static NoAccessException newNoAccessException(MemberName name, MethodHandles.Lookup lookup) {
return newNoAccessException(name, lookup.lookupClass());
}
public static NoAccessException newNoAccessException(String message,
MemberName name, Class<?> lookupClass) {
message += ": " + name;
......@@ -436,7 +442,7 @@ public final class MemberName implements Member, Cloneable {
matchFlags &= ALLOWED_FLAGS;
String matchSig = null;
if (matchType != null) {
matchSig = BytecodeSignature.unparse(matchType);
matchSig = BytecodeDescriptor.unparse(matchType);
if (matchSig.startsWith("("))
matchFlags &= ~(ALL_KINDS & ~IS_INVOCABLE);
else
......@@ -447,17 +453,18 @@ public final class MemberName implements Member, Cloneable {
MemberName[] buf = newMemberBuffer(len1);
int totalCount = 0;
ArrayList<MemberName[]> bufs = null;
int bufCount = 0;
for (;;) {
int bufCount = MethodHandleNatives.getMembers(defc,
bufCount = MethodHandleNatives.getMembers(defc,
matchName, matchSig, matchFlags,
lookupClass,
totalCount, buf);
if (bufCount <= buf.length) {
if (bufCount >= 0)
totalCount += bufCount;
if (bufCount < 0) bufCount = 0;
totalCount += bufCount;
break;
}
// JVM returned tp us with an intentional overflow!
// JVM returned to us with an intentional overflow!
totalCount += buf.length;
int excess = bufCount - buf.length;
if (bufs == null) bufs = new ArrayList<MemberName[]>(1);
......@@ -473,7 +480,7 @@ public final class MemberName implements Member, Cloneable {
Collections.addAll(result, buf0);
}
}
Collections.addAll(result, buf);
result.addAll(Arrays.asList(buf).subList(0, bufCount));
// Signature matching is not the same as type matching, since
// one signature might correspond to several types.
// So if matchType is a Class or MethodType, refilter the results.
......
......@@ -25,6 +25,7 @@
package sun.dyn;
import java.dyn.CallSite;
import java.dyn.MethodHandle;
import java.dyn.MethodType;
import java.lang.reflect.AccessibleObject;
......@@ -60,7 +61,7 @@ class MethodHandleNatives {
static native void init(MethodType self);
/** Tell the JVM that we need to change the target of an invokedynamic. */
static native void linkCallSite(CallSiteImpl site, MethodHandle target);
static native void linkCallSite(CallSite site, MethodHandle target);
/** Fetch the vmtarget field.
* It will be sanitized as necessary to avoid exposing non-Java references.
......@@ -84,8 +85,7 @@ class MethodHandleNatives {
}
/** Fetch the target of this method handle.
* If it directly targets a method, return a tuple of method info.
* The info is of the form new Object[]{defclass, name, sig, refclass}.
* If it directly targets a method, return a MemberName for the method.
* If it is chained to another method handle, return that handle.
*/
static Object getTargetInfo(MethodHandle self) {
......@@ -123,7 +123,7 @@ class MethodHandleNatives {
registerNatives();
JVM_SUPPORT_ = true;
JVM_PUSH_LIMIT_ = getConstant(Constants.GC_JVM_PUSH_LIMIT);
JVM_STACK_MOVE_UNIT_ = getConstant(Constants.GC_JVM_STACK_MOVE_LIMIT);
JVM_STACK_MOVE_UNIT_ = getConstant(Constants.GC_JVM_STACK_MOVE_UNIT);
//sun.reflect.Reflection.registerMethodsToFilter(MethodHandleImpl.class, "init");
} catch (UnsatisfiedLinkError ee) {
// ignore; if we use init() methods later we'll see linkage errors
......@@ -149,7 +149,7 @@ class MethodHandleNatives {
// MethodHandleImpl
static final int // for getConstant
GC_JVM_PUSH_LIMIT = 0,
GC_JVM_STACK_MOVE_LIMIT = 1;
GC_JVM_STACK_MOVE_UNIT = 1;
static final int
ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method)
ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self)
......@@ -178,19 +178,20 @@ class MethodHandleNatives {
*/
static final int
OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
OP_CHECK_CAST = 0x1, // ref-to-ref conversion; requires a Class argument
OP_PRIM_TO_PRIM = 0x2, // converts from one primitive to another
OP_REF_TO_PRIM = 0x3, // unboxes a wrapper to produce a primitive
OP_PRIM_TO_REF = 0x4, // boxes a primitive into a wrapper (NYI)
OP_SWAP_ARGS = 0x5, // swap arguments (vminfo is 2nd arg)
OP_ROT_ARGS = 0x6, // rotate arguments (vminfo is displaced arg)
OP_DUP_ARGS = 0x7, // duplicates one or more arguments (at TOS)
OP_DROP_ARGS = 0x8, // remove one or more argument slots
OP_COLLECT_ARGS = 0x9, // combine one or more arguments into a varargs (NYI)
OP_SPREAD_ARGS = 0xA, // expand in place a varargs array (of known size)
OP_FLYBY = 0xB, // operate first on reified argument list (NYI)
OP_RICOCHET = 0xC, // run an adapter chain on the return value (NYI)
CONV_OP_LIMIT = 0xD; // limit of CONV_OP enumeration
OP_RETYPE_RAW = 0x1, // no argument changes; straight retype
OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument
OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive
OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI)
OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg)
OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
OP_DROP_ARGS = 0x9, // remove one or more argument slots
OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI)
OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
OP_FLYBY = 0xC, // operate first on reified argument list (NYI)
OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI)
CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration
/** Shift and mask values for decoding the AMH.conversion field.
* These numbers are shared with the JVM for creating AMHs.
*/
......@@ -209,6 +210,7 @@ class MethodHandleNatives {
// TODO: The following expression should be replaced by
// a JVM query.
((1<<OP_RETYPE_ONLY)
|(1<<OP_RETYPE_RAW)
|(1<<OP_CHECK_CAST)
|(1<<OP_PRIM_TO_PRIM)
|(1<<OP_REF_TO_PRIM)
......@@ -216,6 +218,7 @@ class MethodHandleNatives {
|(1<<OP_ROT_ARGS)
|(1<<OP_DUP_ARGS)
|(1<<OP_DROP_ARGS)
//|(1<<OP_SPREAD_ARGS) // FIXME: Check JVM assembly code.
);
/**
......
......@@ -27,6 +27,7 @@ package sun.dyn;
import java.dyn.*;
import sun.dyn.util.Wrapper;
import static sun.dyn.MemberName.newIllegalArgumentException;
/**
* Shared information for a group of method types, which differ
......@@ -56,8 +57,8 @@ public class MethodTypeImpl {
// Cached adapter information:
/*lazy*/ ToGeneric toGeneric; // convert cs. with prims to w/o
/*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with
/*lazy*/ SpreadGeneric[] spreadGeneric; // expand one argument to many
/*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly
///*lazy*/ Invokers invokers; // cache of handy higher-order adapters
public MethodType erasedType() {
return erasedType;
......@@ -68,7 +69,7 @@ public class MethodTypeImpl {
}
/** Access methods for the internals of MethodType, supplied to
* MethodTypeForm as a trusted agent.
* MethodTypeImpl as a trusted agent.
*/
static public interface MethodTypeFriend {
Class<?>[] ptypes(MethodType mt);
......@@ -150,7 +151,7 @@ public class MethodTypeImpl {
this.argToSlotTable = argToSlotTab;
this.slotToArgTable = slotToArgTab;
if (pslotCount >= 256) throw new IllegalArgumentException("too many arguments");
if (pslotCount >= 256) throw newIllegalArgumentException("too many arguments");
// send a few bits down to the JVM:
this.vmslots = parameterSlotCount();
......@@ -378,10 +379,10 @@ public class MethodTypeImpl {
static MethodTypeImpl findForm(MethodType mt) {
MethodType erased = canonicalize(mt, ERASE, ERASE);
if (erased == null) {
// It is already erased. Make a new MethodTypeForm.
// It is already erased. Make a new MethodTypeImpl.
return METHOD_TYPE_FRIEND.newMethodTypeForm(mt);
} else {
// Share the MethodTypeForm with the erased version.
// Share the MethodTypeImpl with the erased version.
return METHOD_TYPE_FRIEND.form(erased);
}
}
......
此差异已折叠。
......@@ -29,6 +29,10 @@ package sun.dyn.empty;
* An empty class in an empty package.
* Used as a proxy for unprivileged code, since making access checks
* against it will only succeed against public methods in public types.
* <p>
* This class also stands (internally to sun.dyn) for the type of a
* value that cannot be produced, because the expression of this type
* always returns abnormally. (Cf. Nothing in the closures proposal.)
* @author jrose
*/
public class Empty {
......
......@@ -33,9 +33,9 @@ import java.util.List;
* Utility routines for dealing with bytecode-level signatures.
* @author jrose
*/
public class BytecodeSignature {
public class BytecodeDescriptor {
private BytecodeSignature() { } // cannot instantiate
private BytecodeDescriptor() { } // cannot instantiate
public static List<Class<?>> parseMethod(String bytecodeSignature, ClassLoader loader) {
return parseMethod(bytecodeSignature, 0, bytecodeSignature.length(), loader);
......
......@@ -26,6 +26,7 @@
package sun.dyn.util;
import java.dyn.MethodType;
import sun.dyn.empty.Empty;
/**
* This class centralizes information about the JVM verifier
......@@ -73,29 +74,28 @@ public class VerifyType {
}
/**
* Is the given type either java.lang.Void or java.lang.Null?
* These types serve as markers for bare nulls and therefore
* may be promoted to any type. This is secure, since
* Is the given type java.lang.Null or an equivalent null-only type?
*/
public static boolean isNullType(Class<?> type) {
if (type == null) return false;
return type == NULL_CLASS_1 || type == NULL_CLASS_2;
return type == NULL_CLASS
// This one may also be used as a null type.
// TO DO: Decide if we really want to legitimize it here.
// Probably we do, unless java.lang.Null really makes it into Java 7
//|| type == Void.class
// Locally known null-only class:
|| type == Empty.class
;
}
private static final Class<?> NULL_CLASS_1, NULL_CLASS_2;
private static final Class<?> NULL_CLASS;
static {
Class<?> nullClass1 = null, nullClass2 = null;
Class<?> nullClass = null;
try {
nullClass1 = Class.forName("java.lang.Null");
nullClass = Class.forName("java.lang.Null");
} catch (ClassNotFoundException ex) {
// OK, we'll cope
}
NULL_CLASS_1 = nullClass1;
// This one may also be used as a null type.
// TO DO: Decide if we really want to legitimize it here.
// Probably we do, unless java.lang.Null really makes it into Java 7
nullClass2 = Void.class;
NULL_CLASS_2 = nullClass2;
NULL_CLASS = nullClass;
}
/**
......@@ -191,6 +191,11 @@ public class VerifyType {
// to be captured as a garbage int.
// Caller promises that the actual value will be disregarded.
return dst == int.class ? 1 : 0;
if (isNullType(src))
// Special permission for raw conversions: allow a null
// to be reinterpreted as anything. For objects, it is safe,
// and for primitives you get a garbage value (probably zero).
return 1;
if (!src.isPrimitive())
return 0;
Wrapper sw = Wrapper.forPrimitiveType(src);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册