meth, indy: update patch repo consistently with yesterday's push to JDK7 of 6829144
--- a/series Wed Apr 08 02:53:50 2009 -0700
+++ b/series Wed May 06 19:45:31 2009 -0700
@@ -1,8 +1,11 @@
# base = 940223097cb1 in http://hg.openjdk.java.net/bsd-port/bsd-port/jdk
-anonk.patch #-/anonk #+940223097cb1
-meth.patch #-/meth #+940223097cb1
-indy.patch #-/indy #+940223097cb1
-indy.verify.patch #-/indy #+940223097cb1
+anonk-6829144.patch #-/anonk #+940223097cb1
+meth-6829144.patch #-/meth #+940223097cb1
+indy-6829144.verify.patch #-/indy #+940223097cb1
+indy-6829144.patch #-/indy #+940223097cb1
+indy.tests.patch #-/indy #+940223097cb1
-#inti.patch #-/inti #+jdk7-b34 #-buildable
-callcc.patch #-/callcc #+jdk7-b30 #-testable
+# Keep these separate, for debugging and review:
+dyncast.patch #+dyncast #-/dyncast
+inti.patch #+inti #-/inti #-buildable
+callcc.patch #+callcc #-/callcc #-testable
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/anonk-6829144.patch Wed May 06 19:45:31 2009 -0700
@@ -0,0 +1,1469 @@
+6829144: JSR 292 JVM features need a provisional Java API
+Summary: provide API (sun-private) for underlying anonymous class mechanism
+
+diff --git a/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java b/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java
+@@ -0,0 +1,297 @@
++/*
++ * Copyright 2008-2009 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 sun.dyn.anon;
++
++import java.io.IOException;
++import java.io.InputStream;
++import java.lang.reflect.InvocationTargetException;
++import java.lang.reflect.Method;
++
++/**
++ * Anonymous class loader. Will load any valid classfile, producing
++ * a {@link Class} metaobject, without installing that class in the
++ * system dictionary. Therefore, {@link Class#forName(String)} will never
++ * produce a reference to an anonymous class.
++ * <p>
++ * The access permissions of the anonymous class are borrowed from
++ * a <em>host class</em>. The new class behaves as if it were an
++ * inner class of the host class. It can access the host's private
++ * members, if the creator of the class loader has permission to
++ * do so (or to create accessible reflective objects).
++ * <p>
++ * When the anonymous class is loaded, elements of its constant pool
++ * can be patched to new values. This provides a hook to pre-resolve
++ * named classes in the constant pool to other classes, including
++ * anonymous ones. Also, string constants can be pre-resolved to
++ * any reference. (The verifier treats non-string, non-class reference
++ * constants as plain objects.)
++ * <p>
++ * Why include the patching function? It makes some use cases much easier.
++ * Second, the constant pool needed some internal patching anyway,
++ * to anonymize the loaded class itself. Finally, if you are going
++ * to use this seriously, you'll want to build anonymous classes
++ * on top of pre-existing anonymous classes, and that requires patching.
++ *
++ * <p>%%% TO-DO:
++ * <ul>
++ * <li>needs better documentation</li>
++ * <li>needs more security work (for safe delegation)</li>
++ * <li>needs a clearer story about error processing</li>
++ * <li>patch member references also (use ';' as delimiter char)</li>
++ * <li>patch method references to (conforming) method handles</li>
++ * </ul>
++ *
++ * @author jrose
++ * @author Remi Forax
++ * @see <a href="http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm">
++ * http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm</a>
++ */
++
++public class AnonymousClassLoader {
++ final Class<?> hostClass;
++
++ // Note: Do not refactor the calls to checkHostClass unless you
++ // also adjust this constant:
++ private static int CHC_CALLERS = 3;
++
++ public AnonymousClassLoader() {
++ this.hostClass = checkHostClass(null);
++ }
++ public AnonymousClassLoader(Class<?> hostClass) {
++ this.hostClass = checkHostClass(hostClass);
++ }
++
++ private static Class<?> getTopLevelClass(Class<?> clazz) {
++ for(Class<?> outer = clazz.getDeclaringClass(); outer != null;
++ outer = outer.getDeclaringClass()) {
++ clazz = outer;
++ }
++ return clazz;
++ }
++
++ private static Class<?> checkHostClass(Class<?> hostClass) {
++ // called only from the constructor
++ // does a context-sensitive check on caller class
++ // CC[0..3] = {Reflection, this.checkHostClass, this.<init>, caller}
++ Class<?> caller = sun.reflect.Reflection.getCallerClass(CHC_CALLERS);
++
++ if (caller == null) {
++ // called from the JVM directly
++ if (hostClass == null)
++ return AnonymousClassLoader.class; // anything central will do
++ return hostClass;
++ }
++
++ if (hostClass == null)
++ hostClass = caller; // default value is caller itself
++
++ // anonymous class will access hostClass on behalf of caller
++ Class<?> callee = hostClass;
++
++ if (caller == callee)
++ // caller can always nominate itself to grant caller's own access rights
++ return hostClass;
++
++ // normalize caller and callee to their top-level classes:
++ caller = getTopLevelClass(caller);
++ callee = getTopLevelClass(callee);
++ if (caller == callee)
++ return caller;
++
++ ClassLoader callerCL = caller.getClassLoader();
++ if (callerCL == null) {
++ // caller is trusted code, so accept the proposed hostClass
++ return hostClass;
++ }
++
++ // %%% should do something with doPrivileged, because trusted
++ // code should have a way to execute on behalf of
++ // partially-trusted clients
++
++ // Does the caller have the right to access the private
++ // members of the callee? If not, raise an error.
++ final int ACC_PRIVATE = 2;
++ try {
++ sun.reflect.Reflection.ensureMemberAccess(caller, callee, null, ACC_PRIVATE);
++ } catch (IllegalAccessException ee) {
++ throw new IllegalArgumentException(ee);
++ }
++
++ return hostClass;
++ }
++
++ public Class<?> loadClass(byte[] classFile) {
++ if (defineAnonymousClass == null) {
++ // no JVM support; try to fake an approximation
++ try {
++ return fakeLoadClass(new ConstantPoolParser(classFile).createPatch());
++ } catch (InvalidConstantPoolFormatException ee) {
++ throw new IllegalArgumentException(ee);
++ }
++ }
++ return loadClass(classFile, null);
++ }
++
++ public Class<?> loadClass(ConstantPoolPatch classPatch) {
++ if (defineAnonymousClass == null) {
++ // no JVM support; try to fake an approximation
++ return fakeLoadClass(classPatch);
++ }
++ Object[] patches = classPatch.patchArray;
++ // Convert class names (this late in the game)
++ // to use slash '/' instead of dot '.'.
++ // Java likes dots, but the JVM likes slashes.
++ for (int i = 0; i < patches.length; i++) {
++ Object value = patches[i];
++ if (value != null) {
++ byte tag = classPatch.getTag(i);
++ switch (tag) {
++ case ConstantPoolVisitor.CONSTANT_Class:
++ if (value instanceof String) {
++ if (patches == classPatch.patchArray)
++ patches = patches.clone();
++ patches[i] = ((String)value).replace('.', '/');
++ }
++ break;
++ case ConstantPoolVisitor.CONSTANT_Fieldref:
++ case ConstantPoolVisitor.CONSTANT_Methodref:
++ case ConstantPoolVisitor.CONSTANT_InterfaceMethodref:
++ case ConstantPoolVisitor.CONSTANT_NameAndType:
++ // When/if the JVM supports these patches,
++ // we'll probably need to reformat them also.
++ // Meanwhile, let the class loader create the error.
++ break;
++ }
++ }
++ }
++ return loadClass(classPatch.outer.classFile, classPatch.patchArray);
++ }
++
++ private Class<?> loadClass(byte[] classFile, Object[] patchArray) {
++ try {
++ return (Class<?>)
++ defineAnonymousClass.invoke(unsafe,
++ hostClass, classFile, patchArray);
++ } catch (Exception ex) {
++ throwReflectedException(ex);
++ throw new RuntimeException("error loading into "+hostClass, ex);
++ }
++ }
++
++ private static void throwReflectedException(Exception ex) {
++ if (ex instanceof InvocationTargetException) {
++ Throwable tex = ((InvocationTargetException)ex).getTargetException();
++ if (tex instanceof Error)
++ throw (Error) tex;
++ ex = (Exception) tex;
++ }
++ if (ex instanceof RuntimeException) {
++ throw (RuntimeException) ex;
++ }
++ }
++
++ private Class<?> fakeLoadClass(ConstantPoolPatch classPatch) {
++ // Implementation:
++ // 1. Make up a new name nobody has used yet.
++ // 2. Inspect the tail-header of the class to find the this_class index.
++ // 3. Patch the CONSTANT_Class for this_class to the new name.
++ // 4. Add other CP entries required by (e.g.) string patches.
++ // 5. Flatten Class constants down to their names, making sure that
++ // the host class loader can pick them up again accurately.
++ // 6. Generate the edited class file bytes.
++ //
++ // Potential limitations:
++ // * The class won't be truly anonymous, and may interfere with others.
++ // * Flattened class constants might not work, because of loader issues.
++ // * Pseudo-string constants will not flatten down to real strings.
++ // * Method handles will (of course) fail to flatten to linkage strings.
++ if (true) throw new UnsupportedOperationException("NYI");
++ Object[] cpArray;
++ try {
++ cpArray = classPatch.getOriginalCP();
++ } catch (InvalidConstantPoolFormatException ex) {
++ throw new RuntimeException(ex);
++ }
++ int thisClassIndex = classPatch.getParser().getThisClassIndex();
++ String thisClassName = (String) cpArray[thisClassIndex];
++ synchronized (AnonymousClassLoader.class) {
++ thisClassName = thisClassName+"\\|"+(++fakeNameCounter);
++ }
++ classPatch.putUTF8(thisClassIndex, thisClassName);
++ byte[] classFile = null;
++ return unsafe.defineClass(null, classFile, 0, classFile.length,
++ hostClass.getClassLoader(),
++ hostClass.getProtectionDomain());
++ }
++ private static int fakeNameCounter = 99999;
++
++ // ignore two warnings on this line:
++ static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
++ // preceding line requires that this class be on the boot class path
++
++ static private final Method defineAnonymousClass;
++ static {
++ Method dac = null;
++ Class<? extends sun.misc.Unsafe> unsafeClass = unsafe.getClass();
++ try {
++ dac = unsafeClass.getMethod("defineAnonymousClass",
++ Class.class,
++ byte[].class,
++ Object[].class);
++ } catch (Exception ee) {
++ dac = null;
++ }
++ defineAnonymousClass = dac;
++ }
++
++ private static void noJVMSupport() {
++ throw new UnsupportedOperationException("no JVM support for anonymous classes");
++ }
++
++
++ private static native Class<?> loadClassInternal(Class<?> hostClass,
++ byte[] classFile,
++ Object[] patchArray);
++
++ public static byte[] readClassFile(Class<?> templateClass) throws IOException {
++ String templateName = templateClass.getName();
++ int lastDot = templateName.lastIndexOf('.');
++ java.net.URL url = templateClass.getResource(templateName.substring(lastDot+1)+".class");
++ java.net.URLConnection connection = url.openConnection();
++ int contentLength = connection.getContentLength();
++ if (contentLength < 0)
++ throw new IOException("invalid content length "+contentLength);
++
++ byte[] classFile = new byte[contentLength];
++ InputStream tcs = connection.getInputStream();
++ for (int fill = 0, nr; fill < classFile.length; fill += nr) {
++ nr = tcs.read(classFile, fill, classFile.length - fill);
++ if (nr < 0)
++ throw new IOException("premature end of file");
++ }
++ return classFile;
++ }
++}
+diff --git a/src/share/classes/sun/dyn/anon/ConstantPoolParser.java b/src/share/classes/sun/dyn/anon/ConstantPoolParser.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/sun/dyn/anon/ConstantPoolParser.java
+@@ -0,0 +1,368 @@
++/*
++ * Copyright 2008-2009 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 sun.dyn.anon;
++
++import java.io.IOException;
++import java.io.OutputStream;
++import java.nio.BufferUnderflowException;
++import java.nio.ByteBuffer;
++
++import static sun.dyn.anon.ConstantPoolVisitor.*;
++
++/** A constant pool parser.
++ */
++public class ConstantPoolParser {
++ final byte[] classFile;
++ final byte[] tags;
++ final char[] firstHeader; // maghi, maglo, minor, major, cplen
++
++ // these are filled in on first parse:
++ int endOffset;
++ char[] secondHeader; // flags, this_class, super_class, intlen
++
++ // used to decode UTF8 array
++ private char[] charArray = new char[80];
++
++ /** Creates a constant pool parser.
++ * @param classFile an array of bytes containing a class.
++ * @throws InvalidConstantPoolFormatException if the header of the class has errors.
++ */
++ public ConstantPoolParser(byte[] classFile) throws InvalidConstantPoolFormatException {
++ this.classFile = classFile;
++ this.firstHeader = parseHeader(classFile);
++ this.tags = new byte[firstHeader[4]];
++ }
++
++ /** Create a constant pool parser by loading the bytecodes of the
++ * class taken as argument.
++ *
++ * @param templateClass the class to parse.
++ *
++ * @throws IOException raised if an I/O occurs when loading
++ * the bytecode of the template class.
++ * @throws InvalidConstantPoolFormatException if the header of the class has errors.
++ *
++ * @see #ConstantPoolParser(byte[])
++ * @see AnonymousClassLoader#readClassFile(Class)
++ */
++ public ConstantPoolParser(Class<?> templateClass) throws IOException, InvalidConstantPoolFormatException {
++ this(AnonymousClassLoader.readClassFile(templateClass));
++ }
++
++ /** Creates an empty patch to patch the class file
++ * used by the current parser.
++ * @return a new class patch.
++ */
++ public ConstantPoolPatch createPatch() {
++ return new ConstantPoolPatch(this);
++ }
++
++ /** Report the tag of the indicated CP entry.
++ * @param index
++ * @return one of {@link ConstantPoolVisitor#CONSTANT_Utf8}, etc.
++ */
++ public byte getTag(int index) {
++ getEndOffset(); // trigger an exception if we haven't parsed yet
++ return tags[index];
++ }
++
++ /** Report the length of the constant pool. */
++ public int getLength() {
++ return firstHeader[4];
++ }
++
++ /** Report the offset, within the class file, of the start of the constant pool. */
++ public int getStartOffset() {
++ return firstHeader.length * 2;
++ }
++
++ /** Report the offset, within the class file, of the end of the constant pool. */
++ public int getEndOffset() {
++ if (endOffset == 0)
++ throw new IllegalStateException("class file has not yet been parsed");
++ return endOffset;
++ }
++
++ /** Report the CP index of this class's own name. */
++ public int getThisClassIndex() {
++ getEndOffset(); // provoke exception if not yet parsed
++ return secondHeader[1];
++ }
++
++ /** Report the total size of the class file. */
++ public int getTailLength() {
++ return classFile.length - getEndOffset();
++ }
++
++ /** Write the head (header plus constant pool)
++ * of the class file to the indicated stream.
++ */
++ public void writeHead(OutputStream out) throws IOException {
++ out.write(classFile, 0, getEndOffset());
++ }
++
++ /** Write the head (header plus constant pool)
++ * of the class file to the indicated stream,
++ * incorporating the non-null entries of the given array
++ * as patches.
++ */
++ void writePatchedHead(OutputStream out, Object[] patchArray) {
++ // this will be useful to partially emulate the class loader on old JVMs
++ throw new UnsupportedOperationException("Not yet implemented");
++ }
++
++ /** Write the tail (everything after the constant pool)
++ * of the class file to the indicated stream.
++ */
++ public void writeTail(OutputStream out) throws IOException {
++ out.write(classFile, getEndOffset(), getTailLength());
++ }
++
++ private static char[] parseHeader(byte[] classFile) throws InvalidConstantPoolFormatException {
++ char[] result = new char[5];
++ ByteBuffer buffer = ByteBuffer.wrap(classFile);
++ for (int i = 0; i < result.length; i++)
++ result[i] = (char) getUnsignedShort(buffer);
++ int magic = result[0] << 16 | result[1] << 0;
++ if (magic != 0xCAFEBABE)
++ throw new InvalidConstantPoolFormatException("invalid magic number "+magic);
++ // skip major, minor version
++ int len = result[4];
++ if (len < 1)
++ throw new InvalidConstantPoolFormatException("constant pool length < 1");
++ return result;
++ }
++
++ /** Parse the constant pool of the class
++ * calling a method visit* each time a constant pool entry is parsed.
++ *
++ * The order of the calls to visit* is not guaranteed to be the same
++ * than the order of the constant pool entry in the bytecode array.
++ *
++ * @param visitor
++ * @throws InvalidConstantPoolFormatException
++ */
++ public void parse(ConstantPoolVisitor visitor) throws InvalidConstantPoolFormatException {
++ ByteBuffer buffer = ByteBuffer.wrap(classFile);
++ buffer.position(getStartOffset()); //skip header
++
++ Object[] values = new Object[getLength()];
++ try {
++ parseConstantPool(buffer, values, visitor);
++ } catch(BufferUnderflowException e) {
++ throw new InvalidConstantPoolFormatException(e);
++ }
++ if (endOffset == 0) {
++ endOffset = buffer.position();
++ secondHeader = new char[4];
++ for (int i = 0; i < secondHeader.length; i++) {
++ secondHeader[i] = (char) getUnsignedShort(buffer);
++ }
++ }
++ resolveConstantPool(values, visitor);
++ }
++
++ private char[] getCharArray(int utfLength) {
++ if (utfLength <= charArray.length)
++ return charArray;
++ return charArray = new char[utfLength];
++ }
++
++ private void parseConstantPool(ByteBuffer buffer, Object[] values, ConstantPoolVisitor visitor) throws InvalidConstantPoolFormatException {
++ for (int i = 1; i < tags.length; ) {
++ byte tag = (byte) getUnsignedByte(buffer);
++ assert(tags[i] == 0 || tags[i] == tag);
++ tags[i] = tag;
++ switch (tag) {
++ case CONSTANT_Utf8:
++ int utfLen = getUnsignedShort(buffer);
++ String value = getUTF8(buffer, utfLen, getCharArray(utfLen));
++ visitor.visitUTF8(i, CONSTANT_Utf8, value);
++ tags[i] = tag;
++ values[i++] = value;
++ break;
++ case CONSTANT_Integer:
++ visitor.visitConstantValue(i, tag, buffer.getInt());
++ i++;
++ break;
++ case CONSTANT_Float:
++ visitor.visitConstantValue(i, tag, buffer.getFloat());
++ i++;
++ break;
++ case CONSTANT_Long:
++ visitor.visitConstantValue(i, tag, buffer.getLong());
++ i+=2;
++ break;
++ case CONSTANT_Double:
++ visitor.visitConstantValue(i, tag, buffer.getDouble());
++ i+=2;
++ break;
++
++ case CONSTANT_Class: // fall through:
++ case CONSTANT_String:
++ tags[i] = tag;
++ values[i++] = new int[] { getUnsignedShort(buffer) };
++ break;
++
++ case CONSTANT_Fieldref: // fall through:
++ case CONSTANT_Methodref: // fall through:
++ case CONSTANT_InterfaceMethodref: // fall through:
++ case CONSTANT_NameAndType:
++ tags[i] = tag;
++ values[i++] = new int[] { getUnsignedShort(buffer), getUnsignedShort(buffer) };
++ break;
++ default:
++ throw new AssertionError("invalid constant "+tag);
++ }
++ }
++ }
++
++ private void resolveConstantPool(Object[] values, ConstantPoolVisitor visitor) {
++ // clean out the int[] values, which are temporary
++ for (int beg = 1, end = values.length-1, beg2, end2;
++ beg <= end;
++ beg = beg2, end = end2) {
++ beg2 = end; end2 = beg-1;
++ //System.out.println("CP resolve pass: "+beg+".."+end);
++ for (int i = beg; i <= end; i++) {
++ Object value = values[i];
++ if (!(value instanceof int[]))
++ continue;
++ int[] array = (int[]) value;
++ byte tag = tags[i];
++ switch (tag) {
++ case CONSTANT_String:
++ String stringBody = (String) values[array[0]];
++ visitor.visitConstantString(i, tag, stringBody, array[0]);
++ values[i] = null;
++ break;
++ case CONSTANT_Class: {
++ String className = (String) values[array[0]];
++ // use the external form favored by Class.forName:
++ className = className.replace('/', '.');
++ visitor.visitConstantString(i, tag, className, array[0]);
++ values[i] = className;
++ break;
++ }
++ case CONSTANT_NameAndType: {
++ String memberName = (String) values[array[0]];
++ String signature = (String) values[array[1]];
++ visitor.visitDescriptor(i, tag, memberName, signature,
++ array[0], array[1]);
++ values[i] = new String[] {memberName, signature};
++ break;
++ }
++ case CONSTANT_Fieldref: // fall through:
++ case CONSTANT_Methodref: // fall through:
++ case CONSTANT_InterfaceMethodref: {
++ Object className = values[array[0]];
++ Object nameAndType = values[array[1]];
++ if (!(className instanceof String) ||
++ !(nameAndType instanceof String[])) {
++ // one more pass is needed
++ if (beg2 > i) beg2 = i;
++ if (end2 < i) end2 = i;
++ continue;
++ }
++ String[] nameAndTypeArray = (String[]) nameAndType;
++ visitor.visitMemberRef(i, tag,
++ (String)className,
++ nameAndTypeArray[0],
++ nameAndTypeArray[1],
++ array[0], array[1]);
++ values[i] = null;
++ }
++ break;
++ default:
++ continue;
++ }
++ }
++ }
++ }
++
++ private static int getUnsignedByte(ByteBuffer buffer) {
++ return buffer.get() & 0xFF;
++ }
++
++ private static int getUnsignedShort(ByteBuffer buffer) {
++ int b1 = getUnsignedByte(buffer);
++ int b2 = getUnsignedByte(buffer);
++ return (b1 << 8) + (b2 << 0);
++ }
++
++ private static String getUTF8(ByteBuffer buffer, int utfLen, char[] charArray) throws InvalidConstantPoolFormatException {
++ int utfLimit = buffer.position() + utfLen;
++ int index = 0;
++ while (buffer.position() < utfLimit) {
++ int c = buffer.get() & 0xff;
++ if (c > 127) {
++ buffer.position(buffer.position() - 1);
++ return getUTF8Extended(buffer, utfLimit, charArray, index);
++ }
++ charArray[index++] = (char)c;
++ }
++ return new String(charArray, 0, index);
++ }
++
++ private static String getUTF8Extended(ByteBuffer buffer, int utfLimit, char[] charArray, int index) throws InvalidConstantPoolFormatException {
++ int c, c2, c3;
++ while (buffer.position() < utfLimit) {
++ c = buffer.get() & 0xff;
++ switch (c >> 4) {
++ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
++ /* 0xxxxxxx*/
++ charArray[index++] = (char)c;
++ break;
++ case 12: case 13:
++ /* 110x xxxx 10xx xxxx*/
++ c2 = buffer.get();
++ if ((c2 & 0xC0) != 0x80)
++ throw new InvalidConstantPoolFormatException(
++ "malformed input around byte " + buffer.position());
++ charArray[index++] = (char)(((c & 0x1F) << 6) |
++ (c2 & 0x3F));
++ break;
++ case 14:
++ /* 1110 xxxx 10xx xxxx 10xx xxxx */
++ c2 = buffer.get();
++ c3 = buffer.get();
++ if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80))
++ throw new InvalidConstantPoolFormatException(
++ "malformed input around byte " + (buffer.position()));
++ charArray[index++] = (char)(((c & 0x0F) << 12) |
++ ((c2 & 0x3F) << 6) |
++ ((c3 & 0x3F) << 0));
++ break;
++ default:
++ /* 10xx xxxx, 1111 xxxx */
++ throw new InvalidConstantPoolFormatException(
++ "malformed input around byte " + buffer.position());
++ }
++ }
++ // The number of chars produced may be less than utflen
++ return new String(charArray, 0, index);
++ }
++}
+diff --git a/src/share/classes/sun/dyn/anon/ConstantPoolPatch.java b/src/share/classes/sun/dyn/anon/ConstantPoolPatch.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/sun/dyn/anon/ConstantPoolPatch.java
+@@ -0,0 +1,503 @@
++/*
++ * Copyright 2008-2009 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 sun.dyn.anon;
++
++import java.io.IOException;
++import java.io.OutputStream;
++import java.util.Arrays;
++import java.util.HashSet;
++import java.util.IdentityHashMap;
++import java.util.Map;
++
++import static sun.dyn.anon.ConstantPoolVisitor.*;
++
++/** A class and its patched constant pool.
++ *
++ * This class allow to modify (patch) a constant pool
++ * by changing the value of its entry.
++ * Entry are referenced using index that can be get
++ * by parsing the constant pool using
++ * {@link ConstantPoolParser#parse(ConstantPoolVisitor)}.
++ *
++ * @see ConstantPoolVisitor
++ * @see ConstantPoolParser#createPatch()
++ */
++public class ConstantPoolPatch {
++ final ConstantPoolParser outer;
++ final Object[] patchArray;
++
++ ConstantPoolPatch(ConstantPoolParser outer) {
++ this.outer = outer;
++ this.patchArray = new Object[outer.getLength()];
++ }
++
++ /** Create a {@link ConstantPoolParser} and
++ * a {@link ConstantPoolPatch} in one step.
++ * Equivalent to {@code new ConstantPoolParser(classFile).createPatch()}.
++ *
++ * @param classFile an array of bytes containing a class.
++ * @see #ConstantPoolParser(Class)
++ */
++ public ConstantPoolPatch(byte[] classFile) throws InvalidConstantPoolFormatException {
++ this(new ConstantPoolParser(classFile));
++ }
++
++ /** Create a {@link ConstantPoolParser} and
++ * a {@link ConstantPoolPatch} in one step.
++ * Equivalent to {@code new ConstantPoolParser(templateClass).createPatch()}.
++ *
++ * @param templateClass the class to parse.
++ * @see #ConstantPoolParser(Class)
++ */
++ public ConstantPoolPatch(Class<?> templateClass) throws IOException, InvalidConstantPoolFormatException {
++ this(new ConstantPoolParser(templateClass));
++ }
++
++
++ /** Creates a patch from an existing patch.
++ * All changes are copied from that patch.
++ * @param patch a patch
++ *
++ * @see ConstantPoolParser#createPatch()
++ */
++ public ConstantPoolPatch(ConstantPoolPatch patch) {
++ outer = patch.outer;
++ patchArray = patch.patchArray.clone();
++ }
++
++ /** Which parser built this patch? */
++ public ConstantPoolParser getParser() {
++ return outer;
++ }
++
++ /** Report the tag at the given index in the constant pool. */
++ public byte getTag(int index) {
++ return outer.getTag(index);
++ }
++
++ /** Report the current patch at the given index of the constant pool.
++ * Null means no patch will be made.
++ * To observe the unpatched entry at the given index, use
++ * {@link #getParser()}{@code .}@link ConstantPoolParser#parse(ConstantPoolVisitor)}
++ */
++ public Object getPatch(int index) {
++ Object value = patchArray[index];
++ if (value == null) return null;
++ switch (getTag(index)) {
++ case CONSTANT_Fieldref:
++ case CONSTANT_Methodref:
++ case CONSTANT_InterfaceMethodref:
++ if (value instanceof String)
++ value = stripSemis(2, (String) value);
++ break;
++ case CONSTANT_NameAndType:
++ if (value instanceof String)
++ value = stripSemis(1, (String) value);
++ break;
++ }
++ return value;
++ }
++
++ /** Clear all patches. */
++ public void clear() {
++ Arrays.fill(patchArray, null);
++ }
++
++ /** Clear one patch. */
++ public void clear(int index) {
++ patchArray[index] = null;
++ }
++
++ /** Produce the patches as an array. */
++ public Object[] getPatches() {
++ return patchArray.clone();
++ }
++
++ /** Produce the original constant pool as an array. */
++ public Object[] getOriginalCP() throws InvalidConstantPoolFormatException {
++ return getOriginalCP(0, patchArray.length, -1);
++ }
++
++ /** Walk the constant pool, applying patches using the given map.
++ *
++ * @param utf8Map Utf8 strings to modify, if encountered
++ * @param classMap Classes (or their names) to modify, if encountered
++ * @param valueMap Constant values to modify, if encountered
++ * @param deleteUsedEntries if true, delete map entries that are used
++ */
++ public void putPatches(final Map<String,String> utf8Map,
++ final Map<String,Object> classMap,
++ final Map<Object,Object> valueMap,
++ boolean deleteUsedEntries) throws InvalidConstantPoolFormatException {
++ final HashSet<String> usedUtf8Keys;
++ final HashSet<String> usedClassKeys;
++ final HashSet<Object> usedValueKeys;
++ if (deleteUsedEntries) {
++ usedUtf8Keys = (utf8Map == null) ? null : new HashSet<String>();
++ usedClassKeys = (classMap == null) ? null : new HashSet<String>();
++ usedValueKeys = (valueMap == null) ? null : new HashSet<Object>();
++ } else {
++ usedUtf8Keys = null;
++ usedClassKeys = null;
++ usedValueKeys = null;
++ }
++
++ outer.parse(new ConstantPoolVisitor() {
++
++ @Override
++ public void visitUTF8(int index, byte tag, String utf8) {
++ putUTF8(index, utf8Map.get(utf8));
++ if (usedUtf8Keys != null) usedUtf8Keys.add(utf8);
++ }
++
++ @Override
++ public void visitConstantValue(int index, byte tag, Object value) {
++ putConstantValue(index, tag, valueMap.get(value));
++ if (usedValueKeys != null) usedValueKeys.add(value);
++ }
++
++ @Override
++ public void visitConstantString(int index, byte tag, String name, int nameIndex) {
++ if (tag == CONSTANT_Class) {
++ putConstantValue(index, tag, classMap.get(name));
++ if (usedClassKeys != null) usedClassKeys.add(name);
++ } else {
++ assert(tag == CONSTANT_String);
++ visitConstantValue(index, tag, name);
++ }
++ }
++ });
++ if (usedUtf8Keys != null) utf8Map.keySet().removeAll(usedUtf8Keys);
++ if (usedClassKeys != null) classMap.keySet().removeAll(usedClassKeys);
++ if (usedValueKeys != null) valueMap.keySet().removeAll(usedValueKeys);
++ }
++
++ Object[] getOriginalCP(final int startIndex,
++ final int endIndex,
++ final int tagMask) throws InvalidConstantPoolFormatException {
++ final Object[] cpArray = new Object[endIndex - startIndex];
++ outer.parse(new ConstantPoolVisitor() {
++
++ void show(int index, byte tag, Object value) {
++ if (index < startIndex || index >= endIndex) return;
++ if (((1 << tag) & tagMask) == 0) return;
++ cpArray[index - startIndex] = value;
++ }
++
++ @Override
++ public void visitUTF8(int index, byte tag, String utf8) {
++ show(index, tag, utf8);
++ }
++
++ @Override
++ public void visitConstantValue(int index, byte tag, Object value) {
++ assert(tag != CONSTANT_String);
++ show(index, tag, value);
++ }
++
++ @Override
++ public void visitConstantString(int index, byte tag,
++ String value, int j) {
++ show(index, tag, value);
++ }
++
++ @Override
++ public void visitMemberRef(int index, byte tag,
++ String className, String memberName,
++ String signature,
++ int j, int k) {
++ show(index, tag, new String[]{ className, memberName, signature });
++ }
++
++ @Override
++ public void visitDescriptor(int index, byte tag,
++ String memberName, String signature,
++ int j, int k) {
++ show(index, tag, new String[]{ memberName, signature });
++ }
++ });
++ return cpArray;
++ }
++
++ /** Write the head (header plus constant pool)
++ * of the patched class file to the indicated stream.
++ */
++ void writeHead(OutputStream out) throws IOException {
++ outer.writePatchedHead(out, patchArray);
++ }
++
++ /** Write the tail (everything after the constant pool)
++ * of the patched class file to the indicated stream.
++ */
++ void writeTail(OutputStream out) throws IOException {
++ outer.writeTail(out);
++ }
++
++ private void checkConstantTag(byte tag, Object value) {
++ if (value == null)
++ throw new IllegalArgumentException(
++ "invalid null constant value");
++ if (classForTag(tag) != value.getClass())
++ throw new IllegalArgumentException(
++ "invalid constant value"
++ + (tag == CONSTANT_None ? ""
++ : " for tag "+tagName(tag))
++ + " of class "+value.getClass());
++ }
++
++ private void checkTag(int index, byte putTag) {
++ byte tag = outer.tags[index];
++ if (tag != putTag)
++ throw new IllegalArgumentException(
++ "invalid put operation"
++ + " for " + tagName(putTag)
++ + " at index " + index + " found " + tagName(tag));
++ }
++
++ private void checkTagMask(int index, int tagBitMask) {
++ byte tag = outer.tags[index];
++ int tagBit = ((tag & 0x1F) == tag) ? (1 << tag) : 0;
++ if ((tagBit & tagBitMask) == 0)
++ throw new IllegalArgumentException(
++ "invalid put operation"
++ + " at index " + index + " found " + tagName(tag));
++ }
++
++ private static void checkMemberName(String memberName) {
++ if (memberName.indexOf(';') >= 0)
++ throw new IllegalArgumentException("memberName " + memberName + " contains a ';'");
++ }
++
++ /** Set the entry of the constant pool indexed by index to
++ * a new string.
++ *
++ * @param index an index to a constant pool entry containing a
++ * {@link ConstantPoolVisitor#CONSTANT_Utf8} value.
++ * @param utf8 a string
++ *
++ * @see ConstantPoolVisitor#visitUTF8(int, byte, String)
++ */
++ public void putUTF8(int index, String utf8) {
++ if (utf8 == null) { clear(index); return; }
++ checkTag(index, CONSTANT_Utf8);
++ patchArray[index] = utf8;
++ }
++
++ /** Set the entry of the constant pool indexed by index to
++ * a new value, depending on its dynamic type.
++ *
++ * @param index an index to a constant pool entry containing a
++ * one of the following structures:
++ * {@link ConstantPoolVisitor#CONSTANT_Integer},
++ * {@link ConstantPoolVisitor#CONSTANT_Float},
++ * {@link ConstantPoolVisitor#CONSTANT_Long},
++ * {@link ConstantPoolVisitor#CONSTANT_Double},
++ * {@link ConstantPoolVisitor#CONSTANT_String}, or
++ * {@link ConstantPoolVisitor#CONSTANT_Class}
++ * @param value a boxed int, float, long or double; or a string or class object
++ * @throws IllegalArgumentException if the type of the constant does not
++ * match the constant pool entry type,
++ * as reported by {@link #getTag(int)}
++ *
++ * @see #putConstantValue(int, byte, Object)
++ * @see ConstantPoolVisitor#visitConstantValue(int, byte, Object)
++ * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int)
++ */
++ public void putConstantValue(int index, Object value) {
++ if (value == null) { clear(index); return; }
++ byte tag = tagForConstant(value.getClass());
++ checkConstantTag(tag, value);
++ checkTag(index, tag);
++ patchArray[index] = value;
++ }
++
++ /** Set the entry of the constant pool indexed by index to
++ * a new value.
++ *
++ * @param index an index to a constant pool entry matching the given tag
++ * @param tag one of the following values:
++ * {@link ConstantPoolVisitor#CONSTANT_Integer},
++ * {@link ConstantPoolVisitor#CONSTANT_Float},
++ * {@link ConstantPoolVisitor#CONSTANT_Long},
++ * {@link ConstantPoolVisitor#CONSTANT_Double},
++ * {@link ConstantPoolVisitor#CONSTANT_String}, or
++ * {@link ConstantPoolVisitor#CONSTANT_Class}
++ * @param value a boxed number, string, or class object
++ * @throws IllegalArgumentException if the type of the constant does not
++ * match the constant pool entry type, or if a class name contains
++ * '/' or ';'
++ *
++ * @see #putConstantValue(int, Object)
++ * @see ConstantPoolVisitor#visitConstantValue(int, byte, Object)
++ * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int)
++ */
++ public void putConstantValue(int index, byte tag, Object value) {
++ if (value == null) { clear(index); return; }
++ checkTag(index, tag);
++ if (tag == CONSTANT_Class && value instanceof String) {
++ checkClassName((String) value);
++ } else if (tag == CONSTANT_String) {
++ // the JVM accepts any object as a patch for a string
++ } else {
++ // make sure the incoming value is the right type
++ checkConstantTag(tag, value);
++ }
++ checkTag(index, tag);
++ patchArray[index] = value;
++ }
++
++ /** Set the entry of the constant pool indexed by index to
++ * a new {@link ConstantPoolVisitor#CONSTANT_NameAndType} value.
++ *
++ * @param index an index to a constant pool entry containing a
++ * {@link ConstantPoolVisitor#CONSTANT_NameAndType} value.
++ * @param memberName a memberName
++ * @param signature a signature
++ * @throws IllegalArgumentException if memberName contains the character ';'
++ *
++ * @see ConstantPoolVisitor#visitDescriptor(int, byte, String, String, int, int)
++ */
++ public void putDescriptor(int index, String memberName, String signature) {
++ checkTag(index, CONSTANT_NameAndType);
++ checkMemberName(memberName);
++ patchArray[index] = addSemis(memberName, signature);
++ }
++
++ /** Set the entry of the constant pool indexed by index to
++ * a new {@link ConstantPoolVisitor#CONSTANT_Fieldref},
++ * {@link ConstantPoolVisitor#CONSTANT_Methodref}, or
++ * {@link ConstantPoolVisitor#CONSTANT_InterfaceMethodref} value.
++ *
++ * @param index an index to a constant pool entry containing a member reference
++ * @param className a class name
++ * @param memberName a field or method name
++ * @param signature a field or method signature
++ * @throws IllegalArgumentException if memberName contains the character ';'
++ * or signature is not a correct signature
++ *
++ * @see ConstantPoolVisitor#visitMemberRef(int, byte, String, String, String, int, int)
++ */
++ public void putMemberRef(int index, byte tag,
++ String className, String memberName, String signature) {
++ checkTagMask(tag, CONSTANT_MemberRef_MASK);
++ checkTag(index, tag);
++ checkClassName(className);
++ checkMemberName(memberName);
++ if (signature.startsWith("(") == (tag == CONSTANT_Fieldref))
++ throw new IllegalArgumentException("bad signature: "+signature);
++ patchArray[index] = addSemis(className, memberName, signature);
++ }
++
++ static private final int CONSTANT_MemberRef_MASK =
++ CONSTANT_Fieldref
++ | CONSTANT_Methodref
++ | CONSTANT_InterfaceMethodref;
++
++ private static final Map<Class<?>, Byte> CONSTANT_VALUE_CLASS_TAG
++ = new IdentityHashMap<Class<?>, Byte>();
++ private static final Class[] CONSTANT_VALUE_CLASS = new Class[16];
++ static {
++ Object[][] values = {
++ {Integer.class, CONSTANT_Integer},
++ {Long.class, CONSTANT_Long},
++ {Float.class, CONSTANT_Float},
++ {Double.class, CONSTANT_Double},
++ {String.class, CONSTANT_String},
++ {Class.class, CONSTANT_Class}
++ };
++ for (Object[] value : values) {
++ Class<?> cls = (Class<?>)value[0];
++ Byte tag = (Byte) value[1];
++ CONSTANT_VALUE_CLASS_TAG.put(cls, tag);
++ CONSTANT_VALUE_CLASS[(byte)tag] = cls;
++ }
++ }
++
++ static Class<?> classForTag(byte tag) {
++ if ((tag & 0xFF) >= CONSTANT_VALUE_CLASS.length)
++ return null;
++ return CONSTANT_VALUE_CLASS[tag];
++ }
++
++ static byte tagForConstant(Class<?> cls) {
++ Byte tag = CONSTANT_VALUE_CLASS_TAG.get(cls);
++ return (tag == null) ? CONSTANT_None : (byte)tag;
++ }
++
++ private static void checkClassName(String className) {
++ if (className.indexOf('/') >= 0 || className.indexOf(';') >= 0)
++ throw new IllegalArgumentException("invalid class name " + className);
++ }
++
++ static String addSemis(String name, String... names) {
++ StringBuilder buf = new StringBuilder(name.length() * 5);
++ buf.append(name);
++ for (String name2 : names) {
++ buf.append(';').append(name2);
++ }
++ String res = buf.toString();
++ assert(stripSemis(names.length, res)[0].equals(name));
++ assert(stripSemis(names.length, res)[1].equals(names[0]));
++ assert(names.length == 1 ||
++ stripSemis(names.length, res)[2].equals(names[1]));
++ return res;
++ }
++
++ static String[] stripSemis(int count, String string) {
++ String[] res = new String[count+1];
++ int pos = 0;
++ for (int i = 0; i < count; i++) {
++ int pos2 = string.indexOf(';', pos);
++ if (pos2 < 0) pos2 = string.length(); // yuck
++ res[i] = string.substring(pos, pos2);
++ pos = pos2;
++ }
++ res[count] = string.substring(pos);
++ return res;
++ }
++
++ public String toString() {
++ StringBuilder buf = new StringBuilder(this.getClass().getName());
++ buf.append("{");
++ Object[] origCP = null;
++ for (int i = 0; i < patchArray.length; i++) {
++ if (patchArray[i] == null) continue;
++ if (origCP != null) {
++ buf.append(", ");
++ } else {
++ try {
++ origCP = getOriginalCP();
++ } catch (InvalidConstantPoolFormatException ee) {
++ origCP = new Object[0];
++ }
++ }
++ Object orig = (i < origCP.length) ? origCP[i] : "?";
++ buf.append(orig).append("=").append(patchArray[i]);
++ }
++ buf.append("}");
++ return buf.toString();
++ }
++}
+diff --git a/src/share/classes/sun/dyn/anon/ConstantPoolVisitor.java b/src/share/classes/sun/dyn/anon/ConstantPoolVisitor.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/sun/dyn/anon/ConstantPoolVisitor.java
+@@ -0,0 +1,192 @@
++/*
++ * Copyright 2008-2009 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 sun.dyn.anon;
++
++/**
++ * A visitor called by {@link ConstantPoolParser#parse(ConstantPoolVisitor)}
++ * when a constant pool entry is parsed.
++ * <p>
++ * A visit* method is called when a constant pool entry is parsed.
++ * The first argument is always the constant pool index.
++ * The second argument is always the constant pool tag,
++ * even for methods like {@link #visitUTF8(int, byte, String)} which only apply to one tag.
++ * String arguments refer to Utf8 or NameAndType entries declared elsewhere,
++ * and are always accompanied by the indexes of those entries.
++ * <p>
++ * The order of the calls to the visit* methods is not necessarily related
++ * to the order of the entries in the constant pool.
++ * If one entry has a reference to another entry, the latter (lower-level)
++ * entry will be visited first.
++ * <p>
++ * The following table shows the relation between constant pool entry
++ * types and the corresponding visit* methods:
++ *
++ * <table border=1 cellpadding=5 summary="constant pool visitor methods">
++ * <tr><th>Tag(s)</th><th>Method</th></tr>
++ * <tr>
++ * <td>{@link #CONSTANT_Utf8}</td>
++ * <td>{@link #visitUTF8(int, byte, String)}</td>
++ * </tr><tr>
++ * <td>{@link #CONSTANT_Integer}, {@link #CONSTANT_Float},
++ * {@link #CONSTANT_Long}, {@link #CONSTANT_Double}</td>
++ * <td>{@link #visitConstantValue(int, byte, Object)}</td>
++ * </tr><tr>
++ * <td>{@link #CONSTANT_String}, {@link #CONSTANT_Class}</td>
++ * <td>{@link #visitConstantString(int, byte, String, int)}</td>
++ * </tr><tr>
++ * <td>{@link #CONSTANT_NameAndType}</td>
++ * <td>{@link #visitDescriptor(int, byte, String, String, int, int)}</td>
++ * </tr><tr>
++ * <td>{@link #CONSTANT_Fieldref},
++ * {@link #CONSTANT_Methodref},
++ * {@link #CONSTANT_InterfaceMethodref}</td>
++ * <td>{@link #visitMemberRef(int, byte, String, String, String, int, int)}</td>
++ * </tr>
++ * </table>
++ *
++ * @see ConstantPoolPatch
++ * @author Remi Forax
++ * @author jrose
++ */
++public class ConstantPoolVisitor {
++ /** Called each time an UTF8 constant pool entry is found.
++ * @param index the constant pool index
++ * @param tag always {@link #CONSTANT_Utf8}
++ * @param utf8 string encoded in modified UTF-8 format passed as a {@code String}
++ *
++ * @see ConstantPoolPatch#putUTF8(int, String)
++ */
++ public void visitUTF8(int index, byte tag, String utf8) {
++ // do nothing
++ }
++
++ /** Called for each constant pool entry that encodes an integer,
++ * a float, a long, or a double.
++ * Constant strings and classes are not managed by this method but
++ * by {@link #visitConstantString(int, byte, String, int)}.
++ *
++ * @param index the constant pool index
++ * @param tag one of {@link #CONSTANT_Integer},
++ * {@link #CONSTANT_Float},
++ * {@link #CONSTANT_Long},
++ * or {@link #CONSTANT_Double}
++ * @param value encoded value
++ *
++ * @see ConstantPoolPatch#putConstantValue(int, Object)
++ */
++ public void visitConstantValue(int index, byte tag, Object value) {
++ // do nothing
++ }
++
++ /** Called for each constant pool entry that encodes a string or a class.
++ * @param index the constant pool index
++ * @param tag one of {@link #CONSTANT_String},
++ * {@link #CONSTANT_Class},
++ * @param name string body or class name (using dot separator)
++ * @param nameIndex the index of the Utf8 string for the name
++ *
++ * @see ConstantPoolPatch#putConstantValue(int, byte, Object)
++ */
++ public void visitConstantString(int index, byte tag,
++ String name, int nameIndex) {
++ // do nothing
++ }
++
++ /** Called for each constant pool entry that encodes a name and type.
++ * @param index the constant pool index
++ * @param tag always {@link #CONSTANT_NameAndType}
++ * @param memberName a field or method name
++ * @param signature the member signature
++ * @param memberNameIndex index of the Utf8 string for the member name
++ * @param signatureIndex index of the Utf8 string for the signature
++ *
++ * @see ConstantPoolPatch#putDescriptor(int, String, String)
++ */
++ public void visitDescriptor(int index, byte tag,
++ String memberName, String signature,
++ int memberNameIndex, int signatureIndex) {
++ // do nothing
++ }
++
++ /** Called for each constant pool entry that encodes a field or method.
++ * @param index the constant pool index
++ * @param tag one of {@link #CONSTANT_Fieldref},
++ * or {@link #CONSTANT_Methodref},
++ * or {@link #CONSTANT_InterfaceMethodref}
++ * @param className the class name (using dot separator)
++ * @param memberName name of the field or method
++ * @param signature the field or method signature
++ * @param classNameIndex index of the Utf8 string for the class name
++ * @param descriptorIndex index of the NameAndType descriptor constant
++ *
++ * @see ConstantPoolPatch#putMemberRef(int, byte, String, String, String)
++ */
++ public void visitMemberRef(int index, byte tag,
++ String className, String memberName, String signature,
++ int classNameIndex, int descriptorIndex) {
++ // do nothing
++ }
++
++ public static final byte
++ CONSTANT_None = 0,
++ CONSTANT_Utf8 = 1,
++ //CONSTANT_Unicode = 2, /* unused */
++ CONSTANT_Integer = 3,
++ CONSTANT_Float = 4,
++ CONSTANT_Long = 5,
++ CONSTANT_Double = 6,
++ CONSTANT_Class = 7,
++ CONSTANT_String = 8,
++ CONSTANT_Fieldref = 9,
++ CONSTANT_Methodref = 10,
++ CONSTANT_InterfaceMethodref = 11,
++ CONSTANT_NameAndType = 12;
++
++ private static String[] TAG_NAMES = {
++ "Empty",
++ "Utf8",
++ null, //"Unicode",
++ "Integer",
++ "Float",
++ "Long",
++ "Double",
++ "Class",
++ "String",
++ "Fieldref",
++ "Methodref",
++ "InterfaceMethodref",
++ "NameAndType"
++ };
++
++ public static String tagName(byte tag) {
++ String name = null;
++ if ((tag & 0xFF) < TAG_NAMES.length)
++ name = TAG_NAMES[tag];
++ if (name == null)
++ name = "Unknown#"+(tag&0xFF);
++ return name;
++ }
++}
+diff --git a/src/share/classes/sun/dyn/anon/InvalidConstantPoolFormatException.java b/src/share/classes/sun/dyn/anon/InvalidConstantPoolFormatException.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/sun/dyn/anon/InvalidConstantPoolFormatException.java
+@@ -0,0 +1,45 @@
++/*
++ * Copyright 2008-2009 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 sun.dyn.anon;
++
++/** Exception used when there is an error in the constant pool
++ * format.
++ */
++public class InvalidConstantPoolFormatException extends Exception {
++ private static final long serialVersionUID=-6103888330523770949L;
++
++ public InvalidConstantPoolFormatException(String message,Throwable cause) {
++ super(message,cause);
++ }
++
++ public InvalidConstantPoolFormatException(String message) {
++ super(message);
++ }
++
++ public InvalidConstantPoolFormatException(Throwable cause) {
++ super(cause);
++ }
++}
+diff --git a/src/share/classes/sun/misc/Unsafe.java b/src/share/classes/sun/misc/Unsafe.java
+--- a/src/share/classes/sun/misc/Unsafe.java
++++ b/src/share/classes/sun/misc/Unsafe.java
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
++ * Copyright 2000-2009 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
+@@ -811,6 +811,25 @@
+
+ public native Class defineClass(String name, byte[] b, int off, int len);
+
++ /**
++ * Define a class but do not make it known to the class loader or system dictionary.
++ * <p>
++ * For each CP entry, the corresponding CP patch must either be null or have
++ * the a format that matches its tag:
++ * <ul>
++ * <li>Integer, Long, Float, Double: the corresponding wrapper object type from java.lang
++ * <li>Utf8: a string (must have suitable syntax if used as signature or name)
++ * <li>Class: any java.lang.Class object
++ * <li>String: any object (not just a java.lang.String)
++ * <li>InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments
++ * </ul>
++ * @params hostClass context for linkage, access control, protection domain, and class loader
++ * @params data bytes of a class file
++ * @params cpPatches where non-null entries exist, they replace corresponding CP entries in data
++ */
++ public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches);
++
++
+ /** Allocate an instance but do not run any constructor.
+ Initializes the class if it has not yet been. */
+ public native Object allocateInstance(Class cls)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/indy-6829144.patch Wed May 06 19:45:31 2009 -0700
@@ -0,0 +1,1509 @@
+6829144: JSR 292 JVM features need a provisional Java API
+Summary: JSR 292 RI for invokedynamic, preview edition
+
+diff --git a/src/share/classes/java/dyn/CallSite.java b/src/share/classes/java/dyn/CallSite.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/CallSite.java
+@@ -0,0 +1,201 @@
++/*
++ * Copyright 2008-2009 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;
++
++import sun.dyn.util.BytecodeName;
++
++/**
++ * An <code>invokedynamic</code> call site, as reified to the bootstrap method.
++ * Every instance of a call site corresponds to a distinct instance
++ * of the <code>invokedynamic</code> instruction.
++ * Call sites have state, one reference word, called the <code>target</code>,
++ * and typed as a {@link MethodHandle}. When this state is null (as it is
++ * initially) the call site is in the unlinked state. Otherwise, it is said
++ * to be linked to its target.
++ * <p>
++ * When an unlinked call site is executed, a bootstrap routine is called
++ * to finish the execution of the call site, and optionally to link
++ * the call site.
++ * <p>
++ * @author John Rose, JSR 292 EG
++ */
++public class CallSite {
++ // Fields used only by the JVM. Do not use or change.
++ private Object vmmethod;
++ int callerMID, callerBCI; // supplied by the JVM
++
++ MethodHandle target;
++ final Object caller; // usually a class
++ final String name;
++ final MethodType type;
++
++ public CallSite(Object caller, String name, MethodType type) {
++ this.caller = caller;
++ this.name = name;
++ this.type = 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());
++ }
++
++ /**
++ * Just after a call site is created by a bootstrap method handle,
++ * if the target has not been initialized by the factory method itself,
++ * the method {@code initialTarget} is called to produce an initial
++ * non-null target. (Live call sites must never have null targets.)
++ * <p>
++ * If the bootstrap method itself does not initialize the call site,
++ * this method must be overridden, because it just raises an
++ * {@code InvokeDynamicBootstrapError}.
++ */
++ protected MethodHandle initialTarget() {
++ throw new InvokeDynamicBootstrapError("target must be initialized before call site is linked: "+this);
++ }
++
++ /**
++ * 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.
++ * 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}.
++ * <p>
++ * The interactions of {@code getTarget} with memory are the same
++ * as of a read from an ordinary variable, such as an array element or a
++ * non-volatile, non-final field.
++ * <p>
++ * In particular, the current thread may choose to reuse the result
++ * of a previous read of the target from memory, and may fail to see
++ * a recent update to the target by another thread.
++ * @return the current linkage state of the call site
++ * @see #setTarget
++ */
++ public MethodHandle getTarget() {
++ return target;
++ }
++
++ /**
++ * Link or relink the call site, by setting its target method.
++ * <p>
++ * The interactions of {@code setTarget} with memory are the same
++ * as of a write to an ordinary variable, such as an array element or a
++ * non-volatile, non-final field.
++ * <p>
++ * In particular, unrelated threads may fail to see the updated target
++ * until they perform a read from memory.
++ * Stronger guarantees can be created by putting appropriate operations
++ * into the bootstrap method and/or the target methods used
++ * at any given call site.
++ * @param target the new target, or null if it is to be unlinked
++ * @throws WrongMethodTypeException if the new target is not null
++ * and has a method type that differs from the call site's {@link #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
++ */
++ public Class<?> callerClass() {
++ return (Class) caller;
++ }
++
++ /**
++ * Report the method name specified in the {@code invokedynamic} instruction.
++ * This is immutable static context.
++ * <p>
++ * Note that the name is a JVM bytecode name, and as such can be any
++ * non-empty string, as long as it does not contain certain "dangerous"
++ * characters such as slash {@code '/'} and dot {@code '.'}.
++ * See the Java Virtual Machine specification for more details.
++ * <p>
++ * Application such as a language runtimes may need to encode
++ * arbitrary program element names and other configuration information
++ * into the name. A standard convention for doing this is
++ * <a href="http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm">specified here</a>.
++ * @return method name specified by the call site
++ */
++ public String name() {
++ return name;
++ }
++
++ /**
++ * Report the method name specified in the {@code invokedynamic} instruction,
++ * as a series of components, individually demangled according to
++ * the standard convention
++ * <a href="http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm">specified here</a>.
++ * <p>
++ * Non-empty runs of characters between dangerous characters are demangled.
++ * Each component is either a completely arbitrary demangled string,
++ * or else a character constant for a punctuation character, typically ':'.
++ * (In principle, the character can be any dangerous character that the
++ * JVM lets through in a method name, such as '$' or ']'.
++ * Runtime implementors are encouraged to use colon ':' for building
++ * structured names.)
++ * <p>
++ * In the common case where the name contains no dangerous characters,
++ * the result is an array whose only element array is the demangled
++ * name at the call site. Such a demangled name can be any sequence
++ * of any number of any unicode characters.
++ * @return method name components specified by the call site
++ */
++ public Object[] nameComponents() {
++ return BytecodeName.parseBytecodeName(name);
++ }
++
++ /**
++ * Report the resolved result and parameter types of this call site,
++ * which are derived from its bytecode-level invocation descriptor.
++ * The types are packaged into a {@link MethodType}.
++ * Any linked target of this call site must be exactly this method type.
++ * This is immutable static context.
++ * @return method type specified by the call site
++ */
++ public MethodType type() {
++ return type;
++ }
++
++ @Override
++ public String toString() {
++ return "CallSite#"+hashCode()+"["+name+type+" => "+target+"]";
++ }
++}
+diff --git a/src/share/classes/java/dyn/InvokeDynamic.java b/src/share/classes/java/dyn/InvokeDynamic.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/InvokeDynamic.java
+@@ -0,0 +1,39 @@
++/*
++ * Copyright 2008-2009 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;
++
++/**
++ * Syntactic marker interface to request javac to emit an {@code invokedynamic} instruction.
++ * <p>
++ * This type has no particular meaning as a class or interface supertype, and can never be instantiated.
++ * Logically, it denotes a source of all dynamically typed methods.
++ * @author John Rose, JSR 292 EG
++ */
++public final class InvokeDynamic {
++ private InvokeDynamic() { throw new InternalError(); } // do not instantiate
++
++ // no statically defined static methods
++}
+diff --git a/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java
+@@ -0,0 +1,55 @@
++/*
++ * Copyright 2008-2009 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;
++
++/**
++ * Thrown to indicate that an {@code invokedynamic} instruction has
++ * failed to find its bootstrap method, or the bootstrap method has
++ * failed to provide a call site with a non-null target.
++ * <p>
++ * The boostrap method must have been declared during a class's initialization
++ * by a call to {@link Linkage#registerBootstrapMethod}.
++ *
++ * @author John Rose, JSR 292 EG
++ */
++public class InvokeDynamicBootstrapError extends LinkageError {
++ /**
++ * Constructs a {@code InvokeDynamicBootstrapError} with no detail message.
++ */
++ public InvokeDynamicBootstrapError() {
++ super();
++ }
++
++ /**
++ * Constructs a {@code InvokeDynamicBootstrapError} with the specified
++ * detail message.
++ *
++ * @param s the detail message.
++ */
++ public InvokeDynamicBootstrapError(String s) {
++ super(s);
++ }
++}
+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,199 @@
++/*
++ * Copyright 2008-2009 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;
++
++import java.util.WeakHashMap;
++import sun.reflect.Reflection;
++import static sun.dyn.util.VerifyAccess.checkBootstrapPrivilege;
++
++/**
++ * Static methods which control the linkage of invokedynamic call sites.
++ * @author John Rose, JSR 292 EG
++ */
++public class Linkage {
++ private Linkage() {} // do not instantiate
++
++ /**
++ * Register a bootstrap method for use for a given caller class.
++ * The method handle must be of a type equivalent to {@link Linkage#makeCallSite}.
++ * <p>
++ * The operation will fail with an exception if any of the following conditions hold:
++ * <ul>
++ * <li>The caller of this method is in a different package than the {@code callerClass},
++ * and there is a security manager, and its {@code checkPermission} call throws
++ * when passed {@link LinkagePermission}("registerBootstrapMethod",callerClass).
++ * <li>The given class already has a bootstrap method, either from an embedded
++ * {@code BootstrapInvokeDynamic} classfile attribute, or from a previous
++ * 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.
++ * </ul>
++ * Because of these rules, a class may install its own bootstrap method in
++ * a static initializer.
++ */
++ public static
++ void registerBootstrapMethod(Class callerClass, MethodHandle mh) {
++ Class callc = Reflection.getCallerClass(2);
++ checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod");
++ checkBSM(mh);
++ synchronized (bootstrapMethods) {
++ if (bootstrapMethods.containsKey(callerClass))
++ throw new IllegalStateException("bootstrap method already declared in "+callerClass);
++ bootstrapMethods.put(callerClass, mh);
++ }
++ }
++
++ static void checkBSM(MethodHandle mh) {
++ if (mh == null) throw new IllegalArgumentException("null bootstrap method");
++ if (mh.type() == OLD_BOOTSTRAP_METHOD_TYPE) // FIXME: delete at EDR/PFD
++ throw new WrongMethodTypeException("bootstrap method must be a CallSite factory");
++ if (mh.type() != BOOTSTRAP_METHOD_TYPE)
++ throw new WrongMethodTypeException(mh.toString());
++ }
++
++ /**
++ * Simplified version of registerBootstrapMethod for self-registration,
++ * to be called from a static initializer.
++ * Finds a static method of type (CallSite, Object[]) -> Object in the
++ * given class, and installs it on the caller.
++ * @throws IllegalArgumentException if there is no such method
++ */
++ public static
++ void registerBootstrapMethod(Class<?> runtime, String name) {
++ Class callc = Reflection.getCallerClass(2);
++ MethodHandle bootstrapMethod =
++ MethodHandles.findStaticFrom(callc, runtime, name, BOOTSTRAP_METHOD_TYPE);
++ // FIXME: exception processing wrong here
++ checkBSM(bootstrapMethod);
++ Linkage.registerBootstrapMethod(callc, bootstrapMethod);
++ }
++
++ /**
++ * Simplified version of registerBootstrapMethod for self-registration,
++ * to be called from a static initializer.
++ * Finds a static method of type (CallSite, Object[]) -> Object in the
++ * caller's class, and installs it on the caller.
++ * @throws IllegalArgumentException if there is no such method
++ */
++ public static
++ void registerBootstrapMethod(String name) {
++ Class callc = Reflection.getCallerClass(2);
++ MethodHandle bootstrapMethod =
++ MethodHandles.findStaticFrom(callc, callc, name, BOOTSTRAP_METHOD_TYPE);
++ // FIXME: exception processing wrong here
++ checkBSM(bootstrapMethod);
++ Linkage.registerBootstrapMethod(callc, bootstrapMethod);
++ }
++
++ /**
++ * 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
++ * point into its class.
++ */
++ public static
++ MethodHandle getBootstrapMethod(Class callerClass) {
++ Class callc = Reflection.getCallerClass(2);
++ checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod");
++ synchronized (bootstrapMethods) {
++ return bootstrapMethods.get(callerClass);
++ }
++ }
++
++ /** The type of any bootstrap method is a three-argument method
++ * {@code (Class<?>, String, MethodType)} returning a {@code CallSite}.
++ */
++ public static final MethodType BOOTSTRAP_METHOD_TYPE
++ = MethodType.make(CallSite.class,
++ Class.class, String.class, MethodType.class);
++
++ private static final MethodType OLD_BOOTSTRAP_METHOD_TYPE
++ = MethodType.make(Object.class,
++ CallSite.class, Object[].class);
++
++ private static final WeakHashMap<Class, MethodHandle> bootstrapMethods =
++ new WeakHashMap<Class, MethodHandle>();
++
++ /**
++ * Invalidate all <code>invokedynamic</code> call sites everywhere.
++ * <p>
++ * When this method returns, every <code>invokedynamic</code> instruction
++ * will invoke its bootstrap method on next call.
++ * <p>
++ * It is unspecified whether call sites already known to the Java
++ * code will continue to be associated with <code>invokedynamic</code>
++ * instructions. If any call site is still so associated, its
++ * {@link CallSite#getTarget()} method is guaranteed to return null
++ * the invalidation operation completes.
++ * <p>
++ * Invalidation operations are likely to be slow. Use them sparingly.
++ */
++ public static
++ Object invalidateAll() {
++ SecurityManager security = System.getSecurityManager();
++ if (security != null) {
++ security.checkPermission(new LinkagePermission("invalidateAll"));
++ }
++ throw new UnsupportedOperationException("NYI");
++ }
++
++ /**
++ * Invalidate all <code>invokedynamic</code> call sites associated
++ * with the given class.
++ * (These are exactly those sites which report the given class
++ * via the {@link CallSite#callerClass()} method.)
++ * <p>
++ * When this method returns, every matching <code>invokedynamic</code>
++ * instruction will invoke its bootstrap method on next call.
++ * <p>
++ * For additional semantics of call site invalidation,
++ * see {@link #invalidateAll()}.
++ */
++ public static
++ Object invalidateCallerClass(Class<?> callerClass) {
++ SecurityManager security = System.getSecurityManager();
++ if (security != null) {
++ security.checkPermission(new LinkagePermission("invalidateAll", callerClass));
++ }
++ throw new UnsupportedOperationException("NYI");
++ }
++
++ private static Object doNotBootstrap(CallSite site, Object... arguments) {
++ throw new UnsupportedOperationException("call site must not have null target: "+site);
++ }
++
++ private static final MethodHandle DO_NOT_BOOTSTRAP =
++ MethodHandles.Lookup.IMPL_LOOKUP.findStatic(Linkage.class, "doNotBootstrap",
++ OLD_BOOTSTRAP_METHOD_TYPE);
++
++ // Up-call from the JVM. Obsolete. FIXME: Delete from VM then from here.
++ static
++ MethodHandle findBootstrapMethod(Class callerClass, Class searchBootstrapClass) {
++ return DO_NOT_BOOTSTRAP;
++ }
++}
+diff --git a/src/share/classes/java/dyn/LinkagePermission.java b/src/share/classes/java/dyn/LinkagePermission.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/LinkagePermission.java
+@@ -0,0 +1,111 @@
++/*
++ * Copyright 2008-2009 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;
++
++import java.security.*;
++import java.util.Enumeration;
++import java.util.Hashtable;
++import java.util.StringTokenizer;
++
++/**
++ * This class is for runtime permissions. A RuntimePermission
++ * contains a name (also referred to as a "target name") but
++ * no actions list; you either have the named permission
++ * or you don't.
++ *
++ * <P>
++ * The target name is the name of the runtime permission (see below). The
++ * naming convention follows the hierarchical property naming convention.
++ * Also, an asterisk
++ * may appear at the end of the name, following a ".", or by itself, to
++ * signify a wildcard match. For example: "loadLibrary.*" or "*" is valid,
++ * "*loadLibrary" or "a*b" is not valid.
++ * <P>
++ * The following table lists all the possible RuntimePermission target names,
++ * and for each provides a description of what the permission allows
++ * and a discussion of the risks of granting code the permission.
++ * <P>
++ *
++ * <table border=1 cellpadding=5 summary="permission target name,
++ * what the target allows,and associated risks">
++ * <tr>
++ * <th>Permission Target Name</th>
++ * <th>What the Permission Allows</th>
++ * <th>Risks of Allowing this Permission</th>
++ * </tr>
++ *
++ * <tr>
++ * <td>registerBootstrapMethod.{class name}</td>
++ * <td>Specifying a bootstrap method for invokedynamic, within a class of the given name</td>
++ * <td>An attacker could attempt to attach a bootstrap method to a class which
++ * has just been loaded, thus gaining control of its invokedynamic calls.</td>
++ * </tr>
++ *
++ * <tr>
++ * <td>invalidateAll</td>
++ * <td>Force the relinking of invokedynamic call sites everywhere.</td>
++ * <td>This could allow an attacker to slow down the system, or perhaps surface timing bugs in a dynamic language implementations, by forcing redundant relinking operations.</td>
++ * </tr>
++ *
++ *
++ * <tr>
++ * <td>invalidateCallerClass.{class name}</td>
++ * <td>Force the relinking of invokedynamic call sites in the given class.</td>
++ * <td>See {@code invalidateAll}.</td>
++ * </tr>
++ * </table>
++ *
++ * @see java.security.BasicPermission
++ * @see java.lang.SecurityManager
++ *
++ * @author John Rose, JSR 292 EG
++ */
++
++public final class LinkagePermission extends BasicPermission {
++ /**
++ * Create a new LinkagePermission with the given name.
++ * The name is the symbolic name of the LinkagePermission, such as
++ * "registerBootstrapMethod", "invalidateClass.*", etc. An asterisk
++ * may appear at the end of the name, following a ".", or by itself, to
++ * signify a wildcard match.
++ *
++ * @param name the name of the LinkagePermission
++ */
++ public LinkagePermission(String name) {
++ super(name);
++ }
++
++ /**
++ * Create a new LinkagePermission with the given name on the given class.
++ * Equivalent to {@code LinkagePermission(name+"."+clazz.getName())}.
++ *
++ * @param name the name of the LinkagePermission
++ * @param clazz the class affected by the permission
++ */
++ public LinkagePermission(String name, Class<?> clazz) {
++ super(name + "." + clazz.getName());
++ }
++}
+diff --git a/src/share/classes/sun/dyn/CallSiteImpl.java b/src/share/classes/sun/dyn/CallSiteImpl.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/sun/dyn/CallSiteImpl.java
+@@ -0,0 +1,70 @@
++/*
++ * Copyright 2008-2009 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 sun.dyn;
++
++import java.dyn.*;
++
++/**
++ * The CallSite privately created by the JVM at every invokedynamic instruction.
++ * @author jrose
++ */
++class CallSiteImpl extends CallSite {
++ // Fields used only by the JVM. Do not use or change.
++ private Object vmmethod;
++
++ // Values supplied by the JVM:
++ int callerMID, callerBCI;
++
++ private CallSiteImpl(Class<?> caller, String name, MethodType type) {
++ super(caller, name, type);
++ }
++
++ @Override
++ public void setTarget(MethodHandle mh) {
++ checkTarget(mh);
++ if (MethodHandleNatives.JVM_SUPPORT)
++ MethodHandleNatives.linkCallSite(this, (MethodHandle) mh);
++ else
++ super.setTarget(mh);
++ }
++
++ 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));
++
++ // this is the up-call from the JVM:
++ static CallSite makeSite(Class<?> caller, String name, MethodType type,
++ int callerMID, int callerBCI) {
++ 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);
++ 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);
++ return site;
++ }
++}
+diff --git a/src/share/classes/sun/dyn/MethodHandleNatives.java b/src/share/classes/sun/dyn/MethodHandleNatives.java
+--- a/src/share/classes/sun/dyn/MethodHandleNatives.java
++++ b/src/share/classes/sun/dyn/MethodHandleNatives.java
+@@ -67,6 +67,9 @@
+ /** Initialize a method type, once per form. */
+ 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);
++
+ /** Fetch the vmtarget field.
+ * It will be sanitized as necessary to avoid exposing non-Java references.
+ * This routine is for debugging and reflection.
+diff --git a/src/share/classes/sun/dyn/empty/Empty.java b/src/share/classes/sun/dyn/empty/Empty.java
+--- a/src/share/classes/sun/dyn/empty/Empty.java
++++ b/src/share/classes/sun/dyn/empty/Empty.java
+@@ -1,4 +1,3 @@
+-
+ /*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+diff --git a/src/share/classes/sun/dyn/util/BytecodeName.java b/src/share/classes/sun/dyn/util/BytecodeName.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/sun/dyn/util/BytecodeName.java
+@@ -0,0 +1,711 @@
++/*
++ * Copyright 2007-2009 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 sun.dyn.util;
++
++/**
++ * Utility routines for dealing with bytecode-level names.
++ * Includes universal mangling rules for the JVM.
++ *
++ * <h3>Avoiding Dangerous Characters </h3>
++ *
++ * <p>
++ * The JVM defines a very small set of characters which are illegal
++ * in name spellings. We will slightly extend and regularize this set
++ * into a group of <cite>dangerous characters</cite>.
++ * These characters will then be replaced, in mangled names, by escape sequences.
++ * In addition, accidental escape sequences must be further escaped.
++ * Finally, a special prefix will be applied if and only if
++ * the mangling would otherwise fail to begin with the escape character.
++ * This happens to cover the corner case of the null string,
++ * and also clearly marks symbols which need demangling.
++ * </p>
++ * <p>
++ * Dangerous characters are the union of all characters forbidden
++ * or otherwise restricted by the JVM specification,
++ * plus their mates, if they are brackets
++ * (<code><big><b>[</b></big></code> and <code><big><b>]</b></big></code>,
++ * <code><big><b><</b></big></code> and <code><big><b>></b></big></code>),
++ * plus, arbitrarily, the colon character <code><big><b>:</b></big></code>.
++ * There is no distinction between type, method, and field names.
++ * This makes it easier to convert between mangled names of different
++ * types, since they do not need to be decoded (demangled).
++ * </p>
++ * <p>
++ * The escape character is backslash <code><big><b>\</b></big></code>
++ * (also known as reverse solidus).
++ * This character is, until now, unheard of in bytecode names,
++ * but traditional in the proposed role.
++ *
++ * </p>
++ * <h3> Replacement Characters </h3>
++ *
++ *
++ * <p>
++ * Every escape sequence is two characters
++ * (in fact, two UTF8 bytes) beginning with
++ * the escape character and followed by a
++ * <cite>replacement character</cite>.
++ * (Since the replacement character is never a backslash,
++ * iterated manglings do not double in size.)
++ * </p>
++ * <p>
++ * Each dangerous character has some rough visual similarity
++ * to its corresponding replacement character.
++ * This makes mangled symbols easier to recognize by sight.
++ * </p>
++ * <p>
++ * The dangerous characters are
++ * <code><big><b>/</b></big></code> (forward slash, used to delimit package components),
++ * <code><big><b>.</b></big></code> (dot, also a package delimiter),
++ * <code><big><b>;</b></big></code> (semicolon, used in signatures),
++ * <code><big><b>$</b></big></code> (dollar, used in inner classes and synthetic members),
++ * <code><big><b><</b></big></code> (left angle),
++ * <code><big><b>></b></big></code> (right angle),
++ * <code><big><b>[</b></big></code> (left square bracket, used in array types),
++ * <code><big><b>]</b></big></code> (right square bracket, reserved in this scheme for language use),
++ * and <code><big><b>:</b></big></code> (colon, reserved in this scheme for language use).
++ * Their replacements are, respectively,
++ * <code><big><b>|</b></big></code> (vertical bar),
++ * <code><big><b>,</b></big></code> (comma),
++ * <code><big><b>?</b></big></code> (question mark),
++ * <code><big><b>%</b></big></code> (percent),
++ * <code><big><b>^</b></big></code> (caret),
++ * <code><big><b>_</b></big></code> (underscore), and
++ * <code><big><b>{</b></big></code> (left curly bracket),
++ * <code><big><b>}</b></big></code> (right curly bracket),
++ * <code><big><b>!</b></big></code> (exclamation mark).
++ * In addition, the replacement character for the escape character itself is
++ * <code><big><b>-</b></big></code> (hyphen),
++ * and the replacement character for the null prefix is
++ * <code><big><b>=</b></big></code> (equal sign).
++ * </p>
++ * <p>
++ * An escape character <code><big><b>\</b></big></code>
++ * followed by any of these replacement characters
++ * is an escape sequence, and there are no other escape sequences.
++ * An equal sign is only part of an escape sequence
++ * if it is the second character in the whole string, following a backslash.
++ * Two consecutive backslashes do <em>not</em> form an escape sequence.
++ * </p>
++ * <p>
++ * Each escape sequence replaces a so-called <cite>original character</cite>
++ * which is either one of the dangerous characters or the escape character.
++ * A null prefix replaces an initial null string, not a character.
++ * </p>
++ * <p>
++ * All this implies that escape sequences cannot overlap and may be
++ * determined all at once for a whole string. Note that a spelling
++ * string can contain <cite>accidental escapes</cite>, apparent escape
++ * sequences which must not be interpreted as manglings.
++ * These are disabled by replacing their leading backslash with an
++ * escape sequence (<code><big><b>\-</b></big></code>). To mangle a string, three logical steps
++ * are required, though they may be carried out in one pass:
++ * </p>
++ * <ol>
++ * <li>In each accidental escape, replace the backslash with an escape sequence
++ * (<code><big><b>\-</b></big></code>).</li>
++ * <li>Replace each dangerous character with an escape sequence
++ * (<code><big><b>\|</b></big></code> for <code><big><b>/</b></big></code>, etc.).</li>
++ * <li>If the first two steps introduced any change, <em>and</em>
++ * if the string does not already begin with a backslash, prepend a null prefix (<code><big><b>\=</b></big></code>).</li>
++ * </ol>
++ *
++ * To demangle a mangled string that begins with an escape,
++ * remove any null prefix, and then replace (in parallel)
++ * each escape sequence by its original character.
++ * <p>Spelling strings which contain accidental
++ * escapes <em>must</em> have them replaced, even if those
++ * strings do not contain dangerous characters.
++ * This restriction means that mangling a string always
++ * requires a scan of the string for escapes.
++ * But then, a scan would be required anyway,
++ * to check for dangerous characters.
++ *
++ * </p>
++ * <h3> Nice Properties </h3>
++ *
++ * <p>
++ * If a bytecode name does not contain any escape sequence,
++ * demangling is a no-op: The string demangles to itself.
++ * Such a string is called <cite>self-mangling</cite>.
++ * Almost all strings are self-mangling.
++ * In practice, to demangle almost any name “found in nature”,
++ * simply verify that it does not begin with a backslash.
++ * </p>
++ * <p>
++ * Mangling is a one-to-one function, while demangling
++ * is a many-to-one function.
++ * A mangled string is defined as <cite>validly mangled</cite> if
++ * it is in fact the unique mangling of its spelling string.
++ * Three examples of invalidly mangled strings are <code><big><b>\=foo</b></big></code>,
++ * <code><big><b>\-bar</b></big></code>, and <code><big><b>baz\!</b></big></code>, which demangle to <code><big><b>foo</b></big></code>, <code><big><b>\bar</b></big></code>, and
++ * <code><big><b>baz\!</b></big></code>, but then remangle to <code><big><b>foo</b></big></code>, <code><big><b>\bar</b></big></code>, and <code><big><b>\=baz\-!</b></big></code>.
++ * If a language back-end or runtime is using mangled names,
++ * it should never present an invalidly mangled bytecode
++ * name to the JVM. If the runtime encounters one,
++ * it should also report an error, since such an occurrence
++ * probably indicates a bug in name encoding which
++ * will lead to errors in linkage.
++ * However, this note does not propose that the JVM verifier
++ * detect invalidly mangled names.
++ * </p>
++ * <p>
++ * As a result of these rules, it is a simple matter to
++ * compute validly mangled substrings and concatenations
++ * of validly mangled strings, and (with a little care)
++ * these correspond to corresponding operations on their
++ * spelling strings.
++ * </p>
++ * <ul>
++ * <li>Any prefix of a validly mangled string is also validly mangled,
++ * although a null prefix may need to be removed.</li>
++ * <li>Any suffix of a validly mangled string is also validly mangled,
++ * although a null prefix may need to be added.</li>
++ * <li>Two validly mangled strings, when concatenated,
++ * are also validly mangled, although any null prefix
++ * must be removed from the second string,
++ * and a trailing backslash on the first string may need escaping,
++ * if it would participate in an accidental escape when followed
++ * by the first character of the second string.</li>
++ * </ul>
++ * <p>If languages that include non-Java symbol spellings use this
++ * mangling convention, they will enjoy the following advantages:
++ * </p>
++ * <ul>
++ * <li>They can interoperate via symbols they share in common.</li>
++ * <li>Low-level tools, such as backtrace printers, will have readable displays.</li>
++ * <li>Future JVM and language extensions can safely use the dangerous characters
++ * for structuring symbols, but will never interfere with valid spellings.</li>
++ * <li>Runtimes and compilers can use standard libraries for mangling and demangling.</li>
++ * <li>Occasional transliterations and name composition will be simple and regular,
++ * for classes, methods, and fields.</li>
++ * <li>Bytecode names will continue to be compact.
++ * When mangled, spellings will at most double in length, either in
++ * UTF8 or UTF16 format, and most will not change at all.</li>
++ * </ul>
++ *
++ *
++ * <h3> Suggestions for Human Readable Presentations </h3>
++ *
++ *
++ * <p>
++ * For human readable displays of symbols,
++ * it will be better to present a string-like quoted
++ * representation of the spelling, because JVM users
++ * are generally familiar with such tokens.
++ * We suggest using single or double quotes before and after
++ * mangled symbols which are not valid Java identifiers,
++ * with quotes, backslashes, and non-printing characters
++ * escaped as if for literals in the Java language.
++ * </p>
++ * <p>
++ * For example, an HTML-like spelling
++ * <code><big><b><pre></b></big></code> mangles to
++ * <code><big><b>\^pre\_</b></big></code> and could
++ * display more cleanly as
++ * <code><big><b>'<pre>'</b></big></code>,
++ * with the quotes included.
++ * Such string-like conventions are <em>not</em> suitable
++ * for mangled bytecode names, in part because
++ * dangerous characters must be eliminated, rather
++ * than just quoted. Otherwise internally structured
++ * strings like package prefixes and method signatures
++ * could not be reliably parsed.
++ * </p>
++ * <p>
++ * In such human-readable displays, invalidly mangled
++ * names should <em>not</em> be demangled and quoted,
++ * for this would be misleading. Likewise, JVM symbols
++ * which contain dangerous characters (like dots in field
++ * names or brackets in method names) should not be
++ * simply quoted. The bytecode names
++ * <code><big><b>\=phase\,1</b></big></code> and
++ * <code><big><b>phase.1</b></big></code> are distinct,
++ * and in demangled displays they should be presented as
++ * <code><big><b>'phase.1'</b></big></code> and something like
++ * <code><big><b>'phase'.1</b></big></code>, respectively.
++ * </p>
++ *
++ * @author John Rose
++ * @version 1.2, 02/06/2008
++ * @see http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm
++ */
++public class BytecodeName {
++ private BytecodeName() { } // static only class
++
++ /** Given a source name, produce the corresponding bytecode name.
++ * The source name should not be qualified, because any syntactic
++ * markers (dots, slashes, dollar signs, colons, etc.) will be mangled.
++ * @param s the source name
++ * @return a valid bytecode name which represents the source name
++ */
++ public static String toBytecodeName(String s) {
++ String bn = mangle(s);
++ assert((Object)bn == s || looksMangled(bn)) : bn;
++ assert(s.equals(toSourceName(bn))) : s;
++ return bn;
++ }
++
++ /** Given an unqualified bytecode name, produce the corresponding source name.
++ * The bytecode name must not contain dangerous characters.
++ * In particular, it must not be qualified or segmented by colon {@code ':'}.
++ * @param s the bytecode name
++ * @return the source name, which may possibly have unsafe characters
++ * @throws IllegalArgumentException if the bytecode name is not {@link #isSafeBytecodeName safe}
++ * @see #isSafeBytecodeName(java.lang.String)
++ */
++ public static String toSourceName(String s) {
++ checkSafeBytecodeName(s);
++ String sn = s;
++ if (looksMangled(s)) {
++ sn = demangle(s);
++ assert(s.equals(mangle(sn))) : s+" => "+sn+" => "+mangle(sn);
++ }
++ return sn;
++ }
++
++ /**
++ * Given a bytecode name from a classfile, separate it into
++ * components delimited by dangerous characters.
++ * Each resulting array element will be either a dangerous character,
++ * or else a safe bytecode name.
++ * (The safe name might possibly be mangled to hide further dangerous characters.)
++ * For example, the qualified class name {@code java/lang/String}
++ * will be parsed into the array {@code {"java", '/', "lang", '/', "String"}}.
++ * The name {@code <init>} will be parsed into { '<', "init", '>'}}
++ * The name {@code foo/bar$:baz} will be parsed into
++ * {@code {"foo", '/', "bar", '$', ':', "baz"}}.
++ */
++ public static Object[] parseBytecodeName(String s) {
++ int slen = s.length();
++ Object[] res = null;
++ for (int pass = 0; pass <= 1; pass++) {
++ int fillp = 0;
++ int lasti = 0;
++ for (int i = 0; i <= slen; i++) {
++ int whichDC = -1;
++ if (i < slen) {
++ whichDC = DANGEROUS_CHARS.indexOf(s.charAt(i));
++ if (whichDC < DANGEROUS_CHAR_FIRST_INDEX) continue;
++ }
++ // got to end of string or next dangerous char
++ if (lasti < i) {
++ // normal component
++ if (pass != 0)
++ res[fillp] = s.substring(lasti, i);
++ fillp++;
++ lasti = i+1;
++ }
++ if (whichDC >= DANGEROUS_CHAR_FIRST_INDEX) {
++ if (pass != 0)
++ res[fillp] = DANGEROUS_CHARS_CA[whichDC];
++ fillp++;
++ }
++ }
++ if (pass != 0) break;
++ // between passes, build the result array
++ res = new String[fillp];
++ if (fillp <= 1) {
++ if (fillp != 0) res[0] = s;
++ break;
++ }
++ }
++ return res;
++ }
++
++ /**
++ * Given a series of components, create a bytecode name for a classfile.
++ * This is the inverse of {@link #parseBytecodeName(java.lang.String)}.
++ * Each component must either be an interned one-character string of
++ * a dangerous character, or else a safe bytecode name.
++ * @param components a series of name components
++ * @return the concatenation of all components
++ * @throws IllegalArgumentException if any component contains an unsafe
++ * character, and is not an interned one-character string
++ * @throws NullPointerException if any component is null
++ */
++ public static String unparseBytecodeName(Object[] components) {
++ for (Object c : components) {
++ if (c instanceof String)
++ checkSafeBytecodeName((String) c); // may fail
++ }
++ return appendAll(components);
++ }
++ private static String appendAll(Object[] components) {
++ if (components.length <= 1) {
++ if (components.length == 1) {
++ return String.valueOf(components[0]);
++ }
++ return "";
++ }
++ int slen = 0;
++ for (Object c : components) {
++ if (c instanceof String)
++ slen += String.valueOf(c).length();
++ else
++ slen += 1;
++ }
++ StringBuilder sb = new StringBuilder(slen);
++ for (Object c : components) {
++ sb.append(c);
++ }
++ return sb.toString();
++ }
++
++ /**
++ * Given a bytecode name, produce the corresponding display name.
++ * This is the source name, plus quotes if needed.
++ * If the bytecode name contains dangerous characters,
++ * assume that they are being used as punctuation,
++ * and pass them through unchanged.
++ * @param s the original bytecode name (which may be qualified)
++ * @return a human-readable presentation
++ */
++ public static String toDisplayName(String s) {
++ Object[] components = parseBytecodeName(s);
++ for (int i = 0; i < components.length; i++) {
++ if (!(components[i] instanceof String))
++ continue;
++ String c = (String) components[i];
++ // pretty up the name by demangling it
++ String sn = toSourceName(c);
++ if ((Object)sn != c || !isJavaIdent(sn)) {
++ components[i] = quoteDisplay(sn);
++ }
++ }
++ return appendAll(components);
++ }
++ private static boolean isJavaIdent(String s) {
++ int slen = s.length();
++ if (slen == 0) return false;
++ if (!Character.isUnicodeIdentifierStart(s.charAt(0)))
++ return false;
++ for (int i = 1; i < slen; i++) {
++ if (!Character.isUnicodeIdentifierPart(s.charAt(0)))
++ return false;
++ }
++ return true;
++ }
++ private static String quoteDisplay(String s) {
++ // TO DO: Replace wierd characters in s by C-style escapes.
++ return "'"+s.replaceAll("['\\\\]", "\\\\$0")+"'";
++ }
++
++ private static void checkSafeBytecodeName(String s)
++ throws IllegalArgumentException {
++ if (!isSafeBytecodeName(s)) {
++ throw new IllegalArgumentException(s);
++ }
++ }
++
++ /**
++ * Report whether a simple name is safe as a bytecode name.
++ * Such names are acceptable in class files as class, method, and field names.
++ * Additionally, they are free of "dangerous" characters, even if those
++ * characters are legal in some (or all) names in class files.
++ * @param s the proposed bytecode name
++ * @return true if the name is non-empty and all of its characters are safe
++ */
++ public static boolean isSafeBytecodeName(String s) {
++ if (s.length() == 0) return false;
++ // check occurrences of each DANGEROUS char
++ for (char xc : DANGEROUS_CHARS_A) {
++ if (xc == ESCAPE_C) continue; // not really that dangerous
++ if (s.indexOf(xc) >= 0) return false;
++ }
++ return true;
++ }
++
++ /**
++ * Report whether a character is safe in a bytecode name.
++ * This is true of any unicode character except the following
++ * <em>dangerous characters</em>: {@code ".;:$[]<>/"}.
++ * @param s the proposed character
++ * @return true if the character is safe to use in classfiles
++ */
++ public static boolean isSafeBytecodeChar(char c) {
++ return DANGEROUS_CHARS.indexOf(c) < DANGEROUS_CHAR_FIRST_INDEX;
++ }
++
++ private static boolean looksMangled(String s) {
++ return s.charAt(0) == ESCAPE_C;
++ }
++
++ private static String mangle(String s) {
++ if (s.length() == 0)
++ return NULL_ESCAPE;
++
++ // build this lazily, when we first need an escape:
++ StringBuilder sb = null;
++
++ for (int i = 0, slen = s.length(); i < slen; i++) {
++ char c = s.charAt(i);
++
++ boolean needEscape = false;
++ if (c == ESCAPE_C) {
++ if (i+1 < slen) {
++ char c1 = s.charAt(i+1);
++ if ((i == 0 && c1 == NULL_ESCAPE_C)
++ || c1 != originalOfReplacement(c1)) {
++ // an accidental escape
++ needEscape = true;
++ }
++ }
++ } else {
++ needEscape = isDangerous(c);
++ }
++
++ if (!needEscape) {
++ if (sb != null) sb.append(c);
++ continue;
++ }
++
++ // build sb if this is the first escape
++ if (sb == null) {
++ sb = new StringBuilder(s.length()+10);
++ // mangled names must begin with a backslash:
++ if (s.charAt(0) != ESCAPE_C && i > 0)
++ sb.append(NULL_ESCAPE);
++ // append the string so far, which is unremarkable:
++ sb.append(s.substring(0, i));
++ }
++
++ // rewrite \ to \-, / to \|, etc.
++ sb.append(ESCAPE_C);
++ sb.append(replacementOf(c));
++ }
++
++ if (sb != null) return sb.toString();
++
++ return s;
++ }
++
++ private static String demangle(String s) {
++ // build this lazily, when we first meet an escape:
++ StringBuilder sb = null;
++
++ int stringStart = 0;
++ if (s.startsWith(NULL_ESCAPE))
++ stringStart = 2;
++
++ for (int i = stringStart, slen = s.length(); i < slen; i++) {
++ char c = s.charAt(i);
++
++ if (c == ESCAPE_C && i+1 < slen) {
++ // might be an escape sequence
++ char rc = s.charAt(i+1);
++ char oc = originalOfReplacement(rc);
++ if (oc != rc) {
++ // build sb if this is the first escape
++ if (sb == null) {
++ sb = new StringBuilder(s.length());
++ // append the string so far, which is unremarkable:
++ sb.append(s.substring(stringStart, i));
++ }
++ ++i; // skip both characters
++ c = oc;
++ }
++ }
++
++ if (sb != null)
++ sb.append(c);
++ }
++
++ if (sb != null) return sb.toString();
++
++ return s.substring(stringStart);
++ }
++
++ static char ESCAPE_C = '\\';
++ // empty escape sequence to avoid a null name or illegal prefix
++ static char NULL_ESCAPE_C = '=';
++ static String NULL_ESCAPE = ESCAPE_C+""+NULL_ESCAPE_C;
++
++ static final String DANGEROUS_CHARS = "\\/.;:$[]<>"; // \\ must be first
++ static final String REPLACEMENT_CHARS = "-|,?!%{}^_";
++ static final int DANGEROUS_CHAR_FIRST_INDEX = 1; // index after \\
++ static char[] DANGEROUS_CHARS_A = DANGEROUS_CHARS.toCharArray();
++ static char[] REPLACEMENT_CHARS_A = REPLACEMENT_CHARS.toCharArray();
++ static final Character[] DANGEROUS_CHARS_CA;
++ static {
++ Character[] dcca = new Character[DANGEROUS_CHARS.length()];
++ for (int i = 0; i < dcca.length; i++)
++ dcca[i] = Character.valueOf(DANGEROUS_CHARS.charAt(i));
++ DANGEROUS_CHARS_CA = dcca;
++ }
++
++ static final long[] SPECIAL_BITMAP = new long[2]; // 128 bits
++ static {
++ String SPECIAL = DANGEROUS_CHARS + REPLACEMENT_CHARS;
++ //System.out.println("SPECIAL = "+SPECIAL);
++ for (char c : SPECIAL.toCharArray()) {
++ SPECIAL_BITMAP[c >>> 6] |= 1L << c;
++ }
++ }
++ static boolean isSpecial(char c) {
++ if ((c >>> 6) < SPECIAL_BITMAP.length)
++ return ((SPECIAL_BITMAP[c >>> 6] >> c) & 1) != 0;
++ else
++ return false;
++ }
++ static char replacementOf(char c) {
++ if (!isSpecial(c)) return c;
++ int i = DANGEROUS_CHARS.indexOf(c);
++ if (i < 0) return c;
++ return REPLACEMENT_CHARS.charAt(i);
++ }
++ static char originalOfReplacement(char c) {
++ if (!isSpecial(c)) return c;
++ int i = REPLACEMENT_CHARS.indexOf(c);
++ if (i < 0) return c;
++ return DANGEROUS_CHARS.charAt(i);
++ }
++ static boolean isDangerous(char c) {
++ if (!isSpecial(c)) return false;
++ return (DANGEROUS_CHARS.indexOf(c) >= DANGEROUS_CHAR_FIRST_INDEX);
++ }
++ static int indexOfDangerousChar(String s, int from) {
++ for (int i = from, slen = s.length(); i < slen; i++) {
++ if (isDangerous(s.charAt(i)))
++ return i;
++ }
++ return -1;
++ }
++ static int lastIndexOfDangerousChar(String s, int from) {
++ for (int i = Math.min(from, s.length()-1); i >= 0; i--) {
++ if (isDangerous(s.charAt(i)))
++ return i;
++ }
++ return -1;
++ }
++
++ // test driver
++ static void main(String[] av) {
++ // If verbose is enabled, quietly check everything.
++ // Otherwise, print the output for the user to check.
++ boolean verbose = false;
++
++ int maxlen = 0;
++
++ while (av.length > 0 && av[0].startsWith("-")) {
++ String flag = av[0].intern();
++ av = java.util.Arrays.copyOfRange(av, 1, av.length); // Java 1.6 or later
++ if (flag == "-" || flag == "--") break;
++ else if (flag == "-q")
++ verbose = false;
++ else if (flag == "-v")
++ verbose = true;
++ else if (flag.startsWith("-l"))
++ maxlen = Integer.valueOf(flag.substring(2));
++ else
++ throw new Error("Illegal flag argument: "+flag);
++ }
++
++ if (maxlen == 0)
++ maxlen = (verbose ? 2 : 4);
++ if (verbose) System.out.println("Note: maxlen = "+maxlen);
++
++ switch (av.length) {
++ case 0: av = new String[] {
++ DANGEROUS_CHARS.substring(0) +
++ REPLACEMENT_CHARS.substring(0, 1) +
++ NULL_ESCAPE + "x"
++ }; // and fall through:
++ case 1:
++ char[] cv = av[0].toCharArray();
++ av = new String[cv.length];
++ int avp = 0;
++ for (char c : cv) {
++ String s = String.valueOf(c);
++ if (c == 'x') s = "foo"; // tradition...
++ av[avp++] = s;
++ }
++ }
++ if (verbose)
++ System.out.println("Note: Verbose output mode enabled. Use '-q' to suppress.");
++ Tester t = new Tester();
++ t.maxlen = maxlen;
++ t.verbose = verbose;
++ t.tokens = av;
++ t.test("", 0);
++ }
++
++ static class Tester {
++ boolean verbose;
++ int maxlen;
++ java.util.Map<String,String> map = new java.util.HashMap<String,String>();
++ String[] tokens;
++
++ void test(String stringSoFar, int tokensSoFar) {
++ test(stringSoFar);
++ if (tokensSoFar <= maxlen) {
++ for (String token : tokens) {
++ if (token.length() == 0) continue; // skip empty tokens
++ if (stringSoFar.indexOf(token) != stringSoFar.lastIndexOf(token))
++ continue; // there are already two occs. of this token
++ if (token.charAt(0) == ESCAPE_C && token.length() == 1 && maxlen < 4)
++ test(stringSoFar+token, tokensSoFar); // want lots of \'s
++ else if (tokensSoFar < maxlen)
++ test(stringSoFar+token, tokensSoFar+1);
++ }
++ }
++ }
++
++ void test(String s) {
++ // for small batches, do not test the null string
++ if (s.length() == 0 && maxlen >=1 && maxlen <= 2) return;
++ String bn = testSourceName(s);
++ if (bn == null) return;
++ if (bn == s) {
++ //if (verbose) System.out.println(s+" == id");
++ } else {
++ if (verbose) System.out.println(s+" => "+bn+" "+toDisplayName(bn));
++ String bnbn = testSourceName(bn);
++ if (bnbn == null) return;
++ if (verbose) System.out.println(bn+" => "+bnbn+" "+toDisplayName(bnbn));
++ /*
++ String bn3 = testSourceName(bnbn);
++ if (bn3 == null) return;
++ if (verbose) System.out.println(bnbn+" => "+bn3);
++ */
++ }
++ }
++
++ String testSourceName(String s) {
++ if (map.containsKey(s)) return null;
++ String bn = toBytecodeName(s);
++ map.put(s, bn);
++ String sn = toSourceName(bn);
++ if (!sn.equals(s)) {
++ String bad = (s+" => "+bn+" != "+sn);
++ if (!verbose) throw new Error("Bad mangling: "+bad);
++ System.out.println("*** "+bad);
++ return null;
++ }
++ return bn;
++ }
++ }
++}
+diff --git a/src/share/classes/sun/dyn/util/VerifyAccess.java b/src/share/classes/sun/dyn/util/VerifyAccess.java
+--- a/src/share/classes/sun/dyn/util/VerifyAccess.java
++++ b/src/share/classes/sun/dyn/util/VerifyAccess.java
+@@ -25,10 +25,9 @@
+
+ package sun.dyn.util;
+
+-import sun.dyn.MemberName;
+-import java.lang.reflect.Method;
++import java.dyn.LinkagePermission;
+ import java.lang.reflect.Modifier;
+-import java.util.Arrays;
++import sun.dyn.Access;
+
+ /**
+ * This class centralizes information about the JVM's linkage access control.
+@@ -96,7 +95,7 @@
+ public static boolean isSamePackage(Class<?> class1, Class<?> class2) {
+ if (class1 == class2)
+ return true;
+- if (class1.getClassLoader() != class2.getClassLoader())
++ if (loadersAreRelated(class1.getClassLoader(), class2.getClassLoader()))
+ return false;
+ String name1 = class1.getName(), name2 = class2.getName();
+ int dot = name1.lastIndexOf('.');
+@@ -133,4 +132,38 @@
+ return pkgmem;
+ }
+
++ private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2) {
++ if (loader1 == loader2 || loader1 == null || loader2 == null) {
++ return true;
++ }
++ for (ClassLoader scan1 = loader1;
++ scan1 != null; scan1 = scan1.getParent()) {
++ if (scan1 == loader2) return true;
++ }
++ for (ClassLoader scan2 = loader2;
++ scan2 != null; scan2 = scan2.getParent()) {
++ if (scan2 == loader1) return true;
++ }
++ return false;
++ }
++
++ /**
++ * Ensure the requesting class have privileges to perform invokedynamic
++ * linkage operations on subjectClass. True if requestingClass is
++ * Access.class (meaning the request originates from the JVM) or if the
++ * classes are in the same package and have consistent class loaders.
++ * (The subject class loader must be identical with or be a child of
++ * the requesting class loader.)
++ * @param requestingClass
++ * @param subjectClass
++ */
++ public static void checkBootstrapPrivilege(Class requestingClass, Class subjectClass,
++ String permissionName) {
++ if (requestingClass == Access.class) return;
++ if (requestingClass == subjectClass) return;
++ SecurityManager security = System.getSecurityManager();
++ if (security == null) return; // open season
++ if (isSamePackage(requestingClass, subjectClass)) return;
++ security.checkPermission(new LinkagePermission(permissionName, requestingClass));
++ }
+ }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/indy-6829144.verify.patch Wed May 06 19:45:31 2009 -0700
@@ -0,0 +1,152 @@
+6829144: JSR 292 JVM features need a provisional Java API
+Summary: allow invokedynamic in old verifier plugin
+
+diff --git a/src/share/javavm/export/classfile_constants.h b/src/share/javavm/export/classfile_constants.h
+--- a/src/share/javavm/export/classfile_constants.h
++++ b/src/share/javavm/export/classfile_constants.h
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved.
++ * Copyright 2004-2009 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
+@@ -306,7 +306,7 @@
+ JVM_OPC_invokespecial = 183,
+ JVM_OPC_invokestatic = 184,
+ JVM_OPC_invokeinterface = 185,
+- JVM_OPC_xxxunusedxxx = 186,
++ JVM_OPC_invokedynamic = 186,
+ JVM_OPC_new = 187,
+ JVM_OPC_newarray = 188,
+ JVM_OPC_anewarray = 189,
+@@ -515,7 +515,7 @@
+ 3, /* invokespecial */ \
+ 3, /* invokestatic */ \
+ 5, /* invokeinterface */ \
+- 0, /* xxxunusedxxx */ \
++ 5, /* invokedynamic */ \
+ 3, /* new */ \
+ 2, /* newarray */ \
+ 3, /* anewarray */ \
+diff --git a/src/share/native/common/check_code.c b/src/share/native/common/check_code.c
+--- a/src/share/native/common/check_code.c
++++ b/src/share/native/common/check_code.c
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved.
++ * Copyright 1994-2009 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
+@@ -1223,16 +1223,20 @@
+ case JVM_OPC_invokevirtual:
+ case JVM_OPC_invokespecial:
+ case JVM_OPC_invokestatic:
++ case JVM_OPC_invokedynamic:
+ case JVM_OPC_invokeinterface: {
+ /* Make sure the constant pool item is the right type. */
+ int key = (code[offset + 1] << 8) + code[offset + 2];
+ const char *methodname;
+ jclass cb = context->class;
+ fullinfo_type clazz_info;
+- int is_constructor, is_internal;
++ int is_constructor, is_internal, is_invokedynamic;
+ int kind = (opcode == JVM_OPC_invokeinterface
+ ? 1 << JVM_CONSTANT_InterfaceMethodref
++ : opcode == JVM_OPC_invokedynamic
++ ? 1 << JVM_CONSTANT_NameAndType
+ : 1 << JVM_CONSTANT_Methodref);
++ is_invokedynamic = opcode == JVM_OPC_invokedynamic;
+ /* Make sure the constant pool item is the right type. */
+ verify_constant_pool_type(context, key, kind);
+ methodname = JVM_GetCPMethodNameUTF(env, cb, key);
+@@ -1241,8 +1245,11 @@
+ is_internal = methodname[0] == '<';
+ pop_and_free(context);
+
+- clazz_info = cp_index_to_class_fullinfo(context, key,
+- JVM_CONSTANT_Methodref);
++ if (is_invokedynamic)
++ clazz_info = context->object_info; // anything will do
++ else
++ clazz_info = cp_index_to_class_fullinfo(context, key,
++ JVM_CONSTANT_Methodref);
+ this_idata->operand.i = key;
+ this_idata->operand2.fi = clazz_info;
+ if (is_constructor) {
+@@ -1304,6 +1311,11 @@
+ "Fourth operand byte of invokeinterface must be zero");
+ }
+ pop_and_free(context);
++ } else if (opcode == JVM_OPC_invokedynamic) {
++ if (code[offset + 3] != 0 || code[offset + 4] != 0) {
++ CCerror(context,
++ "Third and fourth operand bytes of invokedynamic must be zero");
++ }
+ } else if (opcode == JVM_OPC_invokevirtual
+ || opcode == JVM_OPC_invokespecial)
+ set_protected(context, inumber, key, opcode);
+@@ -1990,6 +2002,7 @@
+
+ case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
+ case JVM_OPC_invokeinit: /* invokespecial call to <init> */
++ case JVM_OPC_invokedynamic:
+ case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: {
+ /* The top stuff on the stack depends on the method signature */
+ int operand = this_idata->operand.i;
+@@ -2005,7 +2018,8 @@
+ print_formatted_methodname(context, operand);
+ }
+ #endif
+- if (opcode != JVM_OPC_invokestatic)
++ if (opcode != JVM_OPC_invokestatic &&
++ opcode != JVM_OPC_invokedynamic)
+ /* First, push the object */
+ *ip++ = (opcode == JVM_OPC_invokeinit ? '@' : 'A');
+ for (p = signature + 1; *p != JVM_SIGNATURE_ENDFUNC; ) {
+@@ -2290,6 +2304,7 @@
+
+ case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
+ case JVM_OPC_invokeinit:
++ case JVM_OPC_invokedynamic:
+ case JVM_OPC_invokeinterface: case JVM_OPC_invokestatic: {
+ int operand = this_idata->operand.i;
+ const char *signature =
+@@ -2299,7 +2314,8 @@
+ int item;
+ const char *p;
+ check_and_push(context, signature, VM_STRING_UTF);
+- if (opcode == JVM_OPC_invokestatic) {
++ if (opcode == JVM_OPC_invokestatic ||
++ opcode == JVM_OPC_invokedynamic) {
+ item = 0;
+ } else if (opcode == JVM_OPC_invokeinit) {
+ fullinfo_type init_type = this_idata->operand2.fi;
+@@ -2680,6 +2696,7 @@
+
+ case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
+ case JVM_OPC_invokeinit:
++ case JVM_OPC_invokedynamic:
+ case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: {
+ /* Look to signature to determine correct result. */
+ int operand = this_idata->operand.i;
+diff --git a/src/share/native/common/opcodes.in_out b/src/share/native/common/opcodes.in_out
+--- a/src/share/native/common/opcodes.in_out
++++ b/src/share/native/common/opcodes.in_out
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved.
++ * Copyright 1998-2009 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
+@@ -210,7 +210,7 @@
+ {"?", "?"}, /* invokespecial */
+ {"?", "?"}, /* invokestatic */
+ {"?", "?"}, /* invokeinterface */
+- {"?", "?"}, /* xxxunusedxxx */
++ {"?", "?"}, /* invokedynamic */
+ {"", "A"}, /* new */
+ {"I", "A"}, /* newarray */
+ {"I", "A"}, /* anewarray */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/indy.tests.patch Wed May 06 19:45:31 2009 -0700
@@ -0,0 +1,686 @@
+diff --git a/test/java/dyn/FidgetDemo.java b/test/java/dyn/FidgetDemo.java
+new file mode 100644
+--- /dev/null
++++ b/test/java/dyn/FidgetDemo.java
+@@ -0,0 +1,143 @@
++/*
++ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation. Sun designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Sun in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
++ * CA 95054 USA or visit www.sun.com if you need additional information or
++ * have any questions.
++ */
++
++/*
++ * @test
++ * @bug 6829144
++ * @summary smoke test for invokedynamic
++ * @author jrose
++ * @compile -XDinvokedynamic FidgetDemo.java
++ * @run main/othervm -XX:+EnableInvokeDynamic FidgetDemo
++ */
++
++import java.dyn.*;
++import static java.dyn.MethodHandles.*;
++import static java.dyn.MethodType.*;
++
++public class FidgetDemo {
++ public static void main(String... av) {
++ if (av.length == 0) av = new String[]{ "fred", "buster", "ricky" };
++ System.out.println("Fidgety self-modifying call site...");
++ for (int i = 0; i < 6; i++) {
++ System.out.println("--- loop #"+i);
++ for (String a : av) {
++ String result = InvokeDynamic.<String>fidget(a);
++ System.out.println(result);
++ }
++ }
++ }
++
++ private static String itch(String x) { return x+" can't get comfortable"; }
++ private static String fuss(String x) { return x+" is feeling moody"; }
++ private static String bore(String x) { return x+" needs a change of scenery"; }
++ private static final String[] NAMES = { "itch", "fuss", "bore" };
++ private static MethodHandle which(int i) {
++ return lookup().findStatic(FidgetDemo.class, NAMES[i % NAMES.length],
++ make(String.class, String.class));
++ }
++
++ // It is easy to build hybrid closure-like method handles out of classes.
++ private static class Guard extends JavaMethodHandle {
++ MethodHandle target; // wrapped target
++ CSite site; // included site
++ // constructor folds a sneaky site reference into a direct target
++ Guard(CSite site, MethodHandle target) {
++ super(INVOKE);
++ this.target = target;
++ this.site = site;
++ }
++
++ String invoke(String x) {
++ if ((++site.count & 3) == 0)
++ site.setTarget(new Guard(site, which(site.count)));
++ return target.<String>invoke(x);
++ }
++ private static final MethodHandle INVOKE =
++ lookup().findVirtual(Guard.class, "invoke",
++ make(String.class, String.class));
++
++ public String toString() { return "Guard:"+target.toString(); }
++ }
++
++ // Use a local call site subclass. (These are optional but fun.)
++ private static class CSite extends CallSite {
++ int count;
++ public CSite(Class caller, String name, MethodType type) {
++ super(caller, name, type);
++ //?? super(String.class, "gloat", make(void.class));
++ System.out.println("[link] new call site: "+this);
++ setTarget(new Guard(this, which(0)));
++ }
++ // this is just for the noise value:
++ public void setTarget(MethodHandle t) {
++ System.out.println("[link] set target to "+t);
++ super.setTarget(t);
++ }
++ }
++
++ // Set up a class-local bootstrap method.
++ static { Linkage.registerBootstrapMethod("linkDynamic"); }
++ private static CallSite linkDynamic(Class caller, String name, MethodType type) {
++ assert((Object)name == "fidget" &&
++ type == make(String.class, String.class));
++ return new CSite(caller, name, type);
++ }
++}
++
++/* resulting output
++--------
++Fidgety self-modifying call site...
++--- loop #0
++[link] new call site: CallSite#14491894[fidget(java.lang.String)java.lang.String => null]
++[link] set target to Guard:itch(java.lang.String)java.lang.String
++fred can't get comfortable
++buster can't get comfortable
++ricky can't get comfortable
++--- loop #1
++[link] set target to Guard:fuss(java.lang.String)java.lang.String
++fred can't get comfortable
++buster is feeling moody
++ricky is feeling moody
++--- loop #2
++fred is feeling moody
++[link] set target to Guard:bore(java.lang.String)java.lang.String
++buster is feeling moody
++ricky needs a change of scenery
++--- loop #3
++fred needs a change of scenery
++buster needs a change of scenery
++[link] set target to Guard:itch(java.lang.String)java.lang.String
++ricky needs a change of scenery
++--- loop #4
++fred can't get comfortable
++buster can't get comfortable
++ricky can't get comfortable
++--- loop #5
++[link] set target to Guard:fuss(java.lang.String)java.lang.String
++fred can't get comfortable
++buster is feeling moody
++ricky is feeling moody
++--------
++ */
+diff --git a/test/sun/dyn/anonk/CPParser.java b/test/sun/dyn/anonk/CPParser.java
+new file mode 100644
+--- /dev/null
++++ b/test/sun/dyn/anonk/CPParser.java
+@@ -0,0 +1,145 @@
++/*
++ * Copyright 2008-2009 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.
++ *
++ * 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.
++ */
++
++// minimal constant pool parser
++
++import java.io.*;
++import java.util.*;
++
++class CPParser {
++ private CPParser() {}
++
++ public static Object[] parse(byte[] bytes) {
++ return parse(bytes, 0, bytes.length);
++ }
++ public static Object[] parse(byte[] bytes, int offset, int length) {
++ try {
++ ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytes, offset, offset + length);
++ DataInputStream in = new DataInputStream(bytesIn);
++ int magic = in.readInt();
++ if (magic != 0xCAFEBABE) die("magic number");
++ int maj = in.readUnsignedShort(), min = in.readUnsignedShort();
++ int len = in.readUnsignedShort();
++ if (!(len >= 1)) die("cp length");
++ Object[] objects = new Object[len];
++ long[] locs = new long[len];
++ for (int i = 1, nexti; i < len; i = nexti) {
++ nexti = i+1;
++ int loc = offset + length - (int) bytesIn.available();
++ int tag = in.readUnsignedByte();
++ locs[i] = (tag << 32) + loc;
++ Object obj = null;
++ switch (tag) {
++ case CONSTANT_Utf8: obj = in.readUTF(); break;
++ case CONSTANT_Integer: obj = in.readInt(); break;
++ case CONSTANT_Float: obj = in.readFloat(); break;
++ case CONSTANT_Long: obj = in.readLong(); ++nexti; break;
++ case CONSTANT_Double: obj = in.readDouble(); ++nexti; break;
++ case CONSTANT_Class: // fall through:
++ case CONSTANT_String: obj = (int) in.readUnsignedShort(); break;
++
++ case CONSTANT_Fieldref: // fall through:
++ case CONSTANT_Methodref: // fall through:
++ case CONSTANT_InterfaceMethodref: // fall through:
++ case CONSTANT_NameAndType:
++ obj = new int[] { in.readUnsignedShort(), in.readUnsignedShort() };
++ break;
++ }
++ objects[i] = obj;
++ }
++ // pass back the locs inside the objects array:
++ objects[0] = locs;
++ // pass back the last position within the byte array argument:
++ int loc = offset + length - (int) bytesIn.available();
++ locs[0] = loc;
++ return objects;
++ } catch (IOException ee) {
++ die(ee.toString());
++ return null;
++ }
++ }
++
++ public static Object[] resolve(Object[] objects) {
++ objects = objects.clone();
++ long[] locs = (long[]) objects[0];
++ for (int i = 1; i < objects.length; i++) {
++ Object obj = objects[i];
++ int tag = (int)(locs[i] >> 32);
++ switch (tag) {
++ case CONSTANT_Class:
++ case CONSTANT_String:
++ obj = Arrays.asList(tagNames[tag], objects[(Integer)obj]);
++ break;
++ case CONSTANT_Fieldref: // fall through:
++ case CONSTANT_Methodref: // fall through:
++ case CONSTANT_InterfaceMethodref: // fall through:
++ int[] pair1 = (int[]) obj;
++ Object cls = objects[pair1[0]];
++ if (cls instanceof Integer)
++ cls = objects[(Integer)cls];
++ else if (cls instanceof Object[])
++ cls = ((Object[])cls)[1];
++ int[] pair2 = (int[]) objects[pair1[1]];
++ Object name = objects[pair2[0]];
++ Object sig = objects[pair2[1]];
++ obj = Arrays.asList(tagNames[tag], cls, name, sig);
++ break;
++ }
++ objects[i] = obj;
++ }
++ return objects;
++ }
++
++ static void die(String msg) {
++ throw new RuntimeException("bad CP: "+msg);
++ }
++
++ private static final int
++ CONSTANT_Utf8 = 1,
++ CONSTANT_Unicode = 2, /* unused */
++ CONSTANT_Integer = 3,
++ CONSTANT_Float = 4,
++ CONSTANT_Long = 5,
++ CONSTANT_Double = 6,
++ CONSTANT_Class = 7,
++ CONSTANT_String = 8,
++ CONSTANT_Fieldref = 9,
++ CONSTANT_Methodref = 10,
++ CONSTANT_InterfaceMethodref = 11,
++ CONSTANT_NameAndType = 12;
++ private static String[] tagNames = {
++ "null",
++ "Utf8",
++ "Unicode",
++ "Integer",
++ "Float",
++ "Long",
++ "Double",
++ "Class",
++ "String",
++ "Fieldref",
++ "Methodref",
++ "InterfaceMethodref",
++ "NameAndType"
++ };
++}
+diff --git a/test/sun/dyn/anonk/TestAnonK.java b/test/sun/dyn/anonk/TestAnonK.java
+new file mode 100644
+--- /dev/null
++++ b/test/sun/dyn/anonk/TestAnonK.java
+@@ -0,0 +1,383 @@
++/*
++ * Copyright 2008-2009 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.
++ *
++ * 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.
++ */
++
++/*
++ javac -d Classes TestAnonK.java
++ java -esa -ea -Xbootclasspath/p:$REPO/Classes/bootcp -cp $REPO/Classes TestAnonK
++
++ (dbx) r -ea -esa -Xbootclasspath/p:$REPO/Classes/bootcp -cp $REPO/Classes TestAnonK
++ (dbx) (cd $REPO/Classes; ./MAKE.sh) && r
++
++ known-good JVM internal version:
++ java -Xinternalversion
++ OpenJDK Server VM (12.0-b01-internal-jvmg) for solaris-x86 JRE (1.7.0), built on Jan 22 2008 00:14:41 by "jrose" with Workshop 5.8
++ */
++
++import java.util.*;
++import java.io.*;
++import java.dyn.AnonymousClassLoader;
++import java.dyn.ConstantPoolPatch;
++
++/**
++ * @test
++ * @bug 4546734 5007612
++ * @summary AnonymousClassLoader, basic test
++ * @run main/othervm -XX:+EnableAnonymousClasses
++ * @author jrose
++ */
++class TestAnonK {
++ // TO DO: make more unit tests, a la test/java/lang/Appendable/Basic.java
++
++ public static void main(String... av) throws Exception {
++ System.out.println("hello, world, from DVM!");
++ if (Arrays.equals(av, new String[]{ "-raw" }))
++ // this will fail if TestAnonK is not on the boot class path
++ { testRaw(); return; }
++ testAPI();
++ }
++
++ static void testAPI() throws Exception {
++ // Make a bunch of copies of Temp, nested in this class.
++ Class hostClass = TestAnonK.class;
++ AnonymousClassLoader loader = new AnonymousClassLoader(hostClass);
++ List<Class> classes = new ArrayList<Class>();
++ List<Inter> objects = new ArrayList<Inter>();
++ for (int n = 0; n <= 3; n++) {
++ Class acls = Temp.class;
++ ConstantPoolPatch cpp = new ConstantPoolPatch(acls);
++ if (n > 0) {
++ Object oddity = Arrays.asList("odd", "constant", n, "!");
++ HashMap<String,String> utf8Map = new HashMap<String,String>();
++ HashMap<String,Object> classMap = new HashMap<String,Object>();
++ HashMap<Object,Object> constantMap = new HashMap<Object,Object>();
++ // replace Utf8 "dummyStatic" with "foo"+n:
++ utf8Map.put("dummyStatic", "foo"+n);
++ // replace Integer 123456 with n*1000:
++ constantMap.put(-123456, n*1000);
++ // change name of local routine, just for kicks:
++ utf8Map.put("funny", "hilarious"+n);
++ // maybe patch the super class
++ if (n >= 2) classMap.put(TempBase.class.getName(), classes.get(n-2));
++ // change string constant to a pseudo-string:
++ constantMap.put("this space for rent", oddity);
++ cpp.putPatches(utf8Map, classMap, constantMap, true);
++ System.out.println("patches = "+cpp);
++ assert(utf8Map.isEmpty());
++ assert(classMap.isEmpty());
++ assert(constantMap.isEmpty());
++ acls = loader.loadClass(cpp);
++ }
++ System.out.println("loaded "+acls);
++ for (int i = 0; i < n; i++)
++ assert(classes.get(i) != acls);
++ classes.add(acls);
++ }
++ try {
++ exercise(classes, objects);
++ } catch (IllegalArgumentException ee) {
++ if (ee.getMessage().indexOf("not funny") > 0) {
++ System.out.print("OK exception: ");
++ ee.printStackTrace();
++ } else {
++ throw ee;
++ }
++ }
++ System.out.println("Success!");
++ }
++
++ static void testRaw() throws Exception {
++ // ignore two warnings on this line:
++ sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
++
++ final int bufHeaderLen = unsafe.arrayBaseOffset(byte[].class); // 12 or 24
++
++ // Load up the bytecodes of Temp (our template class).
++ Class tempClass = TestAnonK.Temp.class;
++ byte[] tempBytes = readFile(tempClass.getResourceAsStream(tempClass.getName()+".class"));
++ System.out.println("read "+tempBytes.length+" bytes from "+tempClass.getName());
++
++ // Make a bunch of copies of Temp, nested in this class.
++ Class hostClass = TestAnonK.class;
++ List<Class> classes = new ArrayList<Class>();
++ List<Inter> objects = new ArrayList<Inter>();
++ for (int n = 0; n <= 3; n++) {
++ Class acls = Temp.class;
++ if (n > 0) {
++ Object oddity = Arrays.asList("odd", "constant", n, "!");
++ Object[] cpPatches
++ = makePatches(tempBytes,
++ // replace Utf8 "dummyStatic" with "foo"+n:
++ "dummyStatic", "foo"+n,
++ // replace Integer 123456 with n*1000:
++ -123456, n*1000,
++ // change name of local routine, just for kicks:
++ "funny", "hilarious"+n,
++ // maybe patch the super class
++ TempBase.class,
++ (n >= 2) ? classes.get(n-2) : null,
++ // change string constant to a pseudo-string:
++ Arrays.asList("String", "this space for rent"),
++ oddity
++ );
++ acls = unsafe.defineAnonymousClass(hostClass, tempBytes, cpPatches);
++ }
++ System.out.println("loaded "+acls);
++ for (int i = 0; i < n; i++)
++ assert(classes.get(i) != acls);
++ classes.add(acls);
++ }
++ try {
++ exercise(classes, objects);
++ } catch (IllegalArgumentException ee) {
++ if (ee.getMessage().indexOf("not funny") > 0) {
++ System.out.print("OK exception: ");
++ ee.printStackTrace();
++ } else {
++ throw ee;
++ }
++ }
++ System.out.println("Success!");
++ }
++
++ static void exercise(List<Class> classes,
++ List<Inter> objects) throws Exception {
++ /*
++ for (Class acls : classes) {
++ unsafe.ensureClassInitialized(acls);
++ System.out.println("initialized "+acls);
++ }
++ */
++ for (Class acls : classes) {
++ Inter obj = (Inter) acls.newInstance();
++ assert(obj.getClass() == acls);
++ System.out.println("created "+obj);
++ objects.add(obj);
++ }
++ System.out.println("================");
++ for (Inter obj : objects) {
++ obj.foo();
++ System.out.println("----------------");
++ }
++ System.out.println("================");
++
++ new TempBase(); // don't ask why; has to do with access$ methods
++ }
++
++ private static Object[] makePatches(byte[] classFile, Object... patches) {
++ Object[] cp = CPParser.resolve(CPParser.parse(classFile));
++ //System.out.println(Arrays.deepToString(cp));
++ /* Sample output:
++ ---
++ [[637, 10, 9, 7, ...],
++ [Methodref, java/lang/Object, <init>, ()V],
++ [Fieldref, java/lang/System, out, Ljava/io/PrintStream;],
++ [Class, java/lang/StringBuilder], ...,
++ [Fieldref, TestAnonK, dummyStatic, Ljava/lang/String;],
++ [Class, TestAnonK$Temp], ...,
++ toString, java/io/PrintStream, println, (Ljava/lang/String;)V,
++ dummyStatic, Ljava/lang/String;]
++ ---
++ */
++
++ for (int j = 0; j < patches.length; j += 2) {
++ Object pattern = patches[j];
++ if (pattern instanceof Class) {
++ patches[j] = Arrays.asList("Class", ((Class)pattern).getName());
++ }
++ }
++
++ Object[] cpPatches = new Object[cp.length];
++ for (int i = 1; i < cp.length; i++) {
++ Object obj = cp[i];
++ Object patch = null;
++ for (int j = 0; j < patches.length; j += 2) {
++ Object pattern = patches[j];
++ if (pattern.equals(obj)) {
++ patch = patches[j+1];
++ break;
++ }
++ }
++ cpPatches[i] = patch;
++ }
++
++ //System.out.println(Arrays.deepToString(cpPatches));
++ /* Sample output:
++ ---
++ [null, null, null, ...,
++ foo2, null]
++ ---
++ */
++ return cpPatches;
++ }
++
++ /** Read the given stream until EOF. Slurp everything into an array. */
++ // Ouch, why isn't this in rt.jar already??
++ static byte[] readFile(InputStream in) throws IOException {
++ byte[][] bufs = new byte[7][];
++ int nbufs = 0;
++ int totalLen = 0;
++ byte[] lastBuf;
++ int lastLen;
++ int buflen = (1 << 6);
++ mainLoop:
++ for (;;) {
++ // fill up one buffer at a time
++ int avail = (int)in.available();
++ if (avail <= buflen) {
++ avail = buflen; // better guess, in case in.avail is broken
++ if (buflen < (1 << 14)) buflen <<= 1;
++ }
++ byte[] buf = new byte[avail];
++ int len = 0;
++ while (len < avail) {
++ int nr = in.read(buf, len, avail-len);
++ if (nr <= 0) {
++ if (len == 0 && nbufs > 0) {
++ lastBuf = bufs[--nbufs];
++ lastLen = lastBuf.length;
++ } else {
++ lastBuf = buf;
++ lastLen = len;
++ totalLen += lastLen;
++ }
++ break mainLoop;
++ }
++ len += nr;
++ }
++ // add the buffer to the list so we can look for more
++ if (nbufs == bufs.length)
++ bufs = Arrays.copyOf(bufs, nbufs << 2);
++ bufs[nbufs++] = buf;
++ totalLen += avail;
++ }
++ in.close();
++ if (nbufs == 0) {
++ if (totalLen == lastBuf.length)
++ return lastBuf; // common case, if in.avail was correct
++ else
++ return Arrays.copyOf(lastBuf, totalLen);
++ }
++ byte[] result = new byte[totalLen];
++ int fill = 0;
++ for (int i = 0; i < nbufs; i++) {
++ byte[] buf = bufs[i];
++ int len = buf.length;
++ System.arraycopy(buf, 0, result, fill, len);
++ fill += len;
++ }
++ if (lastLen > 0) {
++ System.arraycopy(lastBuf, 0, result, fill, lastLen);
++ }
++ assert(fill + lastLen == totalLen);
++ return result;
++ }
++
++ private static String hostPrivateGreeting = "Hello"; // private but accessible
++
++ static String dummyStatic = "DUMMY!";
++ private static String foo1 = "foo1 string";
++ private static String foo2 = "foo2 different string";
++ private static String foo3 = "foo3 still different string";
++
++ interface Inter {
++ void foo();
++ }
++
++ static private class TempBase {
++ TempBase() { } // it is package-private, but can access from host class
++ public void foo() {
++ System.out.println("...end of foo stuff");
++ }
++ }
++
++ static private class Temp extends TempBase implements Inter {
++ Temp() { } // it is package-private, but can access from host class
++ public void foo() {
++ System.out.println(hostPrivateGreeting + " from foo "+this);
++ System.out.println("secret string is "+dummyStatic);
++ System.out.println("funny number is "+funny());
++ Object oddity = "this space for rent";
++ System.out.println("wierd constant class is "+(oddity.getClass())+" for "+oddity.toString());
++ super.foo();
++ }
++ private int funny() {
++ int n = -123456;
++ if (n >= 3000)
++ throw new IllegalArgumentException("that's not funny");
++ return n;
++ }
++ }
++}
++
++
++/*
++ Sample output:
++----
++$ ./gamma -XX:+AnonymousClasses -Xbootclasspath/p:/scratch/jrose/ws/davinci/build/bootcp TestAnonK
++VM option '+AnonymousClasses'
++hello, world, from DVM!
++loaded class TestAnonK$Temp
++patches = java.dyn.ConstantPoolPatch{this space for rent=[odd, constant, 1, !], -123456=1000, funny=hilarious1, dummyStatic=foo1}
++loaded class TestAnonK$Temp/747212
++patches = java.dyn.ConstantPoolPatch{this space for rent=[odd, constant, 2, !], -123456=2000, TestAnonK$TempBase=class TestAnonK$Temp, funny=hilarious2, dummyStatic=foo2}
++loaded class TestAnonK$Temp/9047389
++patches = java.dyn.ConstantPoolPatch{this space for rent=[odd, constant, 3, !], -123456=3000, TestAnonK$TempBase=class TestAnonK$Temp/747212, funny=hilarious3, dummyStatic=foo3}
++loaded class TestAnonK$Temp/24347419
++created TestAnonK$Temp@a470b8
++created TestAnonK$Temp/747212@1e4457d
++created TestAnonK$Temp/9047389@18e2b22
++created TestAnonK$Temp/24347419@b1c5fa
++================
++Hello from foo TestAnonK$Temp@a470b8
++secret string is DUMMY!
++funny number is -123456
++wierd constant class is class java.lang.String for this space for rent
++...end of foo stuff
++----------------
++Hello from foo TestAnonK$Temp/747212@1e4457d
++secret string is foo1 string
++funny number is 1000
++wierd constant class is class java.util.Arrays$ArrayList for [odd, constant, 1, !]
++...end of foo stuff
++----------------
++Hello from foo TestAnonK$Temp/9047389@18e2b22
++secret string is foo2 different string
++funny number is 2000
++wierd constant class is class java.util.Arrays$ArrayList for [odd, constant, 2, !]
++Hello from foo TestAnonK$Temp/9047389@18e2b22
++secret string is DUMMY!
++funny number is -123456
++wierd constant class is class java.lang.String for this space for rent
++...end of foo stuff
++----------------
++Hello from foo TestAnonK$Temp/24347419@b1c5fa
++secret string is foo3 still different string
++OK exception: java.lang.IllegalArgumentException: that's not funny
++ at TestAnonK$Temp/24347419.hilarious3(TestAnonK.java:324)
++ at TestAnonK$Temp/24347419.foo(TestAnonK.java:316)
++ at TestAnonK.exercise(TestAnonK.java:176)
++ at TestAnonK.testAPI(TestAnonK.java:94)
++ at TestAnonK.main(TestAnonK.java:54)
++Success!
++----
++*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/meth-6829144.patch Wed May 06 19:45:31 2009 -0700
@@ -0,0 +1,8834 @@
+6829144: JSR 292 JVM features need a provisional Java API
+Summary: JSR 292 RI for method handles, preview edition
+
+diff --git a/make/docs/CORE_PKGS.gmk b/make/docs/CORE_PKGS.gmk
+--- a/make/docs/CORE_PKGS.gmk
++++ b/make/docs/CORE_PKGS.gmk
+@@ -55,6 +55,7 @@
+ # This is a list of regular expressions. So foo.* matches "foo" and "foo.bar".
+ #
+ ACTIVE_JSR_PKGS= \
++ java.dyn \
+ java.sql \
+ javax.activation \
+ javax.annotation.* \
+diff --git a/make/java/Makefile b/make/java/Makefile
+--- a/make/java/Makefile
++++ b/make/java/Makefile
+@@ -1,5 +1,5 @@
+ #
+-# Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
++# Copyright 1995-2009 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
+@@ -39,7 +39,7 @@
+ # Others
+ # Note: java_crw_demo java_hprof_demo are demos but must be delivered built in sdk
+ SUBDIRS += security npt java_crw_demo java_hprof_demo \
+- math awt util text applet net nio \
++ math awt util text applet net nio dyn \
+ sql rmi jar beans logging management instrument
+
+
+diff --git a/make/java/dyn/Makefile b/make/java/dyn/Makefile
+new file mode 100644
+--- /dev/null
++++ b/make/java/dyn/Makefile
+@@ -0,0 +1,44 @@
++#
++# Copyright 2008-2009 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.
++#
++
++BUILDDIR = ../..
++
++PACKAGE = java.dyn
++PRODUCT = java
++include $(BUILDDIR)/common/Defs.gmk
++
++AUTO_FILES_JAVA_DIRS = java/dyn sun/dyn
++
++# The sources built here use new language syntax to generate
++# method handle calls. Let's be sure we are using that format.
++#LANGUAGE_VERSION = -source 7
++#CLASS_VERSION = -target 7
++
++# Actually, it will be less disruptive to compile with the same
++# -target option as the rest of the system, and just turn on
++# the specific compiler option we need here:
++OTHER_JAVACFLAGS = -XDinvokedynamic
++
++include $(BUILDDIR)/common/Classes.gmk
+diff --git a/src/share/classes/java/dyn/JavaMethodHandle.java b/src/share/classes/java/dyn/JavaMethodHandle.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/JavaMethodHandle.java
+@@ -0,0 +1,83 @@
++/*
++ * Copyright 2008-2009 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;
++
++/**
++ * A Java method handle extends the basic method handle type with additional
++ * programmer defined methods and fields.
++ * Its behavior as a method handle is determined at instance creation time,
++ * by providing the new instance with an "entry point" method handle
++ * to handle calls. This entry point must accept a leading argument
++ * whose type is the Java method handle itself or a supertype, and the
++ * entry point is always called with the Java method handle itself as
++ * the first argument. This is similar to ordinary virtual methods, which also
++ * accept the receiver object {@code this} as an implicit leading argument.
++ * The {@code MethodType} of the Java method handle is the same as that
++ * of the entry point method handle, with the leading parameter type
++ * omitted.
++ * <p>
++ * Here is an example of usage:
++ * <p><blockquote><pre>
++ * class Greeter extends JavaMethodHandle {
++ * public void run() { System.out.println("hello, "+greetee); }
++ * private final String greetee;
++ * Greeter(String greetee) {
++ * super(RUN);
++ * this.greetee = greetee;
++ * }
++ * // the entry point function is computed once:
++ * private static final MethodHandle RUN
++ * = MethodHandles.findVirtual(MyMethodHandle.class, "run",
++ * MethodType.make(void.class));
++ * }
++ * Greeter greeter = new Greeter("world");
++ * greeter.run(); // prints "hello, world"
++ * MethodHandle mh = greeter;
++ * mh.invoke(); // also prints "hello, world"
++ * </pre></blockquote>
++ * <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.
++ * @see MethodHandle
++ * @author John Rose, JSR 292 EG
++ */
++public abstract class JavaMethodHandle
++ // Note: This is an implementation inheritance hack, and will be removed
++ // with a JVM change which moves the required hidden behavior onto this class.
++ extends sun.dyn.BoundMethodHandle
++{
++ /**
++ * When creating a, pass in {@code entryPoint}, any method handle which
++ * can take the current object
++ * @param entryPoint
++ */
++ protected JavaMethodHandle(MethodHandle entryPoint) {
++ super(entryPoint, 0);
++ }
++}
+diff --git a/src/share/classes/java/dyn/MethodHandle.java b/src/share/classes/java/dyn/MethodHandle.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/MethodHandle.java
+@@ -0,0 +1,135 @@
++/*
++ * Copyright 2008-2009 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;
++
++//import sun.dyn.*;
++
++import sun.dyn.Access;
++import sun.dyn.MethodHandleImpl;
++
++/**
++ * A method handle is a typed reference to the entry point of a method.
++ * <p>
++ * Method handles are strongly typed according to signature.
++ * They are not distinguished by method name or enclosing class.
++ * A method handle must be invoked under a signature which exactly matches
++ * the method handle's own type.
++ * <p>
++ * Every method handle confesses its type via the <code>type</code> accessor.
++ * The structure of this type is a series of classes, one of which is
++ * the return type of the method (or <code>void.class</code> if none).
++ * <p>
++ * Every method handle appears as an object containing a method named
++ * <code>invoke</code>, whose signature exactly matches
++ * the method handle's type.
++ * A normal Java method call (using the <code>invokevirtual</code> instruction)
++ * can invoke this method from Java source code (if language support is present).
++ * <p>
++ * Every call to a method handle specifies an intended method type,
++ * which must exactly match the type of the method handle.
++ * (The type is specified in the <code>invokevirtual</code> instruction,
++ * via a {@code CONSTANT_NameAndType} constant pool entry.)
++ * The call looks within the receiver object for a method
++ * named <code>invoke</code> of the intended method type.
++ * The call fails with a {@link WrongMethodTypeException}
++ * if the method does not exist, even if there is an <code>invoke</code>
++ * method of a closely similar signature.
++ * <p>
++ * A method handle is an unrestricted capability to call a method.
++ * A method handle can be formed on a non-public method by a class
++ * that has access to that method; the resulting handle can be used
++ * in any place by any caller who receives a reference to it. Thus, access
++ * checking is performed when the method handle is created, not
++ * (as in reflection) every time it is called. Handles to non-public
++ * methods, or in non-public classes, should generally be kept secret.
++ * They should not be passed to untrusted code.
++ * <p>
++ * Bytecode in an extended JVM can directly call a method handle's
++ * <code>invoke</code> from an <code>invokevirtual</code> instruction.
++ * The receiver class type must be <code>MethodHandle</code> and the method name
++ * must be <code>invoke</code>. The signature of the invocation
++ * (after resolving symbolic type names) must exactly match the method type
++ * of the target method.
++ * <p>
++ * Bytecode in an extended JVM can directly obtain a method handle
++ * for any accessible method from a <code>ldc</code> instruction
++ * which refers to a <code>CONSTANT_Methodref</code> or
++ * <code>CONSTANT_InterfaceMethodref</code> constant pool entry.
++ * <p>
++ * All JVMs can also use a reflective API called <code>MethodHandles</code>
++ * for creating and calling method handles.
++ * <p>
++ * A method reference may refer either to a static or non-static method.
++ * In the non-static case, the method handle type includes an explicit
++ * receiver argument, prepended before any other arguments.
++ * In the method handle's type, the initial receiver argument is typed
++ * according to the class under which the method was initially requested.
++ * (E.g., if a non-static method handle is obtained via <code>ldc</code>,
++ * the type of the receiver is the class named in the constant pool entry.)
++ * <p>
++ * When a method handle to a virtual method is invoked, the method is
++ * always looked up in the receiver (that is, the first argument).
++ * <p>
++ * A non-virtual method handles to a specific virtual method implementation
++ * can also be created. These do not perform virtual lookup based on
++ * receiver type. Such a method handle simulates the effect of
++ * an <code>invokespecial</code> instruction to the same method.
++ *
++ * @see MethodType
++ * @see MethodHandles
++ * @author John Rose, JSR 292 EG
++ */
++public abstract class MethodHandle
++ // Note: This is an implementation inheritance hack, and will be removed
++ // with a JVM change which moves the required hidden state onto this class.
++ extends MethodHandleImpl
++{
++ // interface MethodHandle<T extends MethodType<R,A...>>
++ // { T type(); <R,A...> public R invoke(A...); }
++
++ final private MethodType type;
++
++ /**
++ * Report the type of this method handle.
++ * Every invocation of this method handle must exactly match this type.
++ * @return the method handle type
++ */
++ public MethodType type() {
++ return type;
++ }
++
++ /**
++ * The constructor for MethodHandle may only be called by privileged code.
++ * Subclasses may be in other packages, but must possess
++ * a token which they obtained from MH with a security check.
++ * @param token non-null object which proves access permission
++ * @param type type (permanently assigned) of the new method handle
++ */
++ protected MethodHandle(Access token, MethodType type) {
++ super(token);
++ this.type = type;
++ }
++}
+diff --git a/src/share/classes/java/dyn/MethodHandles.java b/src/share/classes/java/dyn/MethodHandles.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/MethodHandles.java
+@@ -0,0 +1,1092 @@
++/*
++ * Copyright 2008-2009 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;
++
++import java.lang.reflect.Constructor;
++import sun.dyn.Access;
++import sun.dyn.MemberName;
++import sun.dyn.MethodHandleImpl;
++import sun.dyn.util.VerifyAccess;
++import sun.dyn.util.Wrapper;
++import java.lang.reflect.Field;
++import java.lang.reflect.Method;
++import java.lang.reflect.Modifier;
++import java.util.ArrayList;
++import java.util.Arrays;
++import sun.dyn.Invokers;
++import sun.dyn.MethodTypeImpl;
++import sun.reflect.Reflection;
++import static sun.dyn.MemberName.newIllegalArgumentException;
++import static sun.dyn.MemberName.newNoAccessException;
++
++/**
++ * Fundamental operations and utilities for MethodHandle.
++ * <p>
++ * <em>API Note:</em> The matching of method types in this API cannot
++ * be completely checked by Java's generic type system for three reasons:
++ * <ol>
++ * <li>Method types range over all possible arities,
++ * from no arguments to an arbitrary number of arguments.
++ * Generics are not variadic, and so cannot represent this.</li>
++ * <li>Method types can specify arguments of primitive types,
++ * which Java generic types cannot range over.</li>
++ * <li>Method types can optionally specify varargs (ellipsis).</li>
++ * </ol>
++ * @author John Rose, JSR 292 EG
++ */
++public class MethodHandles {
++
++ private MethodHandles() { } // do not instantiate
++
++ private static final Access IMPL_TOKEN = Access.getToken();
++ private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
++ static { MethodHandleImpl.initStatics(); }
++ // See IMPL_LOOKUP below.
++
++ //// Method handle creation from ordinary methods.
++
++ public static Lookup lookup() {
++ return new Lookup();
++ }
++
++ /**
++ * A factory object for creating method handles, when the creation
++ * requires access checking. Method handles do not perform
++ * access checks when they are called; this is a major difference
++ * from reflective {@link Method}, which performs access checking
++ * against every caller, on every call. Method handle access
++ * restrictions are enforced when a method handle is created.
++ * The caller class against which those restrictions are enforced
++ * is known as the "lookup class". {@link Lookup} embodies an
++ * authenticated lookup class, and can be used to create any number
++ * of access-checked method handles, all checked against a single
++ * lookup class.
++ * <p>
++ * A class which needs to create method handles will call
++ * {@code MethodHandles.lookup()} to create a factory for itself.
++ * It may then use this factory to create method handles on
++ * all of its methods, including private ones.
++ * It may also delegate the lookup (e.g., to a metaobject protocol)
++ * by passing the {@code Lookup} object to other code.
++ * If this other code creates method handles, they will be access
++ * checked against the original lookup class, and not with any higher
++ * privileges.
++ * <p>
++ * Note that access checks only apply to named and reflected methods.
++ * Other method handle creation methods, such as {@link #convertArguments},
++ * do not require any access checks, and can be done independently
++ * of any lookup class.
++ * <p>
++ * <em>A note about error conditions:<em> A lookup can fail, because
++ * the containing class is not accessible to the lookup class, or
++ * because the desired class member is missing, or because the
++ * desired class member is not accessible to the lookup class.
++ * It can also fail if a security manager is installed and refuses
++ * access. In any of these cases, an exception will be
++ * thrown from the attempted lookup.
++ * In general, the conditions under which a method handle may be
++ * created for a method M are exactly as restrictive as the conditions
++ * under which the lookup class could have compiled a call to M.
++ */
++ public static final
++ class Lookup {
++ private final Class<?> lookupClass;
++
++ /** Which class is performing the lookup? It is this class against
++ * which checks are performed for visibility and access permissions.
++ * <p>
++ * This value is null if and only if this lookup is {@link #PUBLIC_LOOKUP}.
++ */
++ public Class<?> lookupClass() {
++ return lookupClass;
++ }
++
++ /** Embody the current class (the lookupClass) as a lookup class
++ * for method handle creation.
++ * Must be called by from a method in this package,
++ * which in turn is called by a method not in this package.
++ * Also, don't make it private, lest javac interpose
++ * an access$N method.
++ */
++ Lookup() {
++ Class caller = getCallerClassAtEntryPoint();
++ // make sure we haven't accidentally picked up this class:
++ checkUnprivilegedlookupClass(caller);
++ this.lookupClass = caller;
++ }
++
++ private Lookup(Class<?> lookupClass) {
++ this.lookupClass = lookupClass;
++ }
++
++ /** Version of lookup which is trusted minimally.
++ * It can only be used to create method handles to
++ * publicly accessible members.
++ */
++ public static final Lookup PUBLIC_LOOKUP = new Lookup(null);
++
++ /** Package-private version of lookup which is trusted. */
++ static final Lookup IMPL_LOOKUP = new Lookup(Access.class);
++ static { MethodHandleImpl.initLookup(IMPL_TOKEN, IMPL_LOOKUP); }
++
++ private static void checkUnprivilegedlookupClass(Class<?> lookupClass) {
++ if (lookupClass == null ||
++ lookupClass == Access.class ||
++ lookupClass.getName().startsWith("java.dyn."))
++ throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
++ }
++
++ @Override
++ public String toString() {
++ if (lookupClass == null)
++ return "public";
++ return lookupClass.getName();
++ }
++
++ // call this from an entry point method in Lookup with extraFrames=0.
++ private static Class<?> getCallerClassAtEntryPoint() {
++ final int CALLER_DEPTH = 4;
++ // 0: Reflection.getCC, 1: getCallerClassAtEntryPoint,
++ // 2: Lookup.<init>, 3: MethodHandles.*, 4: caller
++ // Note: This should be the only use of getCallerClass in this file.
++ return Reflection.getCallerClass(CALLER_DEPTH);
++ }
++
++ /**
++ * Produce a method handle for a static method.
++ * The type of the method handle will be that of the method.
++ * The method and all its argument types must be accessible to the lookup class.
++ * If the method's class has not yet been initialized, that is done
++ * immediately, before the method handle is returned.
++ * @param defc the class from which the method is accessed
++ * @param name the name of the method
++ * @param type the type of the method
++ * @return the desired method handle
++ * @exception SecurityException <em>TBD</em>
++ * @exception NoAccessException if the method does not exist or access checking fails
++ */
++ public
++ MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws NoAccessException {
++ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
++ checkStatic(true, method, lookupClass);
++ //throw NoSuchMethodException
++ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
++ }
++
++ /**
++ * Produce a method handle for a virtual method.
++ * The type of the method handle will be that of the method,
++ * with the receiver type ({@code defc}) prepended.
++ * The method and all its argument types must be accessible to the lookup class.
++ * <p>
++ * When called, the handle will treat the first argument as a receiver
++ * and dispatch on the receiver's type to determine which method
++ * implementation to enter.
++ * (The dispatching action is identical with that performed by an
++ * {@code invokevirtual} or {@code invokeinterface} instruction.)
++ * @param defc the class or interface from which the method is accessed
++ * @param name the name of the method
++ * @param type the type of the method, with the receiver argument omitted
++ * @return the desired method handle
++ * @exception SecurityException <em>TBD</em>
++ * @exception NoAccessException if the method does not exist or access checking fails
++ */
++ public MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws NoAccessException {
++ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), true, lookupClass);
++ checkStatic(false, method, lookupClass);
++ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
++ }
++
++ /**
++ * Produce an early-bound method handle for a virtual method,
++ * or a handle for a constructor, as if called from an {@code invokespecial}
++ * instruction from {@code caller}.
++ * The type of the method handle will be that of the method or constructor,
++ * with a suitably restricted receiver type (such as {@code caller}) prepended.
++ * The method or constructor and all its argument types must be accessible
++ * to the caller.
++ * <p>
++ * When called, the handle will treat the first argument as a receiver,
++ * but will not dispatch on the receiver's type.
++ * (This direct invocation action is identical with that performed by an
++ * {@code invokespecial} instruction.)
++ * <p>
++ * If the explicitly specified caller class is not identical with the
++ * lookup class, a security check TBD is performed.
++ * @param defc the class or interface from which the method is accessed
++ * @param name the name of the method, or "<init>" for a constructor
++ * @param type the type of the method, with the receiver argument omitted
++ * @param specialCaller the proposed calling class to perform the {@code invokespecial}
++ * @return the desired method handle
++ * @exception SecurityException <em>TBD</em>
++ * @exception NoAccessException if the method does not exist or access checking fails
++ */
++ public MethodHandle findSpecial(Class<?> defc, String name, MethodType type,
++ Class<?> specialCaller) throws NoAccessException {
++ checkSpecialCaller(specialCaller, lookupClass);
++ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), false, specialCaller);
++ checkStatic(false, method, lookupClass);
++ if (name.equals("<init>")) {
++ if (defc != specialCaller)
++ throw newNoAccessException("constructor must be local to lookup class", method, lookupClass);
++ } else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) {
++ throw newNoAccessException("method must be in a superclass of lookup class", method, lookupClass);
++ }
++ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, specialCaller);
++ }
++
++ /**
++ * Produce an early-bound method handle for a non-static method.
++ * The receiver must have a supertype {@code defc} in which a method
++ * of the given name and type is accessible to the lookup class.
++ * The method and all its argument types must be accessible to the lookup class.
++ * The type of the method handle will be that of the method.
++ * The given receiver will be bound into the method handle.
++ * <p>
++ * Equivalent to the following expression:
++ * <code>
++ * {@link #insertArgument}({@link #findVirtual}(defc, name, type), receiver)
++ * </code>
++ * @param receiver the object from which the method is accessed
++ * @param name the name of the method
++ * @param type the type of the method, with the receiver argument omitted
++ * @return the desired method handle
++ * @exception SecurityException <em>TBD</em>
++ * @exception NoAccessException if the method does not exist or access checking fails
++ */
++ public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException {
++ Class<? extends Object> rcvc = receiver.getClass(); // may get NPE
++ MemberName reference = new MemberName(rcvc, name, type);
++ MemberName method = IMPL_NAMES.resolveOrFail(reference, true, lookupClass);
++ checkStatic(false, method, lookupClass);
++ MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
++ MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
++ if (bmh == null)
++ throw newNoAccessException(method, lookupClass);
++ return bmh;
++ }
++
++ /**
++ * Make a direct method handle to <i>m</i>, if the lookup class has permission.
++ * If <i>m</i> is non-static, the receiver argument is treated as an initial argument.
++ * If <i>m</i> is virtual, overriding is respected on every call.
++ * Unlike the Core Reflection API, exceptions are <em>not</em> wrapped.
++ * The type of the method handle will be that of the method,
++ * with the receiver type prepended (but only if it is non-static).
++ * If the method's {@code accessible} flag is not set,
++ * access checking is performed immediately on behalf of the lookup class.
++ * If <i>m</i> is not public, do not share the resulting handle with untrusted parties.
++ * @param m the reflected method
++ * @return a method handle which can invoke the reflected method
++ * @exception NoAccessException if access checking fails
++ */
++ public MethodHandle unreflect(Method m) throws NoAccessException {
++ return unreflectImpl(new MemberName(m), m.isAccessible(), true, lookupClass);
++ }
++
++ /**
++ * Produce a method handle for a reflected method.
++ * It will bypass checks for overriding methods on the receiver,
++ * as if by the {@code invokespecial} instruction.
++ * The type of the method handle will be that of the method,
++ * with the receiver type prepended.
++ * If the method's {@code accessible} flag is not set,
++ * access checking is performed immediately on behalf of the lookup class,
++ * as if {@code invokespecial} instruction were being linked.
++ * @param m the reflected method
++ * @return a method handle which can invoke the reflected method
++ * @exception NoAccessException if access checking fails
++ */
++ public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws NoAccessException {
++ checkSpecialCaller(specialCaller, lookupClass);
++ MemberName mname = new MemberName(m);
++ checkStatic(false, mname, lookupClass);
++ return unreflectImpl(mname, m.isAccessible(), false, specialCaller);
++ }
++
++ /**
++ * Produce a method handle for a reflected constructor.
++ * The type of the method handle will be that of the constructor.
++ * The method handle will perform a {@code newInstance} operation,
++ * creating a new instance of the constructor's class on the
++ * arguments passed to the method handle.
++ * <p>
++ * If the constructor's {@code accessible} flag is not set,
++ * access checking is performed immediately on behalf of the lookup class,
++ * as if {@code invokespecial} instruction were being linked.
++ * @param ctor the reflected constructor
++ * @return a method handle which can invoke the reflected constructor
++ * @exception NoAccessException if access checking fails
++ */
++ public MethodHandle unreflectConstructor(Constructor ctor) throws NoAccessException {
++ MemberName m = new MemberName(ctor);
++ return unreflectImpl(m, ctor.isAccessible(), false, lookupClass);
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle giving read access to a reflected field.
++ * The type of the method handle will have a return type of the field's
++ * value type. Its sole argument will be the field's containing class
++ * (but only if it is non-static).
++ * If the method's {@code accessible} flag is not set,
++ * access checking is performed immediately on behalf of the lookup class.
++ * @param f the reflected field
++ * @return a method handle which can load values from the reflected field
++ * @exception NoAccessException if access checking fails
++ */
++ public MethodHandle unreflectGetter(Field f) throws NoAccessException {
++ return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), false, lookupClass);
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle giving write access to a reflected field.
++ * The type of the method handle will have a void return type.
++ * Its last argument will be the field's value type.
++ * Its other argument will be the field's containing class
++ * (but only if it is non-static).
++ * If the method's {@code accessible} flag is not set,
++ * access checking is performed immediately on behalf of the lookup class.
++ * @param f the reflected field
++ * @return a method handle which can store values into the reflected field
++ * @exception NoAccessException if access checking fails
++ */
++ public MethodHandle unreflectSetter(Field f) throws NoAccessException {
++ return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), true, lookupClass);
++ }
++
++ }
++
++ static /*must not be public*/
++ MethodHandle findStaticFrom(Class<?> lookupClass,
++ Class<?> defc, String name, MethodType type) throws NoAccessException {
++ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
++ checkStatic(true, method, lookupClass);
++ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
++ }
++
++ static void checkStatic(boolean wantStatic, MemberName m, Class<?> lookupClass) {
++ if (wantStatic != m.isStatic()) {
++ String message = wantStatic ? "expected a static method" : "expected a non-static method";
++ throw newNoAccessException(message, m, lookupClass);
++ }
++ }
++
++ static void checkSpecialCaller(Class<?> specialCaller, Class<?> lookupClass) {
++ if (lookupClass == Lookup.IMPL_LOOKUP.lookupClass())
++ return; // privileged action
++ if (lookupClass == null || // public-only access
++ !VerifyAccess.isSamePackageMember(specialCaller, lookupClass))
++ throw newNoAccessException("no private access", new MemberName(specialCaller), lookupClass);
++ }
++
++ // Helper for creating handles on reflected methods and constructors.
++ static MethodHandle unreflectImpl(MemberName m, boolean isAccessible,
++ boolean doDispatch, Class<?> lookupClass) {
++ MethodType mtype = m.getInvocationType();
++ Class<?> defc = m.getDeclaringClass();
++ int mods = m.getModifiers();
++ if (m.isStatic()) {
++ if (!isAccessible &&
++ VerifyAccess.isAccessible(defc, mods, false, lookupClass) == null)
++ throw newNoAccessException(m, lookupClass);
++ } else {
++ Class<?> constraint;
++ if (isAccessible) {
++ // abbreviated access check for "unlocked" method
++ constraint = doDispatch ? defc : lookupClass;
++ } else {
++ constraint = VerifyAccess.isAccessible(defc, mods, doDispatch, lookupClass);
++ }
++ if (constraint != defc && !constraint.isAssignableFrom(defc)) {
++ if (!defc.isAssignableFrom(constraint))
++ throw newNoAccessException("receiver must be in caller class", m, lookupClass);
++ mtype = mtype.changeParameterType(0, constraint);
++ }
++ }
++ return MethodHandleImpl.findMethod(IMPL_TOKEN, m, doDispatch, lookupClass);
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle giving read access to elements of an array.
++ * The type of the method handle will have a return type of the array's
++ * element type. Its first argument will be the array type,
++ * and the second will be {@code int}.
++ * @param arrayClass an array type
++ * @return a method handle which can load values from the given array type
++ * @throws IllegalArgumentException if arrayClass is not an array type
++ */
++ public static
++ MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
++ return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, false);
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle giving write access to elements of an array.
++ * The type of the method handle will have a void return type.
++ * Its last argument will be the array's element type.
++ * The first and second arguments will be the array type and int.
++ * @return a method handle which can store values into the array type
++ * @throws IllegalArgumentException if arrayClass is not an array type
++ */
++ public static
++ MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
++ return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, true);
++ }
++
++
++ /// method handle invocation (reflective style)
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Call the {@code invoke} method of a given method handle,
++ * with arguments that exactly match the parameter types of the method handle.
++ * The length of the arguments array must equal the parameter count
++ * of the target's type.
++ * The arguments array is spread into separate arguments, and
++ * basic reference and unboxing conversions are applied.
++ * <p>
++ * In order to match the type of the target, the following argument
++ * conversions are applied as necessary:
++ * <ul>
++ * <li>reference casting
++ * <li>unboxing
++ * </ul>
++ * The following conversions are not applied:
++ * <ul>
++ * <li>primitive conversions (e.g., {@code byte} to {@code int}
++ * <li>varargs conversions other than the initial spread
++ * <li>any application-specific conversions (e.g., string to number)
++ * </ul>
++ * The result returned by the call is boxed if it is a primitive,
++ * or forced to null if the return type is void.
++ * <p>
++ * This call is a convenience method for the following code:
++ * <pre>
++ * MethodHandle invoker = MethodHandles.genericInvoker(target.type(), 0, true);
++ * Object result = invoker.invoke(arguments);
++ * </pre>
++ * @param target the method handle to invoke
++ * @param arguments the arguments to pass to the target
++ * @return the result returned by the target
++ */
++ public static
++ Object invoke(MethodHandle target, Object... arguments) {
++ int argc = arguments == null ? 0 : arguments.length;
++ MethodType type = target.type();
++ if (argc <= 4) {
++ MethodHandle invoker = invokers(type).genericInvoker();
++ switch (argc) {
++ case 0: return invoker.<Object>invoke(target);
++ case 1: return invoker.<Object>invoke(target,
++ arguments[0]);
++ case 2: return invoker.<Object>invoke(target,
++ arguments[0], arguments[1]);
++ case 3: return invoker.<Object>invoke(target,
++ arguments[0], arguments[1], arguments[2]);
++ case 4: return invoker.<Object>invoke(target,
++ arguments[0], arguments[1], arguments[2], arguments[3]);
++ }
++ }
++ MethodHandle invoker = invokers(type).varargsInvoker();
++ return invoker.<Object>invoke(target, arguments);
++ }
++
++ public static
++ Object invoke_0(MethodHandle target) {
++ MethodHandle invoker = invokers(target.type()).genericInvoker();
++ return invoker.<Object>invoke(target);
++ }
++ public static
++ Object invoke_1(MethodHandle target, Object a0) {
++ MethodHandle invoker = invokers(target.type()).genericInvoker();
++ return invoker.<Object>invoke(target, a0);
++ }
++ public static
++ Object invoke_2(MethodHandle target, Object a0, Object a1) {
++ MethodHandle invoker = invokers(target.type()).genericInvoker();
++ return invoker.<Object>invoke(target, a0, a1);
++ }
++ public static
++ Object invoke_3(MethodHandle target, Object a0, Object a1, Object a2) {
++ MethodHandle invoker = invokers(target.type()).genericInvoker();
++ return invoker.<Object>invoke(target, a0, a1, a2);
++ }
++ public static
++ Object invoke_4(MethodHandle target, Object a0, Object a1, Object a2, Object a3) {
++ MethodHandle invoker = invokers(target.type()).genericInvoker();
++ return invoker.<Object>invoke(target, a0, a1, a2, a3);
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Give a method handle which will invoke any method handle of the
++ * given type on a standard set of {@code Object} type arguments.
++ * The the resulting invoker will be a method handle with the following
++ * arguments:
++ * <ul>
++ * <li>a single {@code MethodHandle} target
++ * <li>zero or more {@code Object} values
++ * <li>an optional {@code Object[]} array containing more arguments
++ * </ul>
++ * The invoker will spread the varargs array (if present), apply
++ * reference casts as necessary, and unbox primitive arguments.
++ * The return value of the invoker will be an {@code Object} reference,
++ * boxing a primitive value if the original type returns a primitive,
++ * and always null if the original type returns void.
++ * <p>
++ * This is a convenience method equivalent to the following code:
++ * <pre>
++ * MethodHandle invoker = exactInvoker(type);
++ * MethodType genericType = MethodType.makeGeneric(objectArgCount, varargs);
++ * genericType = genericType.insertParameterType(0, MethodHandle.class);
++ * if (!varargs)
++ * return convertArguments(invoker, genericType);
++ * else
++ * return spreadArguments(invoker, genericType);
++ * </pre>
++ * @param type the desired target type
++ * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments
++ * @param varargs if true, the invoker will accept a final {@code Object[]} argument
++ * @return a method handle suitable for invoking any method handle of the given type
++ */
++ static public
++ MethodHandle genericInvoker(MethodType type, int objectArgCount, boolean varargs) {
++ return invokers(type).genericInvoker();
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Give a method handle which will take a invoke any method handle of the
++ * given type. The resulting invoker will have a type which is
++ * exactly equal to the desired type, except that it will accept
++ * an additional leading argument of type {@code MethodHandle}.
++ * <p>
++ * This is a convenience method equivalent to the following code:
++ * <pre>
++ * MethodHandles.lookup().findVirtual(MethodHandle.class, "invoke", type);
++ * </pre>
++ * @param type the desired target type
++ * @return a method handle suitable for invoking any method handle of the given type
++ */
++ static public
++ MethodHandle exactInvoker(MethodType type) {
++ return invokers(type).exactInvoker();
++ }
++
++ static private Invokers invokers(MethodType type) {
++ return MethodTypeImpl.invokers(IMPL_TOKEN, type);
++ }
++
++ /**
++ * <em>WORK IN PROGRESS:</em>
++ * Perform value checking, exactly as if for an adapted method handle.
++ * It is assumed that the given value is either null, of type T0,
++ * or (if T0 is primitive) of the wrapper type corresponding to T0.
++ * The following checks and conversions are made:
++ * <ul>
++ * <li>If T0 and T1 are references, then a cast to T1 is applied.
++ * (The types do not need to be related in any particular way.)
++ * <li>If T0 and T1 are primitives, then a widening or narrowing
++ * conversion is applied, if one exists.
++ * <li>If T0 is a primitive and T1 a reference, and
++ * T0 has a wrapper type TW, a boxing conversion to TW is applied,
++ * possibly followed by a reference conversion.
++ * T1 must be TW or a supertype.
++ * <li>If T0 is a reference and T1 a primitive, and
++ * T1 has a wrapper type TW, an unboxing conversion is applied,
++ * possibly preceded by a reference conversion.
++ * T0 must be TW or a supertype.
++ * <li>If T1 is void, the return value is discarded
++ * <li>If T0 is void and T1 a reference, a null value is introduced.
++ * <li>If T0 is void and T1 a primitive, a zero value is introduced.
++ * </ul>
++ * If the value is discarded, null will be returned.
++ * @param valueType
++ * @param value
++ * @return the value, converted if necessary
++ * @throws java.lang.ClassCastException if a cast fails
++ */
++ static
++ <T0, T1> T1 checkValue(Class<T0> t0, Class<T1> t1, Object value)
++ throws ClassCastException
++ {
++ if (t0 == t1) {
++ // no conversion needed; just reassert the same type
++ if (t0.isPrimitive())
++ return Wrapper.asPrimitiveType(t1).cast(value);
++ else
++ return Wrapper.OBJECT.cast(value, t1);
++ }
++ boolean prim0 = t0.isPrimitive(), prim1 = t1.isPrimitive();
++ if (!prim0) {
++ // check contract with caller
++ Wrapper.OBJECT.cast(value, t0);
++ if (!prim1) {
++ return Wrapper.OBJECT.cast(value, t1);
++ }
++ // convert reference to primitive by unboxing
++ Wrapper w1 = Wrapper.forPrimitiveType(t1);
++ return w1.cast(value, t1);
++ }
++ // check contract with caller:
++ Wrapper.asWrapperType(t0).cast(value);
++ Wrapper w1 = Wrapper.forPrimitiveType(t1);
++ return w1.cast(value, t1);
++ }
++
++ static
++ Object checkValue(Class<?> T1, Object value)
++ throws ClassCastException
++ {
++ Class<?> T0;
++ if (value == null)
++ T0 = Object.class;
++ else
++ T0 = value.getClass();
++ return checkValue(T0, T1, value);
++ }
++
++ /// method handle modification (creation from other method handles)
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle which adapts the type of the
++ * given method handle to a new type, by pairwise argument conversion,
++ * and/or varargs conversion.
++ * The original type and new type must have the same number of
++ * arguments, or else one or both them the must be varargs types.
++ * The resulting method handle is guaranteed to confess a type
++ * which is equal to the desired new type, with any varargs property erased.
++ * <p>
++ * If the original type and new type are equal, returns target.
++ * <p>
++ * The following conversions are applied as needed both to
++ * arguments and return types. Let T0 and T1 be the differing
++ * new and old parameter types (or old and new return types)
++ * for corresponding values passed by the new and old method types.
++ * <p>
++ * If an ordinary (non-varargs) parameter of the new type is
++ * to be boxed in a varargs parameter of the old type of type T1[],
++ * then T1 is the element type of the varargs array.
++ * Otherwise, if a varargs parameter of the new type of type T0[]
++ * is to be spread into one or more outgoing old type parameters,
++ * then T0 is the element type of the
++ * If the new type is varargs and the old type is not, the varargs
++ * argument will be checked and must be a non-null array of exactly
++ * the right length. If there are no parameters in the old type
++ * corresponding to the new varargs parameter, the varargs argument
++ * is also allowed to be null.
++ * <p>
++ * Given those types T0, T1, one of the following conversions is applied
++ * if possible:
++ * <ul>
++ * <li>If T0 and T1 are references, then a cast to T2 is applied,
++ * where T2 is Object if T1 is an interface, else T1.
++ * (The types do not need to be related in any particular way.
++ * The treatment of interfaces follows the usage of the bytecode verifier.)
++ * <li>If T0 and T1 are primitives, then a Java casting
++ * conversion (JLS 5.5) is applied, if one exists.
++ * <li>If T0 and T1 are primitives and one is boolean,
++ * the boolean is treated as a one-bit unsigned integer.
++ * (This treatment follows the usage of the bytecode verifier.)
++ * A conversion from another primitive type behaves as if
++ * it first converts to byte, and then masks all but the low bit.
++ * <li>If T0 is a primitive and T1 a reference, a boxing
++ * conversion is applied if one exists, possibly followed by
++ * an reference conversion to a superclass.
++ * T1 must be a wrapper class or a supertype of one.
++ * If T1 is a wrapper class, T0 is converted if necessary
++ * to T1's primitive type by one of the preceding conversions.
++ * Otherwise, T0 is boxed, and its wrapper converted to T1.
++ * <li>If T0 is a reference and T1 a primitive, an unboxing
++ * conversion is applied if one exists, possibly preceded by
++ * a reference conversion to a wrapper class.
++ * T0 must be a wrapper class or a supertype of one.
++ * If T0 is a wrapper class, its primitive value is converted
++ * if necessary to T1 by one of the preceding conversions.
++ * Otherwise, T0 is converted directly to the wrapper type for T1,
++ * which is then unboxed.
++ * <li>If T1 is void, any returned value is discarded
++ * <li>If T0 is void and T1 a reference, a null value is introduced.
++ * <li>If T0 is void and T1 a primitive, a zero value is introduced.
++ * </ul>
++ * @param target the method handle to invoke after arguments are retyped
++ * @param newType the expected type of the new method handle
++ * @return a method handle which delegates to {@code target} after performing
++ * any necessary argument conversions, and arranges for any
++ * necessary return value conversions
++ * @throws WrongMethodTypeException if the conversion cannot be made
++ */
++ public static
++ MethodHandle convertArguments(MethodHandle target, MethodType newType) {
++ MethodType oldType = target.type();
++ if (oldType.equals(newType))
++ return target;
++ MethodHandle res = MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
++ newType, oldType, null);
++ if (res == null)
++ throw newIllegalArgumentException("cannot convert to "+newType+": "+target);
++ return res;
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle which adapts the calling sequence of the
++ * given method handle to a new type, by reordering the arguments.
++ * The resulting method handle is guaranteed to confess a type
++ * which is equal to the desired new type.
++ * <p>
++ * The given array controls the reordering.
++ * Call {@code #I} the number of incoming parameters (the value
++ * {@code newType.parameterCount()}, and call {@code #O} the number
++ * of outgoing parameters (the value {@code target.type().parameterCount()}).
++ * Then the length of the reordering array must be {@code #O},
++ * and each element must be a non-negative number less than {@code #I}.
++ * For every {@code N} less than {@code #O}, the {@code N}-th
++ * outgoing argument will be taken from the {@code I}-th incoming
++ * argument, where {@code I} is {@code reorder[N]}.
++ * <p>
++ * The reordering array need not specify an actual permutation.
++ * An incoming argument will be duplicated if its index appears
++ * more than once in the array, and an incoming argument will be dropped
++ * if its index does not appear in the array.
++ * <p>
++ * Pairwise conversions are applied as needed to arguments and return
++ * values, as with {@link #convertArguments}.
++ * @param target the method handle to invoke after arguments are reordered
++ * @param newType the expected type of the new method handle
++ * @param reorder a string which controls the reordering
++ * @return a method handle which delegates to {@code target} after performing
++ * any necessary argument motion and conversions, and arranges for any
++ * necessary return value conversions
++ */
++ public static
++ MethodHandle permuteArguments(MethodHandle target, MethodType newType, int[] reorder) {
++ MethodType oldType = target.type();
++ checkReorder(reorder, newType, oldType);
++ return MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
++ newType, oldType,
++ reorder);
++ }
++
++ private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
++ if (reorder.length == oldType.parameterCount()) {
++ int limit = newType.parameterCount();
++ boolean bad = false;
++ for (int i : reorder) {
++ if (i < 0 || i >= limit) {
++ bad = true; break;
++ }
++ }
++ if (!bad) return;
++ }
++ throw newIllegalArgumentException("bad reorder array");
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle which adapts the type of the
++ * given method handle to a new type, by spreading the final argument.
++ * The resulting method handle is guaranteed to confess a type
++ * which is equal to the desired new type.
++ * <p>
++ * The final parameter type of the new type must be an array type T[].
++ * This is the type of what is called the <i>spread</i> argument.
++ * All other arguments of the new type are called <i>ordinary</i> arguments.
++ * <p>
++ * The ordinary arguments of the new type are pairwise converted
++ * to the initial parameter types of the old type, according to the
++ * rules in {@link #convertArguments}.
++ * Any additional arguments in the old type
++ * are converted from the array element type T,
++ * again according to the rules in {@link #convertArguments}.
++ * The return value is converted according likewise.
++ * <p>
++ * The call verifies that the spread argument is in fact an array
++ * of exactly the type length, i.e., the excess number of
++ * arguments in the old type over the ordinary arguments in the new type.
++ * If there are no excess arguments, the spread argument is also
++ * allowed to be null.
++ * @param target the method handle to invoke after the argument is prepended
++ * @param newType the expected type of the new method handle
++ * @return a new method handle which spreads its final argument,
++ * before calling the original method handle
++ */
++ public static
++ MethodHandle spreadArguments(MethodHandle target, MethodType newType) {
++ MethodType oldType = target.type();
++ int inargs = newType.parameterCount();
++ int outargs = oldType.parameterCount();
++ int spreadPos = inargs - 1;
++ int numSpread = (outargs - spreadPos);
++ MethodHandle res = null;
++ if (spreadPos >= 0 && numSpread >= 0) {
++ res = MethodHandleImpl.spreadArguments(IMPL_TOKEN, target, newType, spreadPos);
++ }
++ if (res == null) {
++ throw newIllegalArgumentException("cannot spread "+newType+" to " +oldType);
++ }
++ return res;
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle which adapts the type of the
++ * given method handle to a new type, by collecting a series of
++ * trailing arguments into an array.
++ * The resulting method handle is guaranteed to confess a type
++ * which is equal to the desired new type.
++ * <p>
++ * This method is inverse to {@link #spreadArguments}.
++ * The final parameter type of the old type must be an array type T[],
++ * which is the type of what is called the <i>spread</i> argument.
++ * The trailing arguments of the new type which correspond to
++ * the spread argument are all converted to type T and collected
++ * into an array before the original method is called.
++ * <p>
++ * ISSUE: Unify this with combineArguments. CollectArguments
++ * is combineArguments with (a) new Object[]{...} as a combiner,
++ * and (b) the combined arguments dropped, in favor of the combined result.
++ * @param target the method handle to invoke after the argument is prepended
++ * @param newType the expected type of the new method handle
++ * @return a new method handle which collects some trailings argument
++ * into an array, before calling the original method handle
++ */
++ public static
++ MethodHandle collectArguments(MethodHandle target, MethodType newType) {
++ MethodType oldType = target.type();
++ int inargs = newType.parameterCount();
++ int outargs = oldType.parameterCount();
++ int collectPos = outargs - 1;
++ int numCollect = (inargs - collectPos);
++ if (collectPos < 0 || numCollect < 0)
++ throw newIllegalArgumentException("wrong number of arguments");
++ return MethodHandleImpl.collectArguments(IMPL_TOKEN, target, newType, collectPos);
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle which calls the original method handle,
++ * after inserting the given argument at the given position.
++ * The type of the new method handle will drop the corresponding argument
++ * type from the original handle's type.
++ * <p>
++ * The given argument object must match the dropped argument type.
++ * If the dropped argument type is a primitive, the argument object
++ * must be a wrapper, and is unboxed to produce the primitive.
++ * <p>
++ * The <i>pos</i> may range between zero and <i>N</i> (inclusively),
++ * where <i>N</i> is the number of argument types in <i>target</i>,
++ * meaning to insert the new argument as the first or last (respectively),
++ * or somewhere in between.
++ * @param target the method handle to invoke after the argument is inserted
++ * @param pos where to insert the argument (zero for the first)
++ * @param value the argument to insert
++ * @return a new method handle which inserts an additional argument,
++ * before calling the original method handle
++ */
++ public static
++ MethodHandle insertArgument(MethodHandle target, int pos, Object value) {
++ MethodType oldType = target.type();
++ ArrayList<Class<?>> ptypes =
++ new ArrayList<Class<?>>(oldType.parameterList());
++ int outargs = oldType.parameterCount();
++ int inargs = outargs - 1;
++ if (pos < 0 || pos >= outargs)
++ throw newIllegalArgumentException("no argument type to append");
++ Class<?> valueType = ptypes.remove(pos);
++ value = checkValue(valueType, value);
++ if (pos == 0 && !valueType.isPrimitive()) {
++ // At least for now, make bound method handles a special case.
++ // This lets us get by with minimal JVM support, at the expense
++ // of generating signature-specific adapters as Java bytecodes.
++ MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, target, value);
++ if (bmh != null) return bmh;
++ // else fall through to general adapter machinery
++ }
++ return MethodHandleImpl.bindArgument(IMPL_TOKEN, target, pos, value);
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Produce a method handle which calls the original method handle,
++ * after dropping the given argument(s) at the given position.
++ * The type of the new method handle will insert the given argument
++ * type(s), at that position, into the original handle's type.
++ * <p>
++ * The <i>pos</i> may range between zero and <i>N-1</i>,
++ * where <i>N</i> is the number of argument types in <i>target</i>,
++ * meaning to drop the first or last argument (respectively),
++ * or an argument somewhere in between.
++ * @param target the method handle to invoke after the argument is dropped
++ * @param valueTypes the type(s) of the argument to drop
++ * @param pos which argument to drop (zero for the first)
++ * @return a new method handle which drops an argument of the given type,
++ * before calling the original method handle
++ */
++ public static
++ MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
++ if (valueTypes.length == 0) return target;
++ MethodType oldType = target.type();
++ int outargs = oldType.parameterCount();
++ int inargs = outargs + valueTypes.length;
++ if (pos < 0 || pos >= inargs)
++ throw newIllegalArgumentException("no argument type to remove");
++ ArrayList<Class<?>> ptypes =
++ new ArrayList<Class<?>>(oldType.parameterList());
++ ptypes.addAll(pos, Arrays.asList(valueTypes));
++ MethodType newType = MethodType.make(oldType.returnType(), ptypes);
++ return MethodHandleImpl.dropArguments(IMPL_TOKEN, target, newType, pos);
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Make a method handle which adapts a target method handle,
++ * by guarding it with a test, a boolean-valued method handle.
++ * If the guard fails, a fallback handle is called instead.
++ * All three method handles must have the same corresponding
++ * argument and return types, except that the return type
++ * of the test must be boolean.
++ * <p> Here is pseudocode for the resulting adapter:
++ * <blockquote><pre>
++ * signature T(A...);
++ * boolean test(A...);
++ * T target(A...);
++ * T fallback(A...);
++ * T adapter(A... a) {
++ * if (test(a...))
++ * return target(a...);
++ * else
++ * return fallback(a...);
++ * }
++ * </pre></blockquote>
++ * @param test method handle used for test, must return boolean
++ * @param target method handle to call if test passes
++ * @param fallback method handle to call if test fails
++ * @return method handle which incorporates the specified if/then/else logic
++ * @throws IllegalArgumentException if {@code test} does not return boolean,
++ * or if all three method types do not match (with the return
++ * type of {@code test} changed to match that of {@code target}).
++ */
++ public static
++ MethodHandle guardWithTest(MethodHandle test,
++ MethodHandle target,
++ MethodHandle fallback) {
++ if (target.type() != fallback.type())
++ throw newIllegalArgumentException("target and fallback types do not match");
++ if (target.type().changeReturnType(boolean.class) != test.type())
++ throw newIllegalArgumentException("target and test types do not match");
++ /* {
++ MethodHandle invoke = findVirtual(MethodHandle.class, "invoke", target.type());
++ static MethodHandle choose(boolean z, MethodHandle t, MethodHandle f) {
++ return z ? t : f;
++ }
++ static MethodHandle compose(MethodHandle f, MethodHandle g) {
++ Class<?> initargs = g.type().parameterArray();
++ f = dropArguments(f, 1, initargs); // ignore 2nd copy of args
++ return combineArguments(f, g);
++ }
++ // choose = \z.(z ? target : fallback)
++ MethodHandle choose = findVirtual(MethodHandles.class, "choose",
++ MethodType.make(boolean.class, MethodHandle.class, MethodHandle.class));
++ choose = appendArgument(choose, target);
++ choose = appendArgument(choose, fallback);
++ MethodHandle dispatch = compose(choose, test);
++ // dispatch = \(a...).(test(a...) ? target : fallback)
++ return combineArguments(invoke, dispatch, 0);
++ // return \(a...).((test(a...) ? target : fallback).invoke(a...))
++ } */
++ return MethodHandleImpl.makeGuardWithTest(IMPL_TOKEN, test, target, fallback);
++ }
++
++ /**
++ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
++ * Adapt a target method handle {@code target} by first processing
++ * its arguments, and then calling the target.
++ * The initial processing is performed by a second method handle, the {@code combiner}.
++ * After this, control passes to the {@code target}, with the same arguments.
++ * <p>
++ * The return value of the {@code combiner} is inserted into the argument list
++ * for the {@code target} at the indicated position {@code pos}, if it is non-negative.
++ * Except for this inserted argument (if any), the argument types of
++ * the target {@code target} and the {@code combiner} must be identical.
++ * <p>
++ * (Note that {@link #dropArguments} can be used to remove any arguments
++ * that either the {@code combiner} or {@code target} does not wish to receive.)
++ * <p>
++ * The combiner handle must have the same argument types as the
++ * target handle, but must return {@link MethodHandle} instead of
++ * the ultimate return type. The returned method handle, in turn,
++ * is required to have exactly the given final method type.
++ * <p> Here is pseudocode for the resulting adapter:
++ * <blockquote><pre>
++ * signature V(A[pos]..., B...);
++ * signature T(A[pos]..., V, B...);
++ * T target(A... a, V v, B... b);
++ * V combiner(A..., B...);
++ * T adapter(A... a, B... b) {
++ * V v = combiner(a..., b...);
++ * return target(a..., v, b...);
++ * }
++ * </pre></blockquote>
++ * @param target the method handle to invoke after arguments are combined
++ * @param pos where the return value of {@code combiner} is to
++ * be inserted as an argument to {@code target}
++ * @param combiner method handle to call initially on the incoming arguments
++ * @return method handle which incorporates the specified dispatch logic
++ * @throws IllegalArgumentException if {@code combiner} does not itself
++ * return either void or the {@code pos}-th argument of {@code target},
++ * or does not have the same argument types as {@code target}
++ * (minus the inserted argument)
++ */
++ public static
++ MethodHandle combineArguments(MethodHandle target, int pos, MethodHandle combiner) {
++ MethodType mhType = target.type();
++ Class<?> combineType = combiner.type().returnType();
++ MethodType incomingArgs;
++ if (pos < 0) {
++ // No inserted argument; target & combiner must have same argument types.
++ incomingArgs = mhType;
++ if (!incomingArgs.changeReturnType(combineType).equals(combiner.type()))
++ throw newIllegalArgumentException("target and combiner types do not match");
++ } else {
++ // Inserted argument.
++ if (pos >= mhType.parameterCount()
++ || mhType.parameterType(pos) != combineType)
++ throw newIllegalArgumentException("inserted combiner argument does not match target");
++ incomingArgs = mhType.dropParameterType(pos);
++ }
++ if (!incomingArgs.changeReturnType(combineType).equals(combiner.type())) {
++ throw newIllegalArgumentException("target and combiner types do not match");
++ }
++ return MethodHandleImpl.combineArguments(IMPL_TOKEN, target, combiner, pos);
++ }
++
++}
+diff --git a/src/share/classes/java/dyn/MethodType.java b/src/share/classes/java/dyn/MethodType.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/MethodType.java
+@@ -0,0 +1,575 @@
++/*
++ * Copyright 2008-2009 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;
++
++import java.util.Arrays;
++import java.util.Collections;
++import java.util.HashMap;
++import java.util.List;
++import sun.dyn.Access;
++import sun.dyn.Invokers;
++import sun.dyn.MethodTypeImpl;
++import sun.dyn.util.BytecodeSignature;
++import static sun.dyn.MemberName.newIllegalArgumentException;
++
++/**
++ * Run-time token used to match call sites with method handles.
++ * The structure is a return type accompanied by any number of parameter types.
++ * The types (primitive, void, and reference) are represented by Class objects.
++ * All instances of <code>MethodType</code> are immutable.
++ * Two instances are completely interchangeable if they compare equal.
++ * Equality depends exactly on the return and parameter types.
++ * <p>
++ * This type can be created only by factory methods, which manage interning.
++ *
++ * @author John Rose, JSR 292 EG
++ */
++public final
++class MethodType {
++ private final Class<?> rtype;
++ private final Class<?>[] ptypes;
++ private MethodTypeForm form; // erased form, plus cached data about primitives
++ private MethodType wrapAlt; // alternative wrapped/unwrapped version
++ private Invokers invokers; // cache of handy higher-order adapters
++
++ private static final Access IMPL_TOKEN = Access.getToken();
++
++ // share a cache with a friend in this package
++ Invokers getInvokers() { return invokers; }
++ void setInvokers(Invokers inv) { invokers = inv; }
++
++ static {
++ // This hack allows the implementation package special access to
++ // the internals of MethodType. In particular, the Form 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; }
++ public MethodTypeImpl form(MethodType mt) { return mt.form; }
++ public void setForm(MethodType mt, MethodTypeImpl form) {
++ assert(mt.form == null);
++ mt.form = (MethodTypeForm) form;
++ }
++ public MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
++ return MethodType.makeImpl(rtype, ptypes, trusted);
++ }
++ public MethodTypeImpl newMethodTypeForm(MethodType mt) {
++ return new MethodTypeForm(mt);
++ }
++ public Invokers getInvokers(MethodType mt) { return mt.invokers; }
++ public void setInvokers(MethodType mt, Invokers inv) { mt.invokers = inv; }
++ });
++ }
++
++ private MethodType(Class<?> rtype, Class<?>[] ptypes) {
++ checkRtype(rtype);
++ checkPtypes(ptypes);
++ this.rtype = rtype;
++ this.ptypes = ptypes;
++ }
++
++ private void checkRtype(Class<?> rtype) {
++ rtype.equals(rtype); // null check
++ }
++ private void checkPtypes(Class<?>[] ptypes) {
++ for (Class<?> ptype : ptypes) {
++ ptype.equals(ptype); // null check
++ if (ptype == void.class)
++ throw newIllegalArgumentException("void parameter: "+this);
++ }
++ }
++
++ static final HashMap<MethodType,MethodType> internTable
++ = new HashMap<MethodType, MethodType>();
++
++ static final Class<?>[] NO_PTYPES = {};
++
++ /** Find or create an instance of the given method type.
++ * @param rtype the return type
++ * @param ptypes the parameter types
++ * @return the interned method type with the given parts
++ * @throws NullPointerException if rtype or any ptype is null
++ * @throws IllegalArgumentException if any of the ptypes is void
++ */
++ public static
++ MethodType make(Class<?> rtype, Class<?>[] ptypes) {
++ return makeImpl(rtype, ptypes, false);
++ }
++
++ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. */
++ public static
++ MethodType make(Class<?> rtype, List<? extends Class<?>> ptypes) {
++ return makeImpl(rtype, ptypes.toArray(NO_PTYPES), true);
++ }
++
++ /** Convenience method for {@link #make(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) {
++ Class<?>[] ptypes1 = new Class<?>[1+ptypes.length];
++ ptypes1[0] = ptype0;
++ System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length);
++ return makeImpl(rtype, ptypes1, true);
++ }
++
++ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
++ * The resulting method has no parameter types.
++ */
++ public static
++ MethodType make(Class<?> rtype) {
++ return makeImpl(rtype, NO_PTYPES, true);
++ }
++
++ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
++ * The resulting method has the single given parameter type.
++ */
++ public static
++ MethodType make(Class<?> rtype, Class<?> ptype0) {
++ return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
++ }
++
++ /** Convenience method for {@link #make(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) {
++ return makeImpl(rtype, ptypes.ptypes, true);
++ }
++
++ /**
++ * Sole factory method to find or create an interned method type.
++ * @param rtype desired return type
++ * @param ptypes desired parameter types
++ * @param trusted whether the ptypes can be used without cloning
++ * @return the unique method type of the desired structure
++ */
++ private static
++ MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
++ if (ptypes == null || ptypes.length == 0) {
++ ptypes = NO_PTYPES; trusted = true;
++ }
++ MethodType mt1 = new MethodType(rtype, ptypes);
++ MethodType mt0;
++ synchronized (internTable) {
++ mt0 = internTable.get(mt1);
++ if (mt0 != null)
++ return mt0;
++ }
++ if (!trusted)
++ // defensively copy the array passed in by the user
++ mt1 = new MethodType(rtype, ptypes.clone());
++ // promote the object to the Real Thing, and reprobe
++ MethodTypeImpl.initForm(IMPL_TOKEN, mt1);
++ synchronized (internTable) {
++ mt0 = internTable.get(mt1);
++ if (mt0 != null)
++ return mt0;
++ internTable.put(mt1, mt1);
++ }
++ return mt1;
++ }
++
++ // Entry point from JVM. TODO: Change the name & signature.
++ private static MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes,
++ boolean ignore1, boolean ignore2) {
++ return makeImpl(rtype, ptypes, true);
++ }
++
++ 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.
++ * @param objectArgCount number of parameters (excluding the varargs parameter if any)
++ * @param varargs whether there will be a varargs parameter, of type Object[]
++ * @return a totally generic method type, given only its count of parameters and varargs
++ * @see #makeGeneric(int)
++ */
++ public static
++ MethodType makeGeneric(int objectArgCount, boolean varargs) {
++ MethodType mt;
++ int ivarargs = (!varargs ? 0 : 1);
++ int ootIndex = objectArgCount*2 + ivarargs;
++ if (ootIndex < objectOnlyTypes.length) {
++ mt = objectOnlyTypes[ootIndex];
++ if (mt != null) return mt;
++ }
++ Class<?>[] ptypes = new Class<?>[objectArgCount + ivarargs];
++ Arrays.fill(ptypes, Object.class);
++ if (ivarargs != 0) ptypes[objectArgCount] = Object[].class;
++ mt = makeImpl(Object.class, ptypes, true);
++ if (ootIndex < objectOnlyTypes.length) {
++ objectOnlyTypes[ootIndex] = mt; // cache it here also!
++ }
++ return mt;
++ }
++
++ /**
++ * 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)
++ */
++ public static
++ MethodType makeGeneric(int objectArgCount) {
++ return makeGeneric(objectArgCount, false);
++ }
++
++ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
++ * @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
++ */
++ public MethodType changeParameterType(int num, Class<?> nptype) {
++ if (parameterType(num) == nptype) return this;
++ Class<?>[] nptypes = ptypes.clone();
++ nptypes[num] = nptype;
++ 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
++ */
++ public MethodType insertParameterType(int num, Class<?> nptype) {
++ int len = ptypes.length;
++ Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+1);
++ System.arraycopy(nptypes, num, nptypes, num+1, len-num);
++ nptypes[num] = nptype;
++ return makeImpl(rtype, nptypes, true);
++ }
++
++ /** Convenience method for {@link #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
++ */
++ public MethodType dropParameterType(int num) {
++ int len = ptypes.length;
++ Class<?>[] nptypes;
++ if (num == 0) {
++ nptypes = Arrays.copyOfRange(ptypes, 1, len);
++ } else {
++ nptypes = Arrays.copyOfRange(ptypes, 0, len-1);
++ System.arraycopy(ptypes, num+1, nptypes, num, (len-1)-num);
++ }
++ return makeImpl(rtype, nptypes, true);
++ }
++
++ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
++ * @param nrtype a return parameter type to replace the old one with
++ * @return the same type, except with the return type change
++ */
++ public MethodType changeReturnType(Class<?> nrtype) {
++ if (returnType() == nrtype) return this;
++ return makeImpl(nrtype, ptypes, true);
++ }
++
++ /** Convenience method.
++ * Report if this type contains a primitive argument or return value.
++ * @return true if any of the types are primitives
++ */
++ public boolean hasPrimitives() {
++ return form.hasPrimitives();
++ }
++
++ /** Convenience method.
++ * Report if this type contains a wrapper argument or return value.
++ * Wrappers are types which box primitive values, such as {@link Integer}.
++ * @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.
++ * @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.
++ * @return a version of the original type with all types replaced
++ */
++ public MethodType generic() {
++ return makeGeneric(parameterCount());
++ }
++
++ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
++ * Convert all primitive types to their corresponding wrapper types.
++ * A {@code void} return type is changed to the type {@code java.lang.Void}.
++ * @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[])}.
++ * Convert all wrapper types to their corresponding primitive types.
++ * A return type of {@java.lang.Void} is changed to {@code void}.
++ * @return a version of the original type with all wrapper types replaced
++ */
++ public MethodType unwrap() {
++ MethodType noprims = !hasPrimitives() ? this : wrapWithPrims(this);
++ return unwrapWithNoPrims(noprims);
++ }
++
++ private static MethodType wrapWithPrims(MethodType pt) {
++ assert(pt.hasPrimitives());
++ MethodType wt = pt.wrapAlt;
++ if (wt == null) {
++ // fill in lazily
++ wt = MethodTypeImpl.canonicalize(pt, MethodTypeImpl.WRAP, MethodTypeImpl.WRAP);
++ assert(wt != null);
++ pt.wrapAlt = wt;
++ }
++ return wt;
++ }
++