--- a/anonk.patch Sun Sep 07 00:55:33 2008 -0700
+++ b/anonk.patch Wed Nov 12 22:24:27 2008 -0800
@@ -677,7 +677,7 @@ new file mode 100644
new file mode 100644
--- /dev/null
+++ b/src/share/classes/java/dyn/ConstantPoolPatch.java
-@@ -0,0 +1,453 @@
+@@ -0,0 +1,503 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -806,6 +806,11 @@ new file mode 100644
+ Arrays.fill(patchArray, null);
+ }
+
++ /** Clear one patch. */
++ public void clear(int index) {
++ patchArray[index] = null;
++ }
++
+ /** Produce the patches as an array. */
+ public Object[] getPatches() {
+ return patchArray.clone();
@@ -820,34 +825,54 @@ new file mode 100644
+ *
+ * @param utf8Map Utf8 strings to modify, if encountered
+ * @param classMap Classes (or their names) to modify, if encountered
-+ * @param constantMap Constant values to modify, if encountered
++ * @param valueMap Constant values to modify, if encountered
+ * @param deleteUsedEntries if true, delete map entries that are used
+ */
+ public void putPatches(final Map<String,String> utf8Map,
-+ final Map<Object,Object> classMap,
-+ final Map<Object,Object> constantMap,
++ final Map<String,Object> classMap,
++ final Map<Object,Object> valueMap,
+ boolean deleteUsedEntries) throws InvalidConstantPoolFormatException {
-+ if (classMap != null || constantMap != null)
-+ throw new UnsupportedOperationException("NYI: non-null classMap or constantMap");
-+ final HashSet<String> usedUtf8Keys
-+ = (!deleteUsedEntries ? null : new HashSet<String>());
-+ outer.parse(new ConstantPoolVisitor() {
++ final HashSet<String> usedUtf8Keys;
++ final HashSet<String> usedClassKeys;
++ final HashSet<Object> usedValueKeys;
++ if (deleteUsedEntries) {
++ usedUtf8Keys = (utf8Map == null) ? null : new HashSet<String>();
++ usedClassKeys = (classMap == null) ? null : new HashSet<String>();
++ usedValueKeys = (valueMap == null) ? null : new HashSet<Object>();
++ } else {
++ usedUtf8Keys = null;
++ usedClassKeys = null;
++ usedValueKeys = null;
++ }
++
++ outer.parse(new ConstantPoolVisitor() {
+
+ @Override
+ public void visitUTF8(int index, byte tag, String utf8) {
-+ String orig = utf8;
-+ Object oldPatch = patchArray[index];
-+ if (oldPatch != null)
-+ orig = (String) oldPatch;
-+ String repl = utf8Map.get(orig);
-+ if (repl != null) {
-+ putUTF8(index, repl);
-+ if (usedUtf8Keys != null) usedUtf8Keys.add(orig);
++ putUTF8(index, utf8Map.get(utf8));
++ if (usedUtf8Keys != null) usedUtf8Keys.add(utf8);
++ }
++
++ @Override
++ public void visitConstantValue(int index, byte tag, Object value) {
++ putConstantValue(index, tag, valueMap.get(value));
++ if (usedValueKeys != null) usedValueKeys.add(value);
++ }
++
++ @Override
++ public void visitConstantString(int index, byte tag, String name, int nameIndex) {
++ if (tag == CONSTANT_Class) {
++ putConstantValue(index, tag, classMap.get(name));
++ if (usedClassKeys != null) usedClassKeys.add(name);
++ } else {
++ assert(tag == CONSTANT_String);
++ visitConstantValue(index, tag, name);
+ }
+ }
+ });
-+ if (usedUtf8Keys != null)
-+ utf8Map.keySet().removeAll(usedUtf8Keys);
++ if (usedUtf8Keys != null) utf8Map.keySet().removeAll(usedUtf8Keys);
++ if (usedClassKeys != null) classMap.keySet().removeAll(usedClassKeys);
++ if (usedValueKeys != null) valueMap.keySet().removeAll(usedValueKeys);
+ }
+
+ Object[] getOriginalCP(final int startIndex,
@@ -956,6 +981,7 @@ new file mode 100644
+ * @see ConstantPoolVisitor#visitUTF8(int, byte, String)
+ */
+ public void putUTF8(int index, String utf8) {
++ if (utf8 == null) { clear(index); return; }
+ checkTag(index, CONSTANT_Utf8);
+ patchArray[index] = utf8;
+ }
@@ -981,6 +1007,7 @@ new file mode 100644
+ * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int)
+ */
+ public void putConstantValue(int index, Object value) {
++ if (value == null) { clear(index); return; }
+ byte tag = tagForConstant(value.getClass());
+ checkConstantTag(tag, value);
+ checkTag(index, tag);
@@ -1008,6 +1035,7 @@ new file mode 100644
+ * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int)
+ */
+ public void putConstantValue(int index, byte tag, Object value) {
++ if (value == null) { clear(index); return; }
+ checkTag(index, tag);
+ if (tag == CONSTANT_Class && value instanceof String) {
+ checkClassName((String) value);
@@ -1129,6 +1157,28 @@ new file mode 100644
+ }
+ res[count] = string.substring(pos);
+ return res;
++ }
++
++ public String toString() {
++ StringBuilder buf = new StringBuilder(this.getClass().getName());
++ buf.append("{");
++ Object[] origCP = null;
++ for (int i = 0; i < patchArray.length; i++) {
++ if (patchArray[i] == null) continue;
++ if (origCP != null) {
++ buf.append(", ");
++ } else {
++ try {
++ origCP = getOriginalCP();
++ } catch (InvalidConstantPoolFormatException ee) {
++ origCP = new Object[0];
++ }
++ }
++ Object orig = (i < origCP.length) ? origCP[i] : "?";
++ buf.append(orig).append("=").append(patchArray[i]);
++ }
++ buf.append("}");
++ return buf.toString();
+ }
+}
diff --git a/src/share/classes/java/dyn/ConstantPoolVisitor.java b/src/share/classes/java/dyn/ConstantPoolVisitor.java
@@ -1388,7 +1438,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 @@
+@@ -811,6 +811,25 @@ public final class Unsafe {
public native Class defineClass(String name, byte[] b, int off, int len);
@@ -1568,7 +1618,7 @@ new file mode 100644
new file mode 100644
--- /dev/null
+++ b/test/java/dyn/AnonymousClassLoader/TestAnonK.java
-@@ -0,0 +1,376 @@
+@@ -0,0 +1,381 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -1607,6 +1657,7 @@ new file mode 100644
+import java.util.*;
+import java.io.*;
+import java.dyn.AnonymousClassLoader;
++import java.dyn.ConstantPoolPatch;
+
+/**
+ * @test
@@ -1625,29 +1676,35 @@ new file mode 100644
+ }
+
+ 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;
++ AnonymousClassLoader loader = new AnonymousClassLoader(hostClass);
+ List<Class> classes = new ArrayList<Class>();
+ List<Inter> objects = new ArrayList<Inter>();
+ for (int n = 0; n <= 3; n++) {
+ Class acls = Temp.class;
++ ConstantPoolPatch cpp = new ConstantPoolPatch(acls);
+ if (n > 0) {
+ Object oddity = Arrays.asList("odd", "constant", n, "!");
++ HashMap<String,String> utf8Map = new HashMap<String,String>();
++ HashMap<String,Object> classMap = new HashMap<String,Object>();
++ HashMap<Object,Object> constantMap = new HashMap<Object,Object>();
+ // replace Utf8 "dummyStatic" with "foo"+n:
-+ acl.putSymbolPatch("dummyStatic", "foo"+n);
++ utf8Map.put("dummyStatic", "foo"+n);
+ // replace Integer 123456 with n*1000:
-+ acl.putPatch(-123456, n*1000);
++ constantMap.put(-123456, n*1000);
+ // change name of local routine, just for kicks:
-+ acl.putSymbolPatch("funny", "hilarious"+n);
++ utf8Map.put("funny", "hilarious"+n);
+ // maybe patch the super class
-+ acl.putPatch(TempBase.class,
-+ (n >= 2) ? classes.get(n-2) : null);
++ if (n >= 2) classMap.put(TempBase.class.getName(), classes.get(n-2));
+ // change string constant to a pseudo-string:
-+ acl.putValuePatch("this space for rent",
-+ oddity);
-+ acls = acl.loadClass();
++ constantMap.put("this space for rent", oddity);
++ cpp.putPatches(utf8Map, classMap, constantMap, true);
++ System.out.println("patches = "+cpp);
++ assert(utf8Map.isEmpty());
++ assert(classMap.isEmpty());
++ assert(constantMap.isEmpty());
++ acls = loader.loadClass(cpp);
+ }
+ System.out.println("loaded "+acls);
+ for (int i = 0; i < n; i++)
@@ -1678,16 +1735,12 @@ new file mode 100644
+ 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;
++ Class acls = Temp.class;
+ if (n > 0) {
+ Object oddity = Arrays.asList("odd", "constant", n, "!");
+ Object[] cpPatches
@@ -1705,9 +1758,7 @@ new file mode 100644
+ Arrays.asList("String", "this space for rent"),
+ oddity
+ );
-+ acls = unsafe.defineAnonymousClass(hostClass,
-+ tempBytesMalloc, tempBytes.length,
-+ cpPatches);
++ acls = unsafe.defineAnonymousClass(hostClass, tempBytes, cpPatches);
+ }
+ System.out.println("loaded "+acls);
+ for (int i = 0; i < n; i++)
@@ -1901,47 +1952,51 @@ new file mode 100644
+/*
+ Sample output:
+----
++$ ./gamma -XX:+AnonymousClasses -Xbootclasspath/p:/scratch/jrose/ws/davinci/build/bootcp TestAnonK
++VM option '+AnonymousClasses'
+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
++patches = java.dyn.ConstantPoolPatch{this space for rent=[odd, constant, 1, !], -123456=1000, funny=hilarious1, dummyStatic=foo1}
++loaded class TestAnonK$Temp/747212
++patches = java.dyn.ConstantPoolPatch{this space for rent=[odd, constant, 2, !], -123456=2000, TestAnonK$TempBase=class TestAnonK$Temp, funny=hilarious2, dummyStatic=foo2}
++loaded class TestAnonK$Temp/9047389
++patches = java.dyn.ConstantPoolPatch{this space for rent=[odd, constant, 3, !], -123456=3000, TestAnonK$TempBase=class TestAnonK$Temp/747212, funny=hilarious3, dummyStatic=foo3}
++loaded class TestAnonK$Temp/24347419
++created TestAnonK$Temp@a470b8
++created TestAnonK$Temp/747212@1e4457d
++created TestAnonK$Temp/9047389@18e2b22
++created TestAnonK$Temp/24347419@b1c5fa
+================
-+Hello from foo TestAnonK$Temp@6eb38a
++Hello from foo TestAnonK$Temp@a470b8
+secret string is DUMMY!
+funny number is -123456
+wierd constant class is class java.lang.String for this space for rent
+...end of foo stuff
+----------------
-+Hello from foo TestAnonK$Temp/18296328@1cd2e5f
++Hello from foo TestAnonK$Temp/747212@1e4457d
+secret string is foo1 string
+funny number is 1000
+wierd constant class is class java.util.Arrays$ArrayList for [odd, constant, 1, !]
+...end of foo stuff
+----------------
-+Hello from foo TestAnonK$Temp/13577344@19f953d
++Hello from foo TestAnonK$Temp/9047389@18e2b22
+secret string is foo2 different string
+funny number is 2000
+wierd constant class is class java.util.Arrays$ArrayList for [odd, constant, 2, !]
-+Hello from foo TestAnonK$Temp/13577344@19f953d
++Hello from foo TestAnonK$Temp/9047389@18e2b22
+secret string is DUMMY!
+funny number is -123456
+wierd constant class is class java.lang.String for this space for rent
+...end of foo stuff
+----------------
-+Hello from foo TestAnonK$Temp/24287316@1fee6fc
++Hello from foo TestAnonK$Temp/24347419@b1c5fa
+secret string is foo3 still different string
+OK exception: java.lang.IllegalArgumentException: that's not funny
-+ at TestAnonK$Temp/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)
++ at TestAnonK$Temp/24347419.hilarious3(TestAnonK.java:324)
++ at TestAnonK$Temp/24347419.foo(TestAnonK.java:316)
++ at TestAnonK.exercise(TestAnonK.java:176)
++ at TestAnonK.testAPI(TestAnonK.java:94)
++ at TestAnonK.main(TestAnonK.java:54)
+Success!
+----
+*/