--- a/anonk.patch Tue Jul 15 00:17:06 2008 -0700
+++ b/anonk.patch Sat Aug 16 00:17:31 2008 -0700
@@ -2,7 +2,7 @@ new file mode 100644
new file mode 100644
--- /dev/null
+++ b/src/share/classes/java/dyn/AnonymousClassLoader.java
-@@ -0,0 +1,492 @@
+@@ -0,0 +1,297 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -30,261 +30,78 @@ new file mode 100644
+
+package java.dyn;
+
-+import java.util.Arrays;
-+import java.util.List;
-+import java.util.Map;
-+import java.util.HashMap;
++import java.io.IOException;
+import java.io.InputStream;
-+import java.io.DataInputStream;
-+import java.io.ByteArrayInputStream;
-+import java.io.IOException;
++import java.lang.reflect.InvocationTargetException;
++import java.lang.reflect.Method;
+
+/**
-+ * Anonymous class loader.
-+ * - loads an arbitrary class from bytecodes
-+ * - puts class in class hierarchy but *not* in any class loader
-+ * - piggybacks on a "host class" as if it were an inner class
-+ * - elements of the anonymous constant pool can be patched easily
-+ * - string constants can be replaced by arbitrary objects in CP
-+ *
++ * 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 the patching stuff? Mainly, 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:
-+ * - needs better documentation
-+ * - needs more security work (for safe delegation)
-+ * - needs a clearer story about error processing
-+ * - RFE: patch member references also (use ';' as delimiter char)
-+ *
-+ * @author John R. Rose
-+ * @see http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm
++ * 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 {
-+ // 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 final int bufHeaderLen = unsafe.arrayBaseOffset(byte[].class); // 12 or 24
-+
-+ final Class hostClass;
-+
-+ private static final byte[] NO_CLASS_FILE = {};
-+ byte[] classFile = NO_CLASS_FILE;
-+
-+ List classFileCP; // built lazily
-+
-+ Map<Object,Object> patchMap;
-+
-+ private void setClassFile0(byte[] classFile) {
-+ this.classFile = classFile;
-+ // decache:
-+ classFileCP = null;
-+ }
-+ public void setClassFile(byte[] classFile) {
-+ setClassFile0(Arrays.copyOf(classFile, classFile.length));
-+ }
-+ public void setClassFile(byte[] classFile, int offset, int length) {
-+ if (offset < 0 || length < 0 || offset > classFile.length - length)
-+ throw new IndexOutOfBoundsException();
-+ setClassFile0(Arrays.copyOfRange(classFile, offset, length));
-+ }
-+ public void setClassFile(Class templateClass) throws IOException {
-+ java.net.URL tcu = templateClass.getResource(templateClass.getName()+".class");
-+ java.net.URLConnection tcc = tcu.openConnection();
-+ int tcl = tcc.getContentLength();
-+ if (tcl < 0) die("NYI");
-+ byte[] classFile = new byte[tcl];
-+ InputStream tcs = tcc.getInputStream();
-+ for (int fill = 0, nr; fill < classFile.length; fill += nr) {
-+ nr = tcs.read(classFile, fill, classFile.length - fill);
-+ if (nr < 0) die("NYI");
-+ }
-+ setClassFile0(classFile);
-+ }
-+
-+ public void setPatchMap(Map patchMap) {
-+ for (Object key : patchMap.keySet())
-+ verifyLegalConstant(key);
-+ this.patchMap = patchMap;
-+ }
-+ public Map getPatchMap() {
-+ return patchMap;
-+ }
-+
-+
-+ public final static class Constant {
-+ public final int tag; public final String symbol;
-+ private Constant(int tag, String symbol) {
-+ this.tag = tag; this.symbol = symbol;
-+ }
-+ static Constant make(int tag, String symbol) {
-+ return new Constant(tag, symbol);
-+ }
-+
-+ // the point is to use them as hash table keys:
-+ public boolean equals(Constant that) {
-+ return this.tag == that.tag && this.symbol.equals(that.symbol);
-+ }
-+ public boolean equals(Object that) {
-+ return that instanceof Constant && equals((Constant)that);
-+ }
-+ public int hashCode() {
-+ return (tag * 127) + symbol.hashCode();
-+ }
-+ public String toString() {
-+ String pfx = null;
-+ switch (tag) {
-+ case CONSTANT_Class: pfx = "Class:"; break;
-+ case CONSTANT_String: pfx = "String:"; break;
-+ case CONSTANT_NameAndType: pfx = "NameAndType:"; break;
-+ case CONSTANT_Fieldref: pfx = "Fieldref:"; break;
-+ case CONSTANT_Methodref: pfx = "Methodref:"; break;
-+ case CONSTANT_InterfaceMethodref:
-+ pfx = "InterfaceMethodref:"; break;
-+ default: assert(false);
-+ }
-+ return pfx+symbol;
-+ }
-+
-+ public String checkTag(int tag) {
-+ if (tag != this.tag) die("wrong tag: "+this);
-+ return symbol;
-+ }
-+
-+ // Factory methods:
-+ public static Constant forClass(String name) {
-+ verifyClassName(name);
-+ return make(CONSTANT_Class, name);
-+ }
-+ private static void verifyClassName(String name) {
-+ if (name.indexOf('/') >= 0 || name.indexOf(';') >= 0)
-+ die("dangerous character in class name: "+name);
-+ }
-+ public static Constant forClass(Class cls) {
-+ return make(CONSTANT_Class, cls.getName());
-+ }
-+
-+ public static Constant forString(String symbol)
-+ { return make(CONSTANT_String, symbol); }
-+
-+ public static Constant forMember(String className,
-+ boolean isInterface,
-+ String memberName,
-+ boolean isField,
-+ String sig) {
-+ verifyClassName(className);
-+ int tag = ( isField ? CONSTANT_Fieldref
-+ : isInterface ? CONSTANT_InterfaceMethodref
-+ : CONSTANT_Methodref );
-+ if (memberName.indexOf(';') >= 0 ||
-+ (sig.startsWith("(") == isField))
-+ die("illegal member reference");
-+ return make(tag, className + ';' + memberName + ';' + sig);
-+ }
-+ public static Constant forField(String cname, String fname, String sig) {
-+ return forMember(cname, false, fname, true, sig);
-+ }
-+ public static Constant forMethod(String cname, String mname, String sig) {
-+ return forMember(cname, false, mname, false, sig);
-+ }
-+ public static Constant forInterfaceMethod(String cname, String mname, String sig) {
-+ return forMember(cname, true, mname, false, sig);
-+ }
-+ public static Constant forNameAndType(String mname, String sig) {
-+ if (mname.indexOf(';') >= 0)
-+ die("illegal descriptor");
-+ return make(CONSTANT_NameAndType, mname + ';' + sig);
-+ }
-+
-+ // For completeness, but you can just use normal Java constant syntax:
-+ public static String forUtf8(String symbol)
-+ { return symbol; }
-+ public static Integer forValue(int val)
-+ { return val; }
-+ public static Float forValue(float val)
-+ { return val; }
-+ public static Long forValue(long val)
-+ { return val; }
-+ public static Double forValue(double val)
-+ { return val; }
-+ public static Constant forValue(String val)
-+ { return forString(val); }
-+ }
-+
-+ private static final List<Class> LEGAL_CON_CLASSES
-+ = Arrays.asList(new Class[] {
-+ String.class, // utf8 symbols
-+ Integer.class, Float.class, Long.class, Double.class, // primitive values
-+ Constant.class // everything else
-+ });
-+ static void verifyLegalConstant(Object con) {
-+ if (con == null || !LEGAL_CON_CLASSES.contains(con.getClass()))
-+ die("bad constant "+con);
-+ }
-+ public Object putSymbolPatch(String con, String patch) {
-+ return putPatch0(con, patch);
-+ }
-+ public Object putValuePatch(Object con, Object patch) {
-+ if (con instanceof String)
-+ con = Constant.forString((String)con);
-+ return putPatch0(con, patch);
-+ }
-+ /** Add the given constant to the patch table.
-+ * As a special restriction, the constant cannot be a string,
-+ * because it is possibly ambiguous whether it means a Utf8
-+ * or a string constant value.
-+ */
-+ public Object putPatch(Object con, Object patch) {
-+ if (con instanceof String)
-+ die("ambiguous string; use putValuePatch or putSymbolPatch");
-+ if (con instanceof Class)
-+ con = Constant.forClass((Class) con);
-+ return putPatch0(con, patch);
-+ }
-+ Object putPatch0(Object con, Object patch) {
-+ verifyLegalConstant(con);
-+ if (patchMap == null)
-+ patchMap = new HashMap();
-+ return patchMap.put(con, patch);
-+ }
++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) {
++ public AnonymousClassLoader(Class<?> hostClass) {
+ this.hostClass = checkHostClass(hostClass);
+ }
-+ public AnonymousClassLoader(Class hostClass, byte[] classFile) {
-+ this.hostClass = checkHostClass(hostClass);
-+ setClassFile(classFile);
-+ }
-+ public AnonymousClassLoader(Class hostClass, byte[] classFile, int offset, int length) {
-+ this.hostClass = checkHostClass(hostClass);
-+ setClassFile(classFile, 0, classFile.length);
-+ }
-+ public AnonymousClassLoader clone() {
-+ return new AnonymousClassLoader(this);
-+ }
-+ private AnonymousClassLoader(AnonymousClassLoader orig) {
-+ this.hostClass = orig.hostClass;
-+ this.classFile = orig.classFile;
-+ this.patchMap = orig.patchMap;
-+ this.classFileCP = classFileCP;
-+ }
-+
-+ private static Class checkHostClass(Class 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);
++ Class<?> caller = sun.reflect.Reflection.getCallerClass(CHC_CALLERS);
+
+ if (caller == null) {
+ // called from the JVM directly
@@ -297,17 +114,15 @@ new file mode 100644
+ hostClass = caller; // default value is caller itself
+
+ // anonymous class will access hostClass on behalf of caller
-+ Class callee = hostClass;
++ 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:
-+ for (Class outer = caller.getDeclaringClass(); outer != null;
-+ caller = outer) { }
-+ for (Class outer = callee.getDeclaringClass(); outer != null;
-+ callee = outer) { }
++ caller = getTopLevelClass(caller);
++ callee = getTopLevelClass(callee);
+ if (caller == callee)
+ return caller;
+
@@ -333,167 +148,1177 @@ new file mode 100644
+ return hostClass;
+ }
+
-+ public Class loadClass() {
-+ // Make a malloc version, for defineClass:
-+ long classFileMalloc = unsafe.allocateMemory(classFile.length);
-+ unsafe.copyMemory(classFile, bufHeaderLen, null, classFileMalloc, classFile.length);
++ 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 unsafe.defineAnonymousClass(hostClass,
-+ classFileMalloc, classFile.length,
-+ makePatchArray());
-+
-+ } finally {
-+ unsafe.freeMemory(classFileMalloc);
-+ }
-+ }
-+
-+ Object[] makePatchArray() {
-+ if (patchMap == null ||
-+ patchMap.isEmpty())
++ 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/java/dyn/ConstantPoolParser.java b/src/share/classes/java/dyn/ConstantPoolParser.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/ConstantPoolParser.java
+@@ -0,0 +1,368 @@
++/*
++ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation. Sun designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Sun in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
++ * CA 95054 USA or visit www.sun.com if you need additional information or
++ * have any questions.
++ */
++
++package java.dyn;
++
++import java.io.IOException;
++import java.io.OutputStream;
++import java.nio.BufferUnderflowException;
++import java.nio.ByteBuffer;
++
++import static java.dyn.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/java/dyn/ConstantPoolPatch.java b/src/share/classes/java/dyn/ConstantPoolPatch.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/ConstantPoolPatch.java
+@@ -0,0 +1,395 @@
++/*
++ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation. Sun designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Sun in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
++ * CA 95054 USA or visit www.sun.com if you need additional information or
++ * have any questions.
++ */
++
++package java.dyn;
++
++import java.io.IOException;
++import java.io.OutputStream;
++import java.util.Arrays;
++import java.util.IdentityHashMap;
++import java.util.Map;
++
++import static java.dyn.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()];
++ }
++
++ /** 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);
++ }
++
++ /** 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);
++ }
++
++ 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) {
++ 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) {
++ 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) {
++ 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;
-+
-+ //System.out.println("pm = "+patchMap);
-+ ensureCP();
-+ //System.out.println("cp = "+classFileCP);
-+ Object[] pa = new Object[classFileCP.size()];
-+ for (Map.Entry e : patchMap.entrySet()) {
-+ Object con = e.getKey();
-+ Object patch = e.getValue();
-+ int i = classFileCP.indexOf(con);
-+ if (i >= 0)
-+ pa[i] = patch;
-+ }
-+ //System.out.println("pa = "+Arrays.asList(pa));
-+ return pa;
-+ }
-+
-+ static void die(String msg) {
-+ throw new IllegalArgumentException(msg);
-+ }
-+
-+ private void ensureCP() {
-+ if (classFileCP == null)
-+ // parse class file and cache the result
-+ new CPParser();
-+ }
-+
-+ // small parser lives here, loaded on demand:
-+ private class CPParser {
-+ Object[] cpVals;
-+ byte[] cpTags;
-+
-+ CPParser() {
-+ parse();
-+ resolve();
-+ // pass results to outer instance:
-+ classFileCP = Arrays.asList(cpVals);
-+ }
-+
-+ void parse() {
-+ try {
-+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(classFile));
-+ 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");
-+ cpVals = new Object[len];
-+ cpTags = new byte[len];
-+ for (int i = 1, nexti; i < len; i = nexti) {
-+ nexti = i+1;
-+ byte tag = cpTags[i] = in.readByte();
-+ 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 = new 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;
-+ }
-+ cpVals[i] = obj;
-+ }
-+ } catch (IOException ee) {
-+ // format problem
-+ die(ee.toString());
-+ }
-+ }
-+
-+ void resolve() {
-+ // clean out the int[] values, which are temporary
-+ for (int beg = 1, end = cpVals.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 obj = cpVals[i];
-+ int tag = cpTags[i];
-+ if (!(obj instanceof int[])) continue;
-+ int[] pair = (int[]) obj;
-+ String name1, name2;
-+ switch (tag) {
-+ case CONSTANT_String:
-+ name1 = (String) cpVals[pair[0]];
-+ obj = Constant.forString(name1);
-+ break;
-+ case CONSTANT_Class:
-+ name1 = (String) cpVals[pair[0]];
-+ // use the external form favored by Class.forName:
-+ name1 = name1.replace('/', '.');
-+ obj = Constant.forClass(name1);
-+ break;
-+ case CONSTANT_NameAndType:
-+ name1 = (String) cpVals[pair[0]];
-+ name2 = (String) cpVals[pair[1]];
-+ obj = Constant.forNameAndType(name1, name2);
-+ break;
-+ case CONSTANT_Fieldref: // fall through:
-+ case CONSTANT_Methodref: // fall through:
-+ case CONSTANT_InterfaceMethodref: // fall through:
-+ Object cls = cpVals[pair[0]];
-+ Object des = cpVals[pair[1]];
-+ if (!(cls instanceof Constant) ||
-+ !(des instanceof Constant)) {
-+ // one more pass is needed
-+ if (beg2 > i) beg2 = i;
-+ if (end2 < i) end2 = i;
-+ continue;
-+ }
-+ name1 = ((Constant)cls).checkTag(CONSTANT_Class);
-+ name2 = ((Constant)des).checkTag(CONSTANT_NameAndType);
-+ obj = Constant.make(tag, name1 + ';' + name2);
-+ break;
-+ default:
-+ continue;
-+ }
-+ cpVals[i] = obj;
-+ }
-+ }
-+ }
-+ }
-+ 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;
++ 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;
++ }
++}
+diff --git a/src/share/classes/java/dyn/ConstantPoolVisitor.java b/src/share/classes/java/dyn/ConstantPoolVisitor.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/ConstantPoolVisitor.java
+@@ -0,0 +1,192 @@
++/*
++ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation. Sun designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Sun in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
++ * CA 95054 USA or visit www.sun.com if you need additional information or
++ * have any questions.
++ */
++
++package java.dyn;
++
++/**
++ * 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/java/dyn/InvalidConstantPoolFormatException.java b/src/share/classes/java/dyn/InvalidConstantPoolFormatException.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/InvalidConstantPoolFormatException.java
+@@ -0,0 +1,45 @@
++/*
++ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation. Sun designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Sun in the LICENSE file that accompanied this code.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
++ * CA 95054 USA or visit www.sun.com if you need additional information or
++ * have any questions.
++ */
++
++package java.dyn;
++
++/** 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
@@ -505,7 +1330,7 @@ diff --git a/src/share/classes/sun/misc/
* 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 final class Unsafe {
+@@ -811,6 +811,25 @@
public native Class defineClass(String name, byte[] b, int off, int len);
@@ -526,7 +1351,7 @@ diff --git a/src/share/classes/sun/misc/
+ * @params length number of bytes in class file
+ * @params cpPatches where non-null entries exist, they replace corresponding CP entries in data
+ */
-+ public native Class defineAnonymousClass(Class hostClass, long data, int length, Object[] cpPatches);
++ 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. */