--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/anonk.patch Tue Apr 15 01:22:42 2008 -0700
@@ -0,0 +1,1064 @@
+diff --git a/src/share/classes/java/dyn/AnonymousClassLoader.java b/src/share/classes/java/dyn/AnonymousClassLoader.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/java/dyn/AnonymousClassLoader.java
+@@ -0,0 +1,492 @@
++/*
++ * 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.util.Arrays;
++import java.util.List;
++import java.util.Map;
++import java.util.HashMap;
++import java.io.InputStream;
++import java.io.DataInputStream;
++import java.io.ByteArrayInputStream;
++import java.io.IOException;
++
++/**
++ * 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
++ *
++ * <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
++ */
++
++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);
++ }
++
++ // 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);
++ }
++ 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) {
++ // 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:
++ for (Class outer = caller.getDeclaringClass(); outer != null;
++ caller = outer) { }
++ for (Class outer = callee.getDeclaringClass(); outer != null;
++ callee = outer) { }
++ 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() {
++ // Make a malloc version, for defineClass:
++ long classFileMalloc = unsafe.allocateMemory(classFile.length);
++ unsafe.copyMemory(classFile, bufHeaderLen, null, classFileMalloc, classFile.length);
++ try {
++ return unsafe.defineAnonymousClass(hostClass,
++ classFileMalloc, classFile.length,
++ makePatchArray());
++
++ } finally {
++ unsafe.freeMemory(classFileMalloc);
++ }
++ }
++
++ Object[] makePatchArray() {
++ if (patchMap == null ||
++ patchMap.isEmpty())
++ 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;
++}
+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-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
+@@ -811,6 +811,25 @@ public final class Unsafe {
+
+ 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 in 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, a raw memory address b
++ * @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);
++
+ /** Allocate an instance but do not run any constructor.
+ Initializes the class if it has not yet been. */
+ public native Object allocateInstance(Class cls)
+diff --git a/test/java/dyn/AnonymousClassLoader/CPParser.java b/test/java/dyn/AnonymousClassLoader/CPParser.java
+new file mode 100644
+--- /dev/null
++++ b/test/java/dyn/AnonymousClassLoader/CPParser.java
+@@ -0,0 +1,145 @@
++/*
++ * 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.
++ *
++ * 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/java/dyn/AnonymousClassLoader/TestAnonK.java b/test/java/dyn/AnonymousClassLoader/TestAnonK.java
+new file mode 100644
+--- /dev/null
++++ b/test/java/dyn/AnonymousClassLoader/TestAnonK.java
+@@ -0,0 +1,376 @@
++/*
++ * 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.
++ *
++ * 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;
++
++/**
++ * @test
++ * @bug 4546734 5007612
++ * @summary AnonymousClassLoader, basic test
++ */
++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 {
++ AnonymousClassLoader acl = new AnonymousClassLoader();
++ acl.setClassFile(Temp.class);
++ // 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, "!");
++ // replace Utf8 "dummyStatic" with "foo"+n:
++ acl.putSymbolPatch("dummyStatic", "foo"+n);
++ // replace Integer 123456 with n*1000:
++ acl.putPatch(-123456, n*1000);
++ // change name of local routine, just for kicks:
++ acl.putSymbolPatch("funny", "hilarious"+n);
++ // maybe patch the super class
++ acl.putPatch(TempBase.class,
++ (n >= 2) ? classes.get(n-2) : null);
++ // change string constant to a pseudo-string:
++ acl.putValuePatch("this space for rent",
++ oddity);
++ acls = acl.loadClass();
++ }
++ 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 malloc version, for defineClass:
++ long tempBytesMalloc = unsafe.allocateMemory(tempBytes.length);
++ unsafe.copyMemory(tempBytes, bufHeaderLen, null, tempBytesMalloc, tempBytes.length);
++
++ // 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 = tempClass;
++ 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,
++ tempBytesMalloc, tempBytes.length,
++ 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:
++----
++hello, world, from DVM!
++read 1345 bytes from TestAnonK$Temp
++loaded class TestAnonK$Temp
++loaded class TestAnonK$Temp/18296328
++loaded class TestAnonK$Temp/13577344
++loaded class TestAnonK$Temp/24287316
++created TestAnonK$Temp@6eb38a
++created TestAnonK$Temp/18296328@1cd2e5f
++created TestAnonK$Temp/13577344@19f953d
++created TestAnonK$Temp/24287316@1fee6fc
++================
++Hello from foo TestAnonK$Temp@6eb38a
++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/18296328@1cd2e5f
++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/13577344@19f953d
++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/13577344@19f953d
++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/24287316@1fee6fc
++secret string is foo3 still different string
++OK exception: java.lang.IllegalArgumentException: that's not funny
++ at TestAnonK$Temp/3374351.hilarious3(TestAnonK.java:323)
++ at TestAnonK$Temp/3374351.foo(TestAnonK.java:315)
++ at TestAnonK.exercise(TestAnonK.java:175)
++ at TestAnonK.testAPI(TestAnonK.java:87)
++ at TestAnonK.main(TestAnonK.java:53)
++Success!
++----
++*/