meth: International Invokedynamic Day
authorjrose
Tue Aug 26 02:44:01 2008 -0700 (15 months ago)
changeset 876dd10b26151
parent 7c1b7e1b0d74a
child 94083d62b930b
meth: International Invokedynamic Day
meth.patch
--- a/meth.patch Tue Aug 26 02:42:41 2008 -0700
+++ b/meth.patch Tue Aug 26 02:44:01 2008 -0700
@@ -2,7 +2,7 @@ new file mode 100644
new file mode 100644
--- /dev/null
+++ b/src/share/classes/java/dyn/CallSite.java
-@@ -0,0 +1,75 @@
+@@ -0,0 +1,116 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -43,40 +43,81 @@ new file mode 100644
+ * to finish the execution of the call site, and optionally to link
+ * the call site.
+ * <p>
-+ * FIXME: Make this into a concrete (but non-final) class.
+ * @author jrose
+ */
-+public interface CallSite {
++public abstract class CallSite {
++ protected MethodHandle target;
++ protected final Object caller; // usually a class
++ protected final String name;
++ protected final MethodType type;
++
++ protected CallSite(Object caller, String name, MethodType type) {
++ this.caller = caller;
++ this.name = name;
++ this.type = type;
++ }
++
+ /**
+ * Report the current linkage state of the call site. (This is mutable.)
++ * The value is null if and only if the call site is currently unlinked.
+ * @return the current linkage state of the call site
+ */
-+ public MethodHandle getTarget();
++ public MethodHandle getTarget() {
++ return target;
++ }
+
+ /**
+ * Link or relink the call site, by setting its target method.
+ * @param target the new target, or null if it is to be unlinked
-+ */
-+ public void setTarget(MethodHandle target);
-+
-+ /**
-+ * Report the class containing the call site. (This is immutable static context.)
++ * @throws WrongMethodTypeException if the new target is not null
++ * and has a method type that differs from the call site type
++ */
++ public void setTarget(MethodHandle target) {
++ checkTarget(target);
++ this.target = target;
++ }
++
++ protected void checkTarget(MethodHandle target) {
++ if (!canSetTarget(target))
++ throw new WrongMethodTypeException(String.valueOf(target));
++ }
++
++ protected boolean canSetTarget(MethodHandle target) {
++ return (target == null || target.type() == type());
++ }
++
++ /**
++ * Report the class containing the call site.
++ * This is immutable static context.
+ * @return class containing the call site
+ */
-+ Class callerClass();
-+
-+ /**
-+ * Report the method name specified in the {@code invokedynamic} instruction. (This is immutable static context.)
++ public Class<?> callerClass() {
++ return (Class) caller;
++ }
++
++ /**
++ * Report the method name specified in the {@code invokedynamic} instruction.
++ * This is immutable static context.
+ * @return method name specified by the call site
+ */
-+ String name();
++ public String name() {
++ return name;
++ }
+
+ /*
+ * Report the result and parameter types, derived from the invocation descriptor, and resolved
-+ * against the calling class's class loader. (This is immutable static context.)
++ * against the calling class's class loader.
++ * This is immutable static context.
+ * @return method type specified by the call site
+ */
-+ MethodType type();
++ public MethodType type() {
++ return type;
++ }
++
++ @Override
++ public String toString() {
++ return "CallSite#"+hashCode()+"["+name+type+" => "+target+"]";
++ }
+}
diff --git a/src/share/classes/java/dyn/Dynamic.java b/src/share/classes/java/dyn/Dynamic.java
new file mode 100644
@@ -117,14 +158,14 @@ new file mode 100644
+ * As such it may be viewed as logically containing all methods on any of those types.
+ * @author jrose
+ */
-+public class Dynamic {
++public interface Dynamic {
+ // no methods
+}
diff --git a/src/share/classes/java/dyn/Linkage.java b/src/share/classes/java/dyn/Linkage.java
new file mode 100644
--- /dev/null
+++ b/src/share/classes/java/dyn/Linkage.java
-@@ -0,0 +1,111 @@
+@@ -0,0 +1,209 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -152,6 +193,10 @@ new file mode 100644
+
+package java.dyn;
+
++import java.dyn.util.MethodHandleInvoker;
++import java.util.WeakHashMap;
++import sun.reflect.Reflection;
++
+/**
+ *
+ * @author jrose
@@ -175,21 +220,65 @@ new file mode 100644
+ * Because of these rules, a class may install its own bootstrap method in
+ * a static initializer.
+ */
++ public static
+ void registerBootstrapMethod(Class callerClass, MethodHandle mh) {
-+ SecurityManager security = System.getSecurityManager();
-+ if (security != null) {
-+ // FIXME: do not run this check if my caller and callerClass are package-mates.
-+ security.checkPermission(new LinkagePermission("registerBootstrapMethod",callerClass));
-+ }
-+ throw new UnsupportedOperationException("NYI");
-+ }
++ Class callc = Reflection.getCallerClass(2);
++ checkPackagePrivilege(callc, callerClass, "registerBootstrapMethod");
++ if (mh != null && mh.type() != BOOTSTRAP_METHOD_TYPE)
++ throw new WrongMethodTypeException(mh.type().toString());
++ synchronized (bootstrapMethods) {
++ if (bootstrapMethods.containsKey(callerClass))
++ throw new IllegalStateException("bootstrap method already declared in "+callerClass);
++ bootstrapMethods.put(callerClass, mh);
++ }
++ }
++
++ /**
++ * 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.
++ * Only callers privileged to set the bootstrap method may inquire
++ * about it, because a bootstrap method is potentially a back-door entry
++ * points to its class.
++ */
++ public static
++ MethodHandle getBootstrapMethod(Class callerClass) {
++ Class callc = Reflection.getCallerClass(2);
++ checkPackagePrivilege(callc, callerClass, "registerBootstrapMethod");
++ synchronized (bootstrapMethods) {
++ return bootstrapMethods.get(callerClass);
++ }
++ }
++
++ /** The type of any bootstrap method is (CallSite,Object...)Object.
++ * The varargs marker is required.
++ */
++ public static final MethodType BOOTSTRAP_METHOD_TYPE
++ = MethodType.make(Object.class,
++ CallSite.class, Object[].class).newVarArgs(true);
++
++ private static MethodHandleInvoker bootstrapMethodInvoker;
++
++ private static final WeakHashMap<Class, MethodHandle> bootstrapMethods =
++ new WeakHashMap<Class, MethodHandle>();
+
+ /** Determine if the caller class has declared or registered its own bootstrap method.
+ * If so, delegate this call to it. Otherwise, throw an IncompatibleClassChangeError.
+ */
+ public static
+ Object bootstrapInvokeDynamic(CallSite site, Object... receiverAndArguments) {
-+ throw new UnsupportedOperationException("NYI");
++ Class callerClass = site.callerClass();
++ MethodHandle mh;
++ synchronized (bootstrapMethods) {
++ mh = bootstrapMethods.get(callerClass);
++ }
++ if (mh == null)
++ throw new IllegalStateException("no bootstrap method declared in "+callerClass);
++
++ System.out.println(site+": calling bootstrap "+mh);
++ if (bootstrapMethodInvoker == null)
++ bootstrapMethodInvoker = MethodHandleInvoker.make(BOOTSTRAP_METHOD_TYPE);
++ return bootstrapMethodInvoker.invoke_LLL(mh, site, receiverAndArguments);
+ }
+
+ /**
@@ -235,6 +324,56 @@ new file mode 100644
+ }
+ throw new UnsupportedOperationException("NYI");
+ }
++
++ /**
++ * Ensure the requesting class have privileges to perform invokedynamic
++ * linkage operations on subjectClass. True if requestingClass is
++ * null (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
++ * @return
++ */
++ static void checkPackagePrivilege(Class requestingClass, Class subjectClass,
++ String permissionName) {
++ if (requestingClass == null) return;
++ if (requestingClass == subjectClass) return;
++ SecurityManager security = System.getSecurityManager();
++ if (security == null) return; // open season
++ ClassLoader rcl = requestingClass.getClassLoader();
++ ClassLoader scl = subjectClass.getClassLoader();
++ if (isParent(rcl, scl)) {
++ String rn = requestingClass.getName();
++ if (rn.startsWith("java.dyn.")) return;
++ String sn = subjectClass.getName();
++ if (samePackage(rn, sn)) return;
++ }
++ security.checkPermission(new LinkagePermission(permissionName, requestingClass));
++ }
++
++ static
++ MethodHandle findBootstrapMethod(Class callerClass, Class searchBootstrapClass) {
++ if (searchBootstrapClass != null) throw new UnsupportedOperationException("NYI");
++ MethodHandle mh = getBootstrapMethod(callerClass);
++ System.out.println("reporting bootstrap method to JVM: "+mh);
++ return mh;
++ }
++
++ private static boolean isParent(ClassLoader rcl, ClassLoader scl) {
++ while (scl != null && scl != rcl)
++ scl = scl.getParent();
++ return (scl == rcl);
++ }
++
++ private static boolean samePackage(String rn, String sn) {
++ assert((rn.indexOf('/') & sn.indexOf('/')) < 0); // no bytecode names
++ int lastDot = rn.lastIndexOf('.');
++ if (lastDot != sn.lastIndexOf('.')) return false;
++ return rn.startsWith(sn.substring(0, lastDot+1));
++ }
++
+}
diff --git a/src/share/classes/java/dyn/LinkagePermission.java b/src/share/classes/java/dyn/LinkagePermission.java
new file mode 100644
@@ -1803,7 +1942,7 @@ new file mode 100644
new file mode 100644
--- /dev/null
+++ b/src/share/classes/java/dyn/MethodTypeForm.java
-@@ -0,0 +1,218 @@
+@@ -0,0 +1,272 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -1847,14 +1986,19 @@ new file mode 100644
+ * No more than half of these are likely to be loaded at once.
+ * @author jrose
+ */
-+class MethodTypeForm extends MTForm {
-+ final int argCounts; // byte-packed slot & value counts
-+ final int primCounts; // byte-packed prim & double counts
-+ final boolean varargs;
-+ final MethodType erase;
-+ final MethodType wrap;
++public class MethodTypeForm extends MTForm {
++ final int[] argToSlotTable, slotToArgTable;
++ final long argCounts; // packed slot & value counts
++ final long primCounts; // packed prim & double counts
++ final boolean varargs; // is this a "..." type?
++ final MethodType erase; // the canonical erasure
++ final MethodType wrap; // erasure, with primitives wrapped
+
+ private static final Access TOKEN = Access.getToken();
++
++ public static MethodTypeForm of(MethodType type) {
++ return type.form;
++ }
+
+ private MethodTypeForm(MethodType erase, boolean varargs) {
+ super(TOKEN);
@@ -1866,9 +2010,11 @@ new file mode 100644
+ int ptypeCount = erase.ptypes.length;
+ int pslotCount = ptypeCount; // temp. estimate
+ int rtypeCount = 1; // temp. estimate
-+ int rslotCount = 1;
++ int rslotCount = 1; // temp. estimate
+
+ assert(varargs == (ptypeCount != 0 && erase.ptypes[ptypeCount-1].isArray()));
++
++ int[] argToSlotTab = null, slotToArgTab = null;
+
+ // Walk the argument types, looking for primitives.
+ if (wt != null) {
@@ -1879,69 +2025,116 @@ new file mode 100644
+ if (pt != Object.class) {
+ assert(pt.isPrimitive());
+ ++pac;
-+ if (pt == long.class || pt == double.class)
-+ ++lac;
++ if (hasTwoArgSlots(pt)) ++lac;
+ }
+ }
+ pslotCount += lac; // #slots = #args + #longs
+ Class<?> rt = erase.rtype;
+ if (rt != Object.class) {
+ ++prc; // even void.class counts as a prim here
-+ if (rt == long.class || rt == double.class)
-+ ++lrc;
++ if (hasTwoArgSlots(rt)) ++lrc;
+ // adjust #slots, #args
+ if (rt == void.class)
+ rtypeCount = rslotCount = 0;
+ else
+ rslotCount += lrc;
+ }
-+ this.primCounts = (lrc << 24) | (prc << 16) | (lac << 8) | pac;
++ if (lac != 0) {
++ int slot = ptypeCount + lac;
++ slotToArgTab = new int[slot+1];
++ argToSlotTab = new int[ptypeCount];
++ for (int i = 0; i < epts.length; i++) {
++ Class<?> pt = epts[i];
++ if (hasTwoArgSlots(pt)) --slot;
++ --slot;
++ slotToArgTab[slot] = i+1;
++ argToSlotTab[i] = slot;
++ }
++ assert(slot == 0); // filled the table
++ }
++ this.primCounts = pack(lrc, prc, lac, pac);
+ } else {
+ this.primCounts = 0;
+ }
-+ this.argCounts = (rslotCount << 24) | (rtypeCount << 16) | (pslotCount << 8) | ptypeCount;
++ this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
++ if (slotToArgTab == null) {
++ int slot = ptypeCount; // first arg is deepest in stack
++ slotToArgTab = new int[slot+1];
++ argToSlotTab = new int[ptypeCount];
++ for (int i = 0; i < ptypeCount; i++) {
++ --slot;
++ slotToArgTab[slot] = i+1;
++ argToSlotTab[i] = slot;
++ }
++ }
++ this.argToSlotTable = argToSlotTab;
++ this.slotToArgTable = slotToArgTab;
+
+ if (pslotCount >= 256) throw new IllegalArgumentException("too many arguments");
+
+ // Allow lower layer to initialize other derived information.
+ super.init(erase, parameterSlotCount());
++ }
++
++ private static boolean hasTwoArgSlots(Class<?> type) {
++ return type == long.class || type == double.class;
++ }
++
++ private static long pack(int a, int b, int c, int d) {
++ assert(((a|b|c|d) & ~0xFFFF) == 0);
++ long hw = ((a << 16) | b), lw = ((c << 16) | d);
++ return (hw << 32) | lw;
++ }
++ private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d
++ assert(word <= 3);
++ return (char)(packed >> ((3-word) * 16));
+ }
+
+ // used only for bootstrapping:
+ static final MethodTypeForm[] FAKE = {
+ new MethodTypeForm(false), new MethodTypeForm(true)
+ };
-+ // used only for making bootstrapping fakes:
++ // used only for making FAKE[]:
+ private MethodTypeForm(boolean varargs) {
+ super(TOKEN);
+ this.varargs = varargs;
+ primCounts = argCounts = -1;
+ erase = wrap = null;
-+ }
-+
-+ int parameterCount() { // # outgoing values
-+ return argCounts & 0xFF;
-+ }
-+ int parameterSlotCount() { // # outgoing interpreter slots
-+ return (argCounts >> 8) & 0xFF;
-+ }
-+ int returnCount() { // = 0 (V), or 1
-+ return (argCounts >> 16) & 0xFF;
-+ }
-+ int returnSlotCount() { // = 0 (V), 2 (J/D), or 1
-+ return (argCounts >> 24) & 0xFF;
-+ }
-+ int primitiveParameterCount() {
-+ return primCounts & 0xFF;
-+ }
-+ int longPrimitiveParameterCount() {
-+ return (primCounts >> 8) & 0xFF;
-+ }
-+ int primitiveReturnCount() { // = 0 (obj), or 1
-+ return (primCounts >> 16) & 0xFF;
-+ }
-+ int longPrimitiveReturnCount() { // = 1 (J/D), or 0
-+ return (primCounts >> 24) & 0xFF;
++ argToSlotTable = slotToArgTable = null;
++ }
++
++ public int parameterCount() { // # outgoing values
++ return unpack(argCounts, 3);
++ }
++ public int parameterSlotCount() { // # outgoing interpreter slots
++ return unpack(argCounts, 2);
++ }
++ public int returnCount() { // = 0 (V), or 1
++ return unpack(argCounts, 1);
++ }
++ public int returnSlotCount() { // = 0 (V), 2 (J/D), or 1
++ return unpack(argCounts, 0);
++ }
++ public int primitiveParameterCount() {
++ return unpack(primCounts, 3);
++ }
++ public int longPrimitiveParameterCount() {
++ return unpack(primCounts, 2);
++ }
++ public int primitiveReturnCount() { // = 0 (obj), or 1
++ return unpack(primCounts, 1);
++ }
++ public int longPrimitiveReturnCount() { // = 1 (J/D), or 0
++ return unpack(primCounts, 0);
++ }
++ public int parameterToArgSlot(int i) {
++ return argToSlotTable[i];
++ }
++ public int argSlotToParameter(int argSlot) {
++ // Note: Empty slots are represented by zero in this table.
++ // Valid arguments slots contain incremented entries, so as to be non-zero.
++ // We return -1 the caller to mean an empty slot.
++ return slotToArgTable[argSlot] - 1;
+ }
+
+ static final int ERASE = 1, WRAP = 2, UNWRAP = 4, ARGS_ONLY = 8, KEEP_VARARG = 16;
@@ -2026,7 +2219,7 @@ new file mode 100644
new file mode 100644
--- /dev/null
+++ b/src/share/classes/java/dyn/Wrappers.java
-@@ -0,0 +1,121 @@
+@@ -0,0 +1,158 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -2060,44 +2253,81 @@ new file mode 100644
+
+ private Wrappers() { } // cannot instantiate
+
-+ public static Class<?> asWrapperType(Class<?> parameterType) {
-+ if (!parameterType.isPrimitive()) {
-+ return parameterType;
++ public static Class<?> asWrapperType(Class<?> type) {
++ if (!type.isPrimitive()) {
++ return type;
+ }
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
-+ Object[] memo = wrappers.get(parameterType);
++ Object[] memo = wrappers.get(type);
+ assert (memo != null);
+ return (Class<?>) memo[0];
+ }
+
-+ public static Class<?> asPrimitiveType(Class<?> parameterType) {
-+ if (parameterType.isPrimitive()) {
-+ return parameterType;
++ public static Class<?> asPrimitiveType(Class<?> type) {
++ if (type.isPrimitive()) {
++ return type;
+ }
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
-+ Object[] memo = wrappers.get(parameterType);
++ Object[] memo = wrappers.get(type);
+ if (memo == null) {
-+ return parameterType;
++ return type;
+ }
+ return (Class<?>) memo[1];
+ }
+
-+ public static char basicTypeChar(Class<?> parameterType) {
-+ if (!parameterType.isPrimitive()) {
++ public static char basicTypeChar(Class<?> type) {
++ if (!type.isPrimitive()) {
+ return 'L';
+ }
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
-+ Object[] memo = wrappers.get(parameterType);
++ Object[] memo = wrappers.get(type);
+ assert (memo != null);
+ return (char) (Character) memo[2];
+ }
+
++ static final String PRIMITIVE_BITS_TABLE = "ZBCSFI DJ";
++ // "-012345678"
++
++ /** Return the number of bits in the given type, or zero for refs. */
++ public static int bitWidth(Class<?> type) {
++ return bitWidth(basicTypeChar(type));
++ }
++
++ /** Return the number of bits in the given basic type, or zero for refs. */
++ public static int bitWidth(char c) {
++ int i = PRIMITIVE_BITS_TABLE.indexOf(c);
++ assert(c != ' ' && (i >= 0 || c == 'L'));
++ return i + (i & 1);
++ }
++
++ /** Return the number of bits in the given type, or zero for refs. */
++ public static boolean isSigned(Class<?> type) {
++ return isSigned(basicTypeChar(type));
++ }
++
++ /** Return the number of bits in the given basic type, or zero for refs. */
++ public static boolean isSigned(char c) {
++ int i = PRIMITIVE_BITS_TABLE.indexOf(c);
++ assert(c != ' ' && (i >= 0 || c == 'L'));
++ return (i & 1) == 0;
++ }
++
++ /** Report if the type is one of int, boolean, byte, char, or short. */
++ public static boolean isSubwordOrInt(Class<?> type) {
++ return isSubwordOrInt(basicTypeChar(type));
++ }
++
++ /** Report if the type char is one of "IZBCS". */
++ public static boolean isSubwordOrInt(char c) {
++ return "IZBCS".indexOf(c) >= 0;
++ }
++
+ public static Class<?> basicTypeFromChar(char c) {
+ if (c == 'L') {
+ return Object.class;
@@ -2114,14 +2344,14 @@ new file mode 100644
+ return (Class<?>) memo[1];
+ }
+
-+ public static Object zeroValue(Class<?> parameterType) {
-+ if (!parameterType.isPrimitive()) {
++ public static Object zeroValue(Class<?> type) {
++ if (!type.isPrimitive()) {
+ return null;
+ }
+ if (wrappers.isEmpty()) {
+ fillWrappers();
+ }
-+ Object[] memo = wrappers.get(parameterType);
++ Object[] memo = wrappers.get(type);
+ assert (memo != null);
+ return memo[3];
+ }
@@ -2216,7 +2446,7 @@ new file mode 100644
new file mode 100644
--- /dev/null
+++ b/src/share/classes/java/dyn/impl/AMH.java
-@@ -0,0 +1,45 @@
+@@ -0,0 +1,356 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -2245,22 +2475,333 @@ new file mode 100644
+package java.dyn.impl;
+
+import java.dyn.*;
-+import java.lang.reflect.Method;
++import java.util.List;
+
+/**
-+ * This method handle performs argument adaptation.
++ * This method handle performs simple conversion or checking of a single argument.
+ * @author jrose
+ */
+public class AMH extends MethodHandle {
-+ final MH target;
-+ Object[] values;
-+ AMH(Method m, MH target, Object[] values) {
-+ super(Access.TOKEN, MethodType.make(m));
++
++ /** MH to call when the the selected argument is converted. */
++ final MethodHandle target;
++
++ private AMH(MethodHandle target, MethodType newType,
++ int argSlot, int conversion, Object ref) {
++ super(Access.TOKEN, newType);
+ this.target = target;
-+ this.values = values;
-+ MH.init(this);
++ MH.init(this, argSlot, conversion, ref);
+ checkHandler();
+ }
++
++ // TO DO: When adapting a DMH, use direct retyping if possible
++
++ /** Create a JVM-level adapter which will call the target MH after
++ * possibly narrowing, casting, or dropping arguments.
++ * @param target the target to invoke after arguments are adjusted
++ * @param adapterType the method type of the desired result
++ * @param dropArguments args to drop; >0 means final, <0 means initial
++ * @return an AMH which does the job
++ * @throws WrongMethodTypeException if the conversions are too complex
++ */
++ static MethodHandle buildAdapter(MethodHandle target,
++ MethodType newType,
++ int dropArguments) {
++ MethodType oldType = target.type();
++ if (oldType == newType && dropArguments == 0)
++ return target; // well, that was easy
++
++ // Check return type. (Not much can be done with it.)
++ Class<?> src = oldType.returnType();
++ Class<?> dest = newType.returnType();
++ int pass = MH.canPassUnchecked(src, dest);
++ if (pass <= 0)
++ throw new WrongMethodTypeException(newType+": cannot return unchecked value from "+target);
++
++ MethodType midType;
++ int nargs = newType.parameterCount();
++ int dropConv = RETYPE_ONLY, dropSlots = 0;
++ MethodType dropConvType = null;
++
++ int ignoreNewArgs = nargs - oldType.parameterCount();
++ if (ignoreNewArgs != 0) {
++ if (ignoreNewArgs < 0)
++ throw new WrongMethodTypeException(newType+": not enough arguments for target "+target);
++
++ List<Class<?>> argList = newType.parameterList();
++ if (dropArguments == -ignoreNewArgs) {
++ // It's OK for the target to ignore some arguments.
++ // Because of the way the JVM stack grows, an adapter can
++ // trivially ignore an initial sequence of arguments.
++ argList = argList.subList(ignoreNewArgs, argList.size());
++ dropConv = DROP_INITIAL;
++ } else if (dropArguments == ignoreNewArgs) {
++
++ // Create a stack-cutter adapter now, to remove trailing args.
++ argList = argList.subList(0, argList.size()-ignoreNewArgs);
++ dropConv = DROP_FINAL;
++ } else {
++
++ throw new WrongMethodTypeException(newType+": cannot drop #"+dropArguments+" to match "+target);
++ }
++ midType = MethodType.make(newType.returnType(), argList);
++ dropSlots = MethodTypeForm.of(midType).parameterSlotCount()
++ - MethodTypeForm.of(newType).parameterSlotCount();
++ assert(dropSlots >= ignoreNewArgs);
++ assert(dropSlots <= ignoreNewArgs + MethodTypeForm.of(newType).longPrimitiveParameterCount());
++ newType = midType;
++ nargs = newType.parameterCount();
++ }
++ assert(nargs == oldType.parameterCount());
++
++ // work backwards through the argument pairs...
++ int sawLongs = 0;
++ for (int i = nargs; --i >= 0; ) {
++ src = newType.parameterType(i);
++ dest = oldType.parameterType(i);
++ pass = MH.canPassUnchecked(src, dest);
++ if (pass == 0)
++ throw new WrongMethodTypeException(newType+": cannot convert arg #"+i+" for target "+target);
++ if (dest == long.class || dest == double.class)
++ ++sawLongs;
++ if (pass > 0) continue;
++ // need a conversion at this point
++ if (!dest.isPrimitive()) {
++ midType = oldType.newParameterType(i, src);
++ target = makeCheckCast(target, midType, i + sawLongs, dest);
++ oldType = midType;
++ } else if (dest == boolean.class) {
++ midType = oldType.newParameterType(i, src);
++ target = makeTestBoolean(target, midType, i + sawLongs);
++ oldType = midType;
++ } else if (Wrappers.isSubwordOrInt(src)
++ && Wrappers.isSubwordOrInt(dest)) {
++ midType = oldType.newParameterType(i, src);
++ target = makeExtendInt(target, midType, i + sawLongs,
++ Wrappers.isSigned(dest), Wrappers.bitWidth(dest));
++ oldType = midType;
++ }
++ }
++
++ // No more explicit conversions needed.
++ // Finish with a dropping and/or retyping AMH.
++ switch (dropConv) {
++ case DROP_FINAL:
++ case DROP_INITIAL:
++ newType = dropConvType;
++ target = makeDropArguments(target, newType, dropSlots, (dropConv == DROP_FINAL));
++ break;
++ default:
++ assert(dropConv == RETYPE_ONLY);
++ break;
++ }
++ if (target.type() == newType)
++ return target;
++ else
++ return makeRetypeOnly(target, newType);
++ }
++
++ /** Conversions recognized by the JVM.
++ * They must align with enum AdapterKind in vm/prims/methodHandles.hpp.
++ */
++ static final int
++ RETYPE_ONLY = 0x0, // no argument changes; straight retype
++ DROP_INITIAL = 0x1, // drop initial argument (actually a no-op)
++ DROP_FINAL = 0x2, // drops the indicated & subsequent arguments
++ CHECK_CAST = 0x3, // requires a klassOop parameter
++ EXTEND_SIGN = 0x4, // includes bit-length parameter in low byte
++ EXTEND_ZERO = 0x5, // includes bit-length parameter in low byte
++ TEST_BOOLEAN = 0x6, // replace non-zero int value with 0x01
++ SWAP_ARGUMENT = 0x7, // swap the last argument with indicated one
++ PUSH_ARGUMENT = 0x8, // swap the last argument with indicated one
++ PUSH_PRIMITIVE = 0x9, // push constant I/J/F/D value (trailing arg)
++ PUSH_REFERENCE = 0xA, // push constant reference (trailing arg)
++ RICOCHET = 0xB, // run an adapter chain on the return value
++ FLYBY = 0xC, // operate first on reified argument list
++ CONV_MASK = 0xFF; // second LSB is the conversion op field
++
++ @Override
++ public String toString() {
++ MethodType adaptedType = ((MethodHandle)this).type();
++ Object namh = nonAdapter(target);
++ if (namh == null) namh = "unknown";
++ return "Adapted[" + adaptedType + "," + namh + "]";
++ }
++
++ private static MethodHandle nonAdapter(MethodHandle mh) {
++ while (mh instanceof AMH) {
++ mh = ((AMH)mh).target;
++ }
++ return mh;
++ }
++
++ /* Return one plus the position of the sole non-trivial difference
++ * between the given types. This is not a symmetric operation;
++ * we are considering adapting the targetType to adapterType.
++ * Trivial differences are those which could be ignored by the JVM
++ * without subverting the verifier.
++ * Return zero if there are no non-trivial differences.
++ * Otherwise, return the -2-N where N is the first non-trivial
++ * argument difference, or -1 for some other difference.
++ */
++ private static int diffTypes(MethodType targetType,
++ MethodType adapterType) {
++ Class<?> src = targetType.returnType();
++ Class<?> dest = adapterType.returnType();
++ if (MH.canPassUnchecked(src, dest) <= 0)
++ return -1;
++ int nargs = adapterType.parameterCount();
++ if (nargs != targetType.parameterCount())
++ return -1;
++ return diffTypes(targetType, 0, adapterType, 0, nargs);
++ }
++ private static int diffTypes(MethodType targetType, int tstart,
++ MethodType adapterType, int astart,
++ int nargs) {
++ int res = 0;
++ for (int i = 0; i < nargs; i++) {
++ Class<?> src = adapterType.parameterType(tstart+i);
++ Class<?> dest = targetType.parameterType(astart+i);
++ if (MH.canPassUnchecked(src, dest) <= 0) {
++ // found a difference; is it the only one so far?
++ if (res != 0)
++ return -1-res; // return -2-i for prev. i
++ res = 1+i;
++ }
++ }
++ return res;
++ }
++
++ /** Can a retyping adapter (alone) validly convert target to newType? */
++ static boolean canRetypeOnly(MethodHandle target, MethodType newType) {
++ return diffTypes(target.type(), newType) == 0;
++ }
++
++ /** Factory method: Performs no conversions; simply retypes the target. */
++ static MethodHandle makeRetypeOnly(MethodHandle target, MethodType newType) {
++ assert(canRetypeOnly(target, newType));
++ return new AMH(target, newType, 0, RETYPE_ONLY, null);
++ }
++
++ /** Can an adapter simply drop arguments to convert target to newType? */
++ static boolean canDropArguments(MethodHandle target, MethodType newType,
++ int dropArgs, boolean dropAfter) {
++ MethodType oldType = target.type();
++ List<Class<?>> ptypes = oldType.parameterList();
++ if (dropArgs > ptypes.size())
++ return false;
++ if (dropAfter)
++ ptypes = ptypes.subList(0, ptypes.size() - dropArgs);
++ else
++ ptypes = ptypes.subList(dropArgs, ptypes.size());
++ MethodType midType = MethodType.make(oldType.returnType(), ptypes);
++ return diffTypes(midType, newType) == 0;
++ }
++
++ /** Factory method: Drop selected initial or final arguments. */
++ static MethodHandle makeDropArguments(MethodHandle target, MethodType newType,
++ int dropArgs, boolean dropAfter) {
++ assert(canDropArguments(target, newType, dropArgs, dropAfter));
++ int dropSlots, conv;
++ MethodTypeForm form = MethodTypeForm.of(target.type());
++ int argCount = form.parameterCount();
++ int slotCount = form.parameterSlotCount();
++ if (dropArgs == 0) {
++ conv = RETYPE_ONLY;
++ dropSlots = 0;
++ } else if (dropArgs == argCount) {
++ conv = DROP_INITIAL; // could choose either...
++ dropSlots = slotCount;
++ } else if (dropAfter) {
++ conv = DROP_FINAL;
++ int lastKeptArg = argCount - dropArgs - 1;
++ // pop to last kept arg:
++ dropSlots = form.parameterToArgSlot(lastKeptArg);
++ assert(dropSlots > 0 && dropSlots < slotCount);
++ } else {
++ conv = DROP_INITIAL;
++ int lastDroppedArg = dropArgs - 1;
++ dropSlots = slotCount - form.parameterToArgSlot(lastDroppedArg);
++ assert(dropSlots > 0 && dropSlots < slotCount);
++ }
++ return new AMH(target, newType, dropSlots, conv, null);
++ }
++
++ /** Can a checkcast adapter validly convert target to newType? */
++ static boolean canCheckCast(MethodHandle target, MethodType newType,
++ int arg, Class<?> destClass) {
++ MethodType targetType = target.type();
++ int pass = MH.canPassUnchecked(destClass, targetType.parameterType(arg));
++ if (pass <= 0)
++ return false;
++ pass = MH.canPassUnchecked(newType.parameterType(arg), destClass);
++ if (pass >= 0)
++ return (pass != 0);
++ int diff = diffTypes(target.type(), newType);
++ // allow any cast between reference types, even a silly one
++ return (diff == 0 || diff == arg+1);
++ }
++
++ /** Factory method: Forces a cast at the given argument. */
++ static MethodHandle makeCheckCast(MethodHandle target, MethodType newType,
++ int arg, Class<?> destClass) {
++ assert(canCheckCast(target, newType, arg, destClass));
++ int argSlot = MethodTypeForm.of(newType).parameterToArgSlot(arg);
++ return new AMH(target, newType, argSlot, CHECK_CAST, destClass);
++ }
++
++ /** Can an integer truncation adapter validly convert target to newType? */
++ static boolean canExtendInt(MethodHandle target, MethodType newType,
++ int arg, boolean signed, int bitWidth) {
++ if (arg >= newType.parameterCount())
++ return false;
++ if (bitWidth >= 32 || bitWidth <= 0)
++ throw new IllegalArgumentException("bitWidth="+bitWidth);
++ MethodType oldType = target.type();
++ int diff = diffTypes(oldType, newType);
++ if (diff != 0 && diff != arg+1)
++ return false;
++ Class<?> src = oldType.parameterType(arg);
++ Class<?> dest = newType.parameterType(arg);
++ if (!Wrappers.isSubwordOrInt(src) || !Wrappers.isSubwordOrInt(dest))
++ return false;
++ // TO DO: model the conversion better, to allow more cases
++ return signed == Wrappers.isSigned(dest) &&
++ bitWidth <= Wrappers.bitWidth(dest);
++ }
++
++ /** Factory method: Truncate the given argument with zero or sign extension. */
++ static MethodHandle makeExtendInt(MethodHandle target, MethodType newType,
++ int arg, boolean isSigned, int bitWidth) {
++ assert(canExtendInt(target, newType, arg, isSigned, bitWidth));
++ bitWidth <<= 8; // optional argument gets shifted up
++ assert((CONV_MASK & 0xFF00) == 0); // low bits clear for bitWidth
++ assert((bitWidth & ~0xFF00) == 0); // high bits clear for conversion op
++ int conv = (isSigned ? EXTEND_SIGN : EXTEND_ZERO) | bitWidth;
++ int argSlot = MethodTypeForm.of(newType).parameterToArgSlot(arg);
++ return new AMH(target, newType, argSlot, conv, null);
++ }
++
++ /** Factory method: Convert the given argument to a boolean. */
++ private static boolean canTestBoolean(MethodHandle target, MethodType newType,
++ int arg) {
++ if (arg >= newType.parameterCount())
++ return false;
++ MethodType oldType = target.type();
++ if (diffTypes(oldType, newType) != arg+1)
++ return false;
++ if (newType.parameterType(arg) != boolean.class)
++ return false;
++ return Wrappers.isSubwordOrInt(oldType.parameterType(arg));
++ }
++
++ /** Can a boolean test adapter validly convert target to newType? */
++ private static MethodHandle makeTestBoolean(MethodHandle target, MethodType newType, int arg) {
++ assert(canTestBoolean(target, newType, arg));
++ int argSlot = MethodTypeForm.of(newType).parameterToArgSlot(arg);
++ return new AMH(target, newType, argSlot, TEST_BOOLEAN, null);
++ }
++
+}
diff --git a/src/share/classes/java/dyn/impl/Access.java b/src/share/classes/java/dyn/impl/Access.java
new file mode 100644
@@ -2364,7 +2905,7 @@ new file mode 100644
new file mode 100644
--- /dev/null
+++ b/src/share/classes/java/dyn/impl/BMH.java
-@@ -0,0 +1,56 @@
+@@ -0,0 +1,61 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -2419,6 +2960,11 @@ new file mode 100644
+ ptype0.cast(receiver); // check the type now (one last time)
+ ptypes = Arrays.copyOfRange(ptypes, 1, ptypes.length);
+ return MethodType.make(type.returnType(), ptypes);
++ }
++
++ @Override
++ public String toString() {
++ return "Bound[" + super.toString() + "]";
+ }
+}
diff --git a/src/share/classes/java/dyn/impl/DMH.java b/src/share/classes/java/dyn/impl/DMH.java
@@ -2471,11 +3017,11 @@ new file mode 100644
+ }
+ }
+}
-diff --git a/src/share/classes/java/dyn/impl/MH.java b/src/share/classes/java/dyn/impl/MH.java
+diff --git a/src/share/classes/java/dyn/impl/DynCallSite.java b/src/share/classes/java/dyn/impl/DynCallSite.java
new file mode 100644
--- /dev/null
-+++ b/src/share/classes/java/dyn/impl/MH.java
-@@ -0,0 +1,185 @@
++++ b/src/share/classes/java/dyn/impl/DynCallSite.java
+@@ -0,0 +1,60 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -2504,6 +3050,71 @@ new file mode 100644
+package java.dyn.impl;
+
+import java.dyn.*;
++
++/**
++ * The CallSite privately created by the JVM at every invokedynamic instruction.
++ * @author jrose
++ */
++class DynCallSite extends CallSite {
++ // Fields used only by the JVM. Do not use or change.
++ Object vmref;
++ long vmdata;
++
++ private DynCallSite(Class<?> caller, String name, MethodType type) {
++ super(caller, name, type);
++ }
++
++ @Override
++ public void setTarget(MethodHandle mh) {
++ checkTarget(mh);
++ if (MH.JVM_SUPPORT)
++ MH.linkCallSite(this, mh);
++ else
++ super.setTarget(mh);
++ }
++
++ // this is the up-call from the JVM:
++ static DynCallSite makeSite(Class<?> caller, String name, MethodType type,
++ long vmdata) {
++ DynCallSite site = new DynCallSite(caller, name, type);
++ site.vmdata = vmdata;
++ System.out.println("DynCallSite: "+site);
++ return site;
++ }
++}
+diff --git a/src/share/classes/java/dyn/impl/MH.java b/src/share/classes/java/dyn/impl/MH.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/impl/MH.java
+@@ -0,0 +1,242 @@
++/*
++ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation. Sun designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Sun in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
++ * CA 95054 USA or visit www.sun.com if you need additional information or
++ * have any questions.
++ */
++
++package java.dyn.impl;
++
++import java.dyn.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
@@ -2519,8 +3130,8 @@ new file mode 100644
+
+ // Fields used only by the JVM. Do not use or change.
+ Object vmref; // often the method, could be something else
-+ long vmdata; // additional data; could be an int or a C pointer
-+ long entry; // call entry; a stub or method entry point
++ long vmdata; // additional data; could be a 2 ints or a C pointer
++ long entry; // call entry; a stub or method entry point
+
+ /**
+ * VM-based method handles must have a security token.
@@ -2622,9 +3233,55 @@ new file mode 100644
+ throw new NullPointerException();
+ }
+
++ /**
++ * Determine if the JVM verifier allows a value of type src to be
++ * passed to a formal parameter (or return variable) of type dest.
++ * Returns 1 if the verifier allows the types to match without conversion.
++ * Returns -1 if a cast or narrowing conversion can match the types.
++ * (Autoboxing is not supported here; it must be done via Java code.)
++ * Returns 0 otherwise.
++ */
++ static int canPassUnchecked(Class<?> src, Class<?> dest) {
++ if (src == dest)
++ return 1;
++
++ if (dest.isPrimitive()) {
++ if (dest == void.class)
++ // Return anything to a caller expecting void.
++ return 1;
++ if (!src.isPrimitive())
++ // Cannot pass a reference to any primitive type (exc. void).
++ return 0;
++ boolean swt = Wrappers.isSubwordOrInt(src);
++ boolean dwt = Wrappers.isSubwordOrInt(dest);
++ if (swt && dwt) {
++ if (Wrappers.bitWidth(src) >= Wrappers.bitWidth(dest))
++ return -1; // truncation may be required
++ if (!Wrappers.isSigned(dest) && Wrappers.isSigned(src))
++ return -1; // sign elimination may be required
++ return 1;
++ }
++ return 0;
++
++ } else if (src.isPrimitive()) {
++ // Cannot pass a primitive to any reference type.
++ // (Maybe allow null.class?)
++ return 0;
++ }
++
++ // The verifier treats interfaces exactly like Object.
++ if (dest.isInterface()) dest = Object.class;
++ //if (src.isInterface()) src = Object.class;
++ if (dest == Object.class)
++ // pass any reference to object or an arb. interface
++ return 1;
++ // else it's a definite "maybe" (cast is required)
++ return -1;
++ }
++
+ @Override
+ public String toString() {
-+ String name = JVM_SUPPORT ? methodName(this) : null;
++ String name = JVM_SUPPORT ? getMethodName(this) : null;
+ if (name == null) name = "*";
+ return name + ":" + ((MethodHandle)this).type();
+ }
@@ -2632,7 +3289,7 @@ new file mode 100644
+ /// The JVM interface for this package is all here:
+
+ /** Initialize the method handle to adapt the call. */
-+ static native void init(AMH self);
++ static native void init(AMH self, int argSlot, int conversion, Object ref);
+ /** Initialize the method handle to call the correct method, directly. */
+ static native void init(BMH self, Object refm, Object receiver);
+ /** Initialize the method handle to call as if by invoke. */
@@ -2643,8 +3300,19 @@ new file mode 100644
+ */
+ static native void init(MTForm self, MethodType erased);
+
-+ /** For debugging. Fetch the name of the handled method, if available. */
-+ static native String methodName(MH self);
++ /** Fetch the name of the handled method, if available.
++ * This routine is for debugging and reflection.
++ */
++ static native String getMethodName(MH self);
++
++ /** Tell the JVM that we need to change the target of an invokedynamic. */
++ static native void linkCallSite(DynCallSite site, MH target);
++
++ /** Fetch the vmref field.
++ * It will be sanitized as necessary to avoid exposing non-Java references.
++ * This routine is for debugging and reflection.
++ */
++ static native Object getVMRef(MH self);
+
+ private static native void registerNatives();
+ static final boolean JVM_SUPPORT;