diff options
Diffstat (limited to 'dcevm/src/main/java/com')
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); } + } } |