aboutsummaryrefslogtreecommitdiffstats
path: root/dcevm/src/main/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'dcevm/src/main/java/com')
-rw-r--r--dcevm/src/main/java/com/github/dcevm/ClassRedefinitionPolicy.java8
-rw-r--r--dcevm/src/main/java/com/github/dcevm/FieldRedefinitionPolicy.java3
-rw-r--r--dcevm/src/main/java/com/github/dcevm/HotSwapTool.java340
-rw-r--r--dcevm/src/main/java/com/github/dcevm/InstrumentationRedefiner.java30
-rw-r--r--dcevm/src/main/java/com/github/dcevm/JDIRedefiner.java173
-rw-r--r--dcevm/src/main/java/com/github/dcevm/MethodRedefinitionPolicy.java3
-rw-r--r--dcevm/src/main/java/com/github/dcevm/Redefiner.java2
-rw-r--r--dcevm/src/main/java/com/github/dcevm/RedefinitionPolicy.java9
-rw-r--r--dcevm/src/main/java/com/github/dcevm/TestClassAdapter.java172
9 files changed, 369 insertions, 371 deletions
diff --git a/dcevm/src/main/java/com/github/dcevm/ClassRedefinitionPolicy.java b/dcevm/src/main/java/com/github/dcevm/ClassRedefinitionPolicy.java
index 4fd1be8a..8c7b67fe 100644
--- a/dcevm/src/main/java/com/github/dcevm/ClassRedefinitionPolicy.java
+++ b/dcevm/src/main/java/com/github/dcevm/ClassRedefinitionPolicy.java
@@ -32,9 +32,9 @@ import java.lang.annotation.RetentionPolicy;
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ClassRedefinitionPolicy {
- // Default value if no alias is set.
- public final static class NoClass {
- }
+ // Default value if no alias is set.
+ public final static class NoClass {
+ }
- Class<?> alias() default NoClass.class;
+ Class<?> alias() default NoClass.class;
}
diff --git a/dcevm/src/main/java/com/github/dcevm/FieldRedefinitionPolicy.java b/dcevm/src/main/java/com/github/dcevm/FieldRedefinitionPolicy.java
index cf3733c9..1c2d2cf3 100644
--- a/dcevm/src/main/java/com/github/dcevm/FieldRedefinitionPolicy.java
+++ b/dcevm/src/main/java/com/github/dcevm/FieldRedefinitionPolicy.java
@@ -27,11 +27,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- *
* @author Thomas Wuerthinger
*/
@Retention(RetentionPolicy.CLASS)
public @interface FieldRedefinitionPolicy {
- RedefinitionPolicy value();
+ RedefinitionPolicy value();
}
diff --git a/dcevm/src/main/java/com/github/dcevm/HotSwapTool.java b/dcevm/src/main/java/com/github/dcevm/HotSwapTool.java
index 568124af..391554a9 100644
--- a/dcevm/src/main/java/com/github/dcevm/HotSwapTool.java
+++ b/dcevm/src/main/java/com/github/dcevm/HotSwapTool.java
@@ -44,197 +44,197 @@ import java.util.regex.Pattern;
*/
public class HotSwapTool {
- /**
- * Prefix for the version number in the class name. The class bytes are modified that this string including
- * the following number is removed. This means that e.g. A___2 is treated as A anywhere in the source code. This is introduced
- * to make the IDE not complain about multiple defined classes.
- */
- public static final Pattern VERSION_PATTERN = Pattern.compile("___([0-9]+)");
- private static final String CLASS_FILE_SUFFIX = ".class";
- private static Map<Class<?>, Integer> currentVersion = new Hashtable<Class<?>, Integer>();
- private static Redefiner redefiner;
- private static int redefinitionCount;
- private static long totalTime;
-
- static {
- try {
- //redefiner = new JDIRedefiner(4000);
- redefiner = new InstrumentationRedefiner();
- } catch (Exception e) {
- throw new IllegalStateException(e);
- }
+ /**
+ * Prefix for the version number in the class name. The class bytes are modified that this string including
+ * the following number is removed. This means that e.g. A___2 is treated as A anywhere in the source code. This is introduced
+ * to make the IDE not complain about multiple defined classes.
+ */
+ public static final Pattern VERSION_PATTERN = Pattern.compile("___([0-9]+)");
+ private static final String CLASS_FILE_SUFFIX = ".class";
+ private static Map<Class<?>, Integer> currentVersion = new Hashtable<Class<?>, Integer>();
+ private static Redefiner redefiner;
+ private static int redefinitionCount;
+ private static long totalTime;
+
+ static {
+ try {
+ //redefiner = new JDIRedefiner(4000);
+ redefiner = new InstrumentationRedefiner();
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
}
-
- /**
- * Returns the current version of the inner classes of a specified outer class.
- *
- * @param baseClass the outer class whose version is queried
- * @return the version of the inner classes of the specified outer class
- */
- public static int getCurrentVersion(Class<?> baseClass) {
- if (!currentVersion.containsKey(baseClass)) {
- currentVersion.put(baseClass, 0);
- }
- return currentVersion.get(baseClass);
+ }
+
+ /**
+ * Returns the current version of the inner classes of a specified outer class.
+ *
+ * @param baseClass the outer class whose version is queried
+ * @return the version of the inner classes of the specified outer class
+ */
+ public static int getCurrentVersion(Class<?> baseClass) {
+ if (!currentVersion.containsKey(baseClass)) {
+ currentVersion.put(baseClass, 0);
}
+ return currentVersion.get(baseClass);
+ }
+
+ /**
+ * Performs an explicit shutdown and disconnects from the VM.
+ */
+ public static void shutdown() throws IOException {
+ redefiner.close();
+ redefiner = null;
+ }
+
+ private static Map<Class<?>, byte[]> buildRedefinitionMap(Map<String, File> classes) throws IOException, ClassNotFoundException {
+ // Collect rename rules
+ // Also, makes sure all classes are loaded in the VM, before they are redefined
+ final Map<String, String> typeMappings = new HashMap<String, String>();
+ for (String name : classes.keySet()) {
+ Class<?> clazz = Class.forName(name); // FIXME: classloader?
+ ClassRedefinitionPolicy policy = clazz.getAnnotation(ClassRedefinitionPolicy.class);
+ Class<?> replacement = (policy != null && policy.alias() != ClassRedefinitionPolicy.NoClass.class) ?
+ policy.alias() : clazz;
+ typeMappings.put(Type.getInternalName(clazz), stripVersion(Type.getInternalName(replacement)));
- /**
- * Performs an explicit shutdown and disconnects from the VM.
- */
- public static void shutdown() throws IOException {
- redefiner.close();
- redefiner = null;
}
- private static Map<Class<?>, byte[]> buildRedefinitionMap(Map<String, File> classes) throws IOException, ClassNotFoundException {
- // Collect rename rules
- // Also, makes sure all classes are loaded in the VM, before they are redefined
- final Map<String, String> typeMappings = new HashMap<String, String>();
- for (String name : classes.keySet()) {
- Class<?> clazz = Class.forName(name); // FIXME: classloader?
- ClassRedefinitionPolicy policy = clazz.getAnnotation(ClassRedefinitionPolicy.class);
- Class<?> replacement = (policy != null && policy.alias() != ClassRedefinitionPolicy.NoClass.class) ?
- policy.alias() : clazz;
- typeMappings.put(Type.getInternalName(clazz), stripVersion(Type.getInternalName(replacement)));
-
- }
-
- Map<Class<?>, byte[]> classesMap = new HashMap<Class<?>, byte[]>();
- for (File file : classes.values()) {
- loadAdaptedClass(file, typeMappings, classesMap);
- }
- return classesMap;
+ Map<Class<?>, byte[]> classesMap = new HashMap<Class<?>, byte[]>();
+ for (File file : classes.values()) {
+ loadAdaptedClass(file, typeMappings, classesMap);
}
-
- private static void loadAdaptedClass(File file, Map<String, String> typeMappnigs, Map<Class<?>, byte[]> result) throws IOException, ClassNotFoundException {
-
- ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
- TestClassAdapter adapter = new TestClassAdapter(writer, typeMappnigs);
-
- InputStream in = new FileInputStream(file);
- try {
- new ClassReader(in).accept(adapter, ClassReader.EXPAND_FRAMES);
- } finally {
- try {
- in.close();
- } catch (IOException e) {
- // Ignore.
- }
- }
- byte[] bytes = writer.toByteArray();
- String className = adapter.getClassName().replace('/', '.');
- result.put(Class.forName(className), bytes); // FIXME: ClassLoader...
+ return classesMap;
+ }
+
+ private static void loadAdaptedClass(File file, Map<String, String> typeMappnigs, Map<Class<?>, byte[]> result) throws IOException, ClassNotFoundException {
+
+ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ TestClassAdapter adapter = new TestClassAdapter(writer, typeMappnigs);
+
+ InputStream in = new FileInputStream(file);
+ try {
+ new ClassReader(in).accept(adapter, ClassReader.EXPAND_FRAMES);
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // Ignore.
+ }
}
-
- /**
- * Redefines all inner classes of a outer class to a specified version. Inner classes who do not have a particular
- * representation for a version remain unchanged.
- *
- * @param outerClass the outer class whose inner classes should be redefined
- * @param versionNumber the target version number
- */
- public static void toVersion(Class<?> outerClass, int versionNumber, Class<?>... extraClasses) {
- assert versionNumber >= 0;
-
- if (versionNumber == getCurrentVersion(outerClass)) {
- // Nothing to do!
- return;
- }
-
- Map<String, File> files = findClassesWithVersion(outerClass, versionNumber);
-
- for (Class<?> extra : extraClasses) {
- if (parseClassVersion(extra.getSimpleName()) == versionNumber) {
- String packageName = extra.getPackage().getName().replace('.', '/');
- URL url = extra.getClassLoader().getResource(packageName);
- if (url == null) {
- throw new IllegalArgumentException("Cannot find URL corresponding to the package '" + packageName + "'");
- }
- File file = new File(url.getFile(), extra.getSimpleName() + ".class");
- files.put(extra.getName(), file);
- }
- }
-
- try {
- Map<Class<?>, byte[]> map = buildRedefinitionMap(files);
-
- long startTime = System.currentTimeMillis();
- redefiner.redefineClasses(map);
- long curTime = System.currentTimeMillis() - startTime;
- totalTime += curTime;
- redefinitionCount++;
-
- } catch (UnmodifiableClassException e) {
- throw new UnsupportedOperationException(e);
- } catch (ClassNotFoundException e) {
- throw new RuntimeException("Cannot redefine classes", e);
- } catch (IOException e) {
- throw new RuntimeException("Cannot redefine classes", e);
- }
-
- setCurrentVersion(outerClass, versionNumber);
+ byte[] bytes = writer.toByteArray();
+ String className = adapter.getClassName().replace('/', '.');
+ result.put(Class.forName(className), bytes); // FIXME: ClassLoader...
+ }
+
+ /**
+ * Redefines all inner classes of a outer class to a specified version. Inner classes who do not have a particular
+ * representation for a version remain unchanged.
+ *
+ * @param outerClass the outer class whose inner classes should be redefined
+ * @param versionNumber the target version number
+ */
+ public static void toVersion(Class<?> outerClass, int versionNumber, Class<?>... extraClasses) {
+ assert versionNumber >= 0;
+
+ if (versionNumber == getCurrentVersion(outerClass)) {
+ // Nothing to do!
+ return;
}
- private static Map<String, File> findClassesWithVersion(Class<?> baseClass, int version) {
- Map<String, File> classes = new HashMap<String, File>();
+ Map<String, File> files = findClassesWithVersion(outerClass, versionNumber);
- String packageName = baseClass.getPackage().getName().replace('.', '/');
- URL url = baseClass.getClassLoader().getResource(packageName);
+ for (Class<?> extra : extraClasses) {
+ if (parseClassVersion(extra.getSimpleName()) == versionNumber) {
+ String packageName = extra.getPackage().getName().replace('.', '/');
+ URL url = extra.getClassLoader().getResource(packageName);
if (url == null) {
- throw new IllegalArgumentException("Cannot find URL corresponding to the package '" + packageName + "'");
+ throw new IllegalArgumentException("Cannot find URL corresponding to the package '" + packageName + "'");
}
- File folder = new File(url.getFile());
- for (File f : folder.listFiles(IsClassFile.INSTANCE)) {
- String simpleName = f.getName().substring(0, f.getName().length() - CLASS_FILE_SUFFIX.length());
- String name = baseClass.getPackage().getName() + '.' + simpleName;
-
- if (isInnerClass(name, baseClass) && parseClassVersion(simpleName) == version) {
- classes.put(name, f);
- }
- }
- return classes;
+ File file = new File(url.getFile(), extra.getSimpleName() + ".class");
+ files.put(extra.getName(), file);
+ }
}
- private enum IsClassFile implements FilenameFilter {
- INSTANCE;
-
- @Override
- public boolean accept(File dir, String name) {
- return name.endsWith(CLASS_FILE_SUFFIX);
- }
+ try {
+ Map<Class<?>, byte[]> map = buildRedefinitionMap(files);
+
+ long startTime = System.currentTimeMillis();
+ redefiner.redefineClasses(map);
+ long curTime = System.currentTimeMillis() - startTime;
+ totalTime += curTime;
+ redefinitionCount++;
+
+ } catch (UnmodifiableClassException e) {
+ throw new UnsupportedOperationException(e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Cannot redefine classes", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot redefine classes", e);
}
- private static boolean isInnerClass(String name, Class<?> baseClass) {
- return name.startsWith(baseClass.getName() + "$");
- }
-
- private static void setCurrentVersion(Class<?> baseClass, int value) {
- currentVersion.put(baseClass, value);
- }
+ setCurrentVersion(outerClass, versionNumber);
+ }
- /**
- * Parse version of the class from the class name. Classes are named in the form of [Name]___[Version]
- */
- private static int parseClassVersion(String simpleName) {
- Matcher m = VERSION_PATTERN.matcher(simpleName);
- return m.find() ? Integer.valueOf(m.group(1)) : 0;
- }
+ private static Map<String, File> findClassesWithVersion(Class<?> baseClass, int version) {
+ Map<String, File> classes = new HashMap<String, File>();
- private static String stripVersion(String className) {
- Matcher m = VERSION_PATTERN.matcher(className);
- return m.replaceAll("");
+ String packageName = baseClass.getPackage().getName().replace('.', '/');
+ URL url = baseClass.getClassLoader().getResource(packageName);
+ if (url == null) {
+ throw new IllegalArgumentException("Cannot find URL corresponding to the package '" + packageName + "'");
}
-
- public static void resetTimings() {
- redefinitionCount = 0;
- totalTime = 0;
+ File folder = new File(url.getFile());
+ for (File f : folder.listFiles(IsClassFile.INSTANCE)) {
+ String simpleName = f.getName().substring(0, f.getName().length() - CLASS_FILE_SUFFIX.length());
+ String name = baseClass.getPackage().getName() + '.' + simpleName;
+
+ if (isInnerClass(name, baseClass) && parseClassVersion(simpleName) == version) {
+ classes.put(name, f);
+ }
}
+ return classes;
+ }
- public static int getRedefinitionCount() {
- return redefinitionCount;
- }
+ private enum IsClassFile implements FilenameFilter {
+ INSTANCE;
- public static long getTotalTime() {
- return totalTime;
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.endsWith(CLASS_FILE_SUFFIX);
}
+ }
+
+ private static boolean isInnerClass(String name, Class<?> baseClass) {
+ return name.startsWith(baseClass.getName() + "$");
+ }
+
+ private static void setCurrentVersion(Class<?> baseClass, int value) {
+ currentVersion.put(baseClass, value);
+ }
+
+ /**
+ * Parse version of the class from the class name. Classes are named in the form of [Name]___[Version]
+ */
+ private static int parseClassVersion(String simpleName) {
+ Matcher m = VERSION_PATTERN.matcher(simpleName);
+ return m.find() ? Integer.valueOf(m.group(1)) : 0;
+ }
+
+ private static String stripVersion(String className) {
+ Matcher m = VERSION_PATTERN.matcher(className);
+ return m.replaceAll("");
+ }
+
+ public static void resetTimings() {
+ redefinitionCount = 0;
+ totalTime = 0;
+ }
+
+ public static int getRedefinitionCount() {
+ return redefinitionCount;
+ }
+
+ public static long getTotalTime() {
+ return totalTime;
+ }
}
diff --git a/dcevm/src/main/java/com/github/dcevm/InstrumentationRedefiner.java b/dcevm/src/main/java/com/github/dcevm/InstrumentationRedefiner.java
index 42215435..5c4a4253 100644
--- a/dcevm/src/main/java/com/github/dcevm/InstrumentationRedefiner.java
+++ b/dcevm/src/main/java/com/github/dcevm/InstrumentationRedefiner.java
@@ -10,22 +10,22 @@ import java.lang.instrument.UnmodifiableClassException;
import java.util.Map;
public class InstrumentationRedefiner implements Redefiner {
- public void redefineClasses(Map<Class<?>, byte[]> classes) throws ClassNotFoundException, UnmodifiableClassException {
- Instrumentation instrumentation = InstrumentationAgent.INSTRUMENTATION;
- if (instrumentation == null) {
- throw new IllegalStateException("Instrumentation agent is not properly installed!");
- }
-
- ClassDefinition[] definitions = new ClassDefinition[classes.size()];
- int i = 0;
- for (Map.Entry<Class<?>, byte[]> entry : classes.entrySet()) {
- definitions[i++] = new ClassDefinition(entry.getKey(), entry.getValue());
- }
- instrumentation.redefineClasses(definitions);
+ public void redefineClasses(Map<Class<?>, byte[]> classes) throws ClassNotFoundException, UnmodifiableClassException {
+ Instrumentation instrumentation = InstrumentationAgent.INSTRUMENTATION;
+ if (instrumentation == null) {
+ throw new IllegalStateException("Instrumentation agent is not properly installed!");
}
- @Override
- public void close() throws IOException {
- // Do nothing.
+ ClassDefinition[] definitions = new ClassDefinition[classes.size()];
+ int i = 0;
+ for (Map.Entry<Class<?>, byte[]> entry : classes.entrySet()) {
+ definitions[i++] = new ClassDefinition(entry.getKey(), entry.getValue());
}
+ instrumentation.redefineClasses(definitions);
+ }
+
+ @Override
+ public void close() throws IOException {
+ // Do nothing.
+ }
}
diff --git a/dcevm/src/main/java/com/github/dcevm/JDIRedefiner.java b/dcevm/src/main/java/com/github/dcevm/JDIRedefiner.java
index a28444bc..7d1b014a 100644
--- a/dcevm/src/main/java/com/github/dcevm/JDIRedefiner.java
+++ b/dcevm/src/main/java/com/github/dcevm/JDIRedefiner.java
@@ -47,107 +47,108 @@ import java.util.logging.Logger;
* @author Thomas Wuerthinger
* @author Kerstin Breiteneder
* @author Christoph Wimberger
- *
*/
public class JDIRedefiner implements Redefiner {
- private static final String PORT_ARGUMENT_NAME = "port";
- private static final String TRANSPORT_NAME = "dt_socket";
-
- private VirtualMachine vm;
-
-
- /** Port at which to connect to the agent of the VM. **/
- public static final int PORT = 4000;
-
- public JDIRedefiner(int port) throws IOException {
- vm = connect(port);
+ private static final String PORT_ARGUMENT_NAME = "port";
+ private static final String TRANSPORT_NAME = "dt_socket";
+
+ private VirtualMachine vm;
+
+
+ /**
+ * Port at which to connect to the agent of the VM.
+ **/
+ public static final int PORT = 4000;
+
+ public JDIRedefiner(int port) throws IOException {
+ vm = connect(port);
+ }
+
+ @Override
+ public void close() throws IOException {
+ disconnect();
+ }
+
+ private VirtualMachine connect(int port) throws IOException {
+ VirtualMachineManager manager = Bootstrap.virtualMachineManager();
+
+ // Find appropiate connector
+ List<AttachingConnector> connectors = manager.attachingConnectors();
+ AttachingConnector chosenConnector = null;
+ for (AttachingConnector c : connectors) {
+ if (c.transport().name().equals(TRANSPORT_NAME)) {
+ chosenConnector = c;
+ break;
+ }
+ }
+ if (chosenConnector == null) {
+ throw new IllegalStateException("Could not find socket connector");
}
- @Override
- public void close() throws IOException {
- disconnect();
+ // Set port argument
+ AttachingConnector connector = chosenConnector;
+ Map<String, Argument> defaults = connector.defaultArguments();
+ Argument arg = defaults.get(PORT_ARGUMENT_NAME);
+ if (arg == null) {
+ throw new IllegalStateException("Could not find port argument");
}
+ arg.setValue(Integer.toString(port));
- private VirtualMachine connect(int port) throws IOException {
- VirtualMachineManager manager = Bootstrap.virtualMachineManager();
-
- // Find appropiate connector
- List<AttachingConnector> connectors = manager.attachingConnectors();
- AttachingConnector chosenConnector = null;
- for (AttachingConnector c : connectors) {
- if (c.transport().name().equals(TRANSPORT_NAME)) {
- chosenConnector = c;
- break;
- }
- }
- if (chosenConnector == null) {
- throw new IllegalStateException("Could not find socket connector");
- }
-
- // Set port argument
- AttachingConnector connector = chosenConnector;
- Map<String, Argument> defaults = connector.defaultArguments();
- Argument arg = defaults.get(PORT_ARGUMENT_NAME);
- if (arg == null) {
- throw new IllegalStateException("Could not find port argument");
- }
- arg.setValue(Integer.toString(port));
-
- // Attach
- try {
- System.out.println("Connector arguments: " + defaults);
- return connector.attach(defaults);
- } catch (IllegalConnectorArgumentsException e) {
- throw new IllegalArgumentException("Illegal connector arguments", e);
- }
+ // Attach
+ try {
+ System.out.println("Connector arguments: " + defaults);
+ return connector.attach(defaults);
+ } catch (IllegalConnectorArgumentsException e) {
+ throw new IllegalArgumentException("Illegal connector arguments", e);
}
+ }
- public void disconnect() {
- if (vm != null) {
- vm.dispose();
- vm = null;
- }
+ public void disconnect() {
+ if (vm != null) {
+ vm.dispose();
+ vm = null;
}
+ }
- public void redefineClasses(Map<Class<?>, byte[]> classes) {
- refreshAllClasses();
- List<ReferenceType> references = vm.allClasses();
+ public void redefineClasses(Map<Class<?>, byte[]> classes) {
+ refreshAllClasses();
+ List<ReferenceType> references = vm.allClasses();
- Map<ReferenceType, byte[]> map = new HashMap<ReferenceType, byte[]>(classes.size());
- for (Map.Entry<Class<?>, byte[]> entry : classes.entrySet()) {
- map.put(findReference(references, entry.getKey().getName()), entry.getValue());
- }
- vm.redefineClasses(map);
+ Map<ReferenceType, byte[]> map = new HashMap<ReferenceType, byte[]>(classes.size());
+ for (Map.Entry<Class<?>, byte[]> entry : classes.entrySet()) {
+ map.put(findReference(references, entry.getKey().getName()), entry.getValue());
}
+ vm.redefineClasses(map);
+ }
- /**
- * Call this method before calling allClasses() in order to refresh the JDI state of loaded classes.
- * This is necessary because the JDI map of all loaded classes is only updated based on events received over JDWP (network connection)
- * and therefore it is not necessarily up-to-date with the real state within the VM.
- */
- private void refreshAllClasses() {
- try {
- Field f = vm.getClass().getDeclaredField("retrievedAllTypes");
- f.setAccessible(true);
- f.set(vm, false);
- } catch (IllegalArgumentException ex) {
- Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
- } catch (IllegalAccessException ex) {
- Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
- } catch (NoSuchFieldException ex) {
- Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
- } catch (SecurityException ex) {
- Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
- }
+ /**
+ * Call this method before calling allClasses() in order to refresh the JDI state of loaded classes.
+ * This is necessary because the JDI map of all loaded classes is only updated based on events received over JDWP (network connection)
+ * and therefore it is not necessarily up-to-date with the real state within the VM.
+ */
+ private void refreshAllClasses() {
+ try {
+ Field f = vm.getClass().getDeclaredField("retrievedAllTypes");
+ f.setAccessible(true);
+ f.set(vm, false);
+ } catch (IllegalArgumentException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (IllegalAccessException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (NoSuchFieldException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (SecurityException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
}
+ }
- private static ReferenceType findReference(List<ReferenceType> list, String name) {
- for (ReferenceType ref : list) {
- if (ref.name().equals(name)) {
- return ref;
- }
- }
- throw new IllegalArgumentException("Cannot find corresponding reference for class name '" + name + "'" );
+ private static ReferenceType findReference(List<ReferenceType> list, String name) {
+ for (ReferenceType ref : list) {
+ if (ref.name().equals(name)) {
+ return ref;
+ }
}
+ throw new IllegalArgumentException("Cannot find corresponding reference for class name '" + name + "'");
+ }
}
diff --git a/dcevm/src/main/java/com/github/dcevm/MethodRedefinitionPolicy.java b/dcevm/src/main/java/com/github/dcevm/MethodRedefinitionPolicy.java
index 01d17c22..260f7cc9 100644
--- a/dcevm/src/main/java/com/github/dcevm/MethodRedefinitionPolicy.java
+++ b/dcevm/src/main/java/com/github/dcevm/MethodRedefinitionPolicy.java
@@ -28,11 +28,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- *
* @author Thomas Wuerthinger
*/
@Retention(RetentionPolicy.CLASS)
public @interface MethodRedefinitionPolicy {
- RedefinitionPolicy value();
+ RedefinitionPolicy value();
}
diff --git a/dcevm/src/main/java/com/github/dcevm/Redefiner.java b/dcevm/src/main/java/com/github/dcevm/Redefiner.java
index 620ab263..18ff437c 100644
--- a/dcevm/src/main/java/com/github/dcevm/Redefiner.java
+++ b/dcevm/src/main/java/com/github/dcevm/Redefiner.java
@@ -33,5 +33,5 @@ import java.util.Map;
* @author Ivan Dubrov
*/
public interface Redefiner extends Closeable {
- void redefineClasses(Map<Class<?>, byte[]> classes) throws ClassNotFoundException, UnmodifiableClassException;
+ void redefineClasses(Map<Class<?>, byte[]> classes) throws ClassNotFoundException, UnmodifiableClassException;
}
diff --git a/dcevm/src/main/java/com/github/dcevm/RedefinitionPolicy.java b/dcevm/src/main/java/com/github/dcevm/RedefinitionPolicy.java
index f56ba80a..82b7d6e2 100644
--- a/dcevm/src/main/java/com/github/dcevm/RedefinitionPolicy.java
+++ b/dcevm/src/main/java/com/github/dcevm/RedefinitionPolicy.java
@@ -25,12 +25,11 @@
package com.github.dcevm;
/**
- *
* @author Thomas Wuerthinger
*/
public enum RedefinitionPolicy {
- StaticCheck,
- DynamicCheck,
- AccessDeletedMembers,
- AccessOldMembers
+ StaticCheck,
+ DynamicCheck,
+ AccessDeletedMembers,
+ AccessOldMembers
}
diff --git a/dcevm/src/main/java/com/github/dcevm/TestClassAdapter.java b/dcevm/src/main/java/com/github/dcevm/TestClassAdapter.java
index e8639df1..8ea89b6d 100644
--- a/dcevm/src/main/java/com/github/dcevm/TestClassAdapter.java
+++ b/dcevm/src/main/java/com/github/dcevm/TestClassAdapter.java
@@ -35,101 +35,101 @@ import java.util.Map;
* @author Ivan Dubrov
*/
public class TestClassAdapter extends RemappingClassAdapter {
- /**
- * This suffix is automatically removed from the method.
- */
- private final static String METHOD_SUFFIX = "___";
-
- private boolean isObject;
-
- public TestClassAdapter(ClassVisitor cv, final Map<String, String> typeMappings) {
- super(cv, new Remapper() {
- @Override
- public String map(String type) {
- return typeMappings.containsKey(type) ? typeMappings.get(type) : type;
- }
- });
+ /**
+ * This suffix is automatically removed from the method.
+ */
+ private final static String METHOD_SUFFIX = "___";
+
+ private boolean isObject;
+
+ public TestClassAdapter(ClassVisitor cv, final Map<String, String> typeMappings) {
+ super(cv, new Remapper() {
+ @Override
+ public String map(String type) {
+ return typeMappings.containsKey(type) ? typeMappings.get(type) : type;
+ }
+ });
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ // For java/lang/Object redefinition
+ String newName = remapper.mapType(name);
+ if (newName.equals("java/lang/Object")) {
+ superName = null;
+ isObject = true;
}
-
- @Override
- public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
- // For java/lang/Object redefinition
- String newName = remapper.mapType(name);
- if (newName.equals("java/lang/Object")) {
- superName = null;
- isObject = true;
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ return super.visitMethod(access, stripMethodSuffix(name), desc, signature, exceptions);
+ }
+
+ /**
+ * Get renamed class name.
+ *
+ * @return
+ */
+ public String getClassName() {
+ return remapper.mapType(className);
+ }
+
+ protected MethodVisitor createRemappingMethodAdapter(
+ int access,
+ String newDesc,
+ MethodVisitor mv) {
+ return new RemappingMethodAdapter(access, newDesc, mv, remapper) {
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+ if (name.equals("<init>") && isObject && owner.equals("java/lang/Object")) {
+ return;
}
- super.visit(version, access, name, signature, superName, interfaces);
- }
- @Override
- public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
- return super.visitMethod(access, stripMethodSuffix(name), desc, signature, exceptions);
- }
+ super.visitMethodInsn(opcode, owner, stripMethodSuffix(name), desc, itf);
+ }
+ };
+ }
+
+ private static String stripMethodSuffix(String name) {
+ int pos = name.indexOf(METHOD_SUFFIX);
+ return (pos != -1) ? name.substring(0, pos) : name;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
+ AnnotationVisitor av = super.visitAnnotation(remapper.mapDesc(desc), visible);
+ return av == null ? null : new RemappingAnnotationAdapter(av, remapper) {
+ @Override
+ public void visitEnum(String name, String enumDesc, String value) {
+ if (Type.getType(enumDesc).getClassName().equals(RedefinitionPolicy.class.getName())) {
+ RedefinitionPolicy valueAsEnum = RedefinitionPolicy.valueOf(value);
+ if (Type.getType(desc).getClassName().equals(FieldRedefinitionPolicy.class.getName())) {
+ cv.visitAttribute(new SingleByteAttribute(FieldRedefinitionPolicy.class.getSimpleName(), (byte) valueAsEnum.ordinal()));
+ }
+ if (Type.getType(desc).getClassName().equals(MethodRedefinitionPolicy.class.getName())) {
+ cv.visitAttribute(new SingleByteAttribute(MethodRedefinitionPolicy.class.getSimpleName(), (byte) valueAsEnum.ordinal()));
+ }
+ }
+ super.visitEnum(name, desc, value);
+ }
+ };
+ }
- /**
- * Get renamed class name.
- *
- * @return
- */
- public String getClassName() {
- return remapper.mapType(className);
- }
+ private static class SingleByteAttribute extends Attribute {
+ private byte value;
- protected MethodVisitor createRemappingMethodAdapter(
- int access,
- String newDesc,
- MethodVisitor mv) {
- return new RemappingMethodAdapter(access, newDesc, mv, remapper) {
- @Override
- public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
- if (name.equals("<init>") && isObject && owner.equals("java/lang/Object")) {
- return;
- }
-
- super.visitMethodInsn(opcode, owner, stripMethodSuffix(name), desc, itf);
- }
- };
- }
-
- private static String stripMethodSuffix(String name) {
- int pos = name.indexOf(METHOD_SUFFIX);
- return (pos != -1) ? name.substring(0, pos) : name;
+ public SingleByteAttribute(String name, byte value) {
+ super(name);
+ this.value = value;
}
@Override
- public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
- AnnotationVisitor av = super.visitAnnotation(remapper.mapDesc(desc), visible);
- return av == null ? null : new RemappingAnnotationAdapter(av, remapper) {
- @Override
- public void visitEnum(String name, String enumDesc, String value) {
- if (Type.getType(enumDesc).getClassName().equals(RedefinitionPolicy.class.getName())) {
- RedefinitionPolicy valueAsEnum = RedefinitionPolicy.valueOf(value);
- if (Type.getType(desc).getClassName().equals(FieldRedefinitionPolicy.class.getName())) {
- cv.visitAttribute(new SingleByteAttribute(FieldRedefinitionPolicy.class.getSimpleName(), (byte) valueAsEnum.ordinal()));
- }
- if (Type.getType(desc).getClassName().equals(MethodRedefinitionPolicy.class.getName())) {
- cv.visitAttribute(new SingleByteAttribute(MethodRedefinitionPolicy.class.getSimpleName(), (byte) valueAsEnum.ordinal()));
- }
- }
- super.visitEnum(name, desc, value);
- }
- };
- }
-
- private static class SingleByteAttribute extends Attribute {
- private byte value;
-
- public SingleByteAttribute(String name, byte value) {
- super(name);
- this.value = value;
- }
-
- @Override
- protected ByteVector write(ClassWriter writer, byte[] code, int len, int maxStack, int maxLocals) {
- return new ByteVector().putByte(value);
- }
+ protected ByteVector write(ClassWriter writer, byte[] code, int len, int maxStack, int maxLocals) {
+ return new ByteVector().putByte(value);
}
+ }
}