aboutsummaryrefslogtreecommitdiffstats
path: root/framework
diff options
context:
space:
mode:
authorIvan Dubrov <idubrov@guidewire.com>2014-04-24 17:02:24 -0700
committerIvan Dubrov <idubrov@guidewire.com>2014-04-24 17:27:05 -0700
commit38ed95d3c0e7043351bcd98edcec840440c2f50c (patch)
tree2e96aab1587260c971f55ad2e3431ab69b6571ab /framework
parentb4a131ffa7877b6c1853a3b0d6cf2dc1391b72d9 (diff)
downloaddcevm-38ed95d3c0e7043351bcd98edcec840440c2f50c.tar.gz
dcevm-38ed95d3c0e7043351bcd98edcec840440c2f50c.zip
Extracting DCEVM patch & code into separate project
Making DCEVM to be toplevel project that clones HotSpot repository, patches the code, builds JVM and tests it.
Diffstat (limited to 'framework')
-rw-r--r--framework/src/main/java/org/dcevm/ClassRedefinitionPolicy.java40
-rw-r--r--framework/src/main/java/org/dcevm/HotSwapTool.java233
-rw-r--r--framework/src/main/java/org/dcevm/InstrumentationRedefiner.java54
-rw-r--r--framework/src/main/java/org/dcevm/JDIRedefiner.java153
-rw-r--r--framework/src/main/java/org/dcevm/Redefiner.java37
-rw-r--r--framework/src/main/java/org/dcevm/TestClassAdapter.java101
6 files changed, 618 insertions, 0 deletions
diff --git a/framework/src/main/java/org/dcevm/ClassRedefinitionPolicy.java b/framework/src/main/java/org/dcevm/ClassRedefinitionPolicy.java
new file mode 100644
index 00000000..32571863
--- /dev/null
+++ b/framework/src/main/java/org/dcevm/ClassRedefinitionPolicy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+package org.dcevm;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @author Kerstin Breiteneder
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+public @interface ClassRedefinitionPolicy {
+
+ // Default value if no alias is set.
+ public final static class NoClass {
+ }
+
+ Class<?> alias() default NoClass.class;
+}
diff --git a/framework/src/main/java/org/dcevm/HotSwapTool.java b/framework/src/main/java/org/dcevm/HotSwapTool.java
new file mode 100644
index 00000000..b622cba9
--- /dev/null
+++ b/framework/src/main/java/org/dcevm/HotSwapTool.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+package org.dcevm;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Type;
+
+import java.io.*;
+import java.lang.instrument.UnmodifiableClassException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * @author Thomas Wuerthinger
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ * @author Ivan Dubrov
+ */
+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 String IDENTIFIER = "___";
+ 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);
+ }
+
+ /**
+ * 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;
+ }
+
+ 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...
+ }
+
+ /**
+ * 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) {
+ assert versionNumber >= 0;
+
+ if (versionNumber == getCurrentVersion(outerClass)) {
+ // Nothing to do!
+ return;
+ }
+
+ Map<String, File> files = findClassesWithVersion(outerClass, versionNumber);
+
+ 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);
+ }
+
+ private static Map<String, File> findClassesWithVersion(Class<?> baseClass, int version) {
+ Map<String, File> classes = new HashMap<String, File>();
+
+ 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 + "'");
+ }
+ File folder = new File(url.getFile());
+ for (File f : folder.listFiles(IsClassFile.INSTANCE)) {
+ String fileName = f.getName();
+ String simpleName = f.getName().substring(0, f.getName().length() - CLASS_FILE_SUFFIX.length());
+ String name = baseClass.getPackage().getName() + '.' + simpleName;
+
+ if (isInnerClass(name, baseClass) && parseClassVersion(fileName) == version) {
+ classes.put(name, f);
+ }
+ }
+ return classes;
+ }
+
+ private enum IsClassFile implements FilenameFilter {
+ INSTANCE;
+
+ @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 name) {
+ int index = name.indexOf(IDENTIFIER);
+ if (index == -1) {
+ return 0;
+ }
+ return Integer.valueOf(name.substring(index + IDENTIFIER.length(), name.length() - CLASS_FILE_SUFFIX.length()));
+ }
+
+ private static String stripVersion(String className) {
+ int index = className.indexOf(IDENTIFIER);
+ if (index == -1) {
+ return className;
+ }
+ return className.substring(0, index);
+ }
+
+ public static void resetTimings() {
+ redefinitionCount = 0;
+ totalTime = 0;
+ }
+
+ public static int getRedefinitionCount() {
+ return redefinitionCount;
+ }
+
+ public static long getTotalTime() {
+ return totalTime;
+ }
+}
diff --git a/framework/src/main/java/org/dcevm/InstrumentationRedefiner.java b/framework/src/main/java/org/dcevm/InstrumentationRedefiner.java
new file mode 100644
index 00000000..247fbb2e
--- /dev/null
+++ b/framework/src/main/java/org/dcevm/InstrumentationRedefiner.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+package org.dcevm;
+
+
+import org.dcevm.agent.InstrumentationAgent;
+
+import java.io.IOException;
+import java.lang.instrument.ClassDefinition;
+import java.lang.instrument.Instrumentation;
+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);
+ }
+
+ @Override
+ public void close() throws IOException {
+ // Do nothing.
+ }
+}
diff --git a/framework/src/main/java/org/dcevm/JDIRedefiner.java b/framework/src/main/java/org/dcevm/JDIRedefiner.java
new file mode 100644
index 00000000..6fc742d4
--- /dev/null
+++ b/framework/src/main/java/org/dcevm/JDIRedefiner.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+package org.dcevm;
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.VirtualMachineManager;
+import com.sun.jdi.connect.AttachingConnector;
+import com.sun.jdi.connect.Connector.Argument;
+import com.sun.jdi.connect.IllegalConnectorArgumentsException;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Utility class for performing class redefinition using JDI.
+ * </li>
+ * </ul>
+ *
+ * @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);
+ }
+
+ @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");
+ }
+
+ // 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);
+ }
+ }
+
+ public void disconnect() {
+ if (vm != null) {
+ vm.dispose();
+ vm = null;
+ }
+ }
+
+ 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);
+ }
+
+ /**
+ * 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 + "'" );
+ }
+}
diff --git a/framework/src/main/java/org/dcevm/Redefiner.java b/framework/src/main/java/org/dcevm/Redefiner.java
new file mode 100644
index 00000000..cd183fe3
--- /dev/null
+++ b/framework/src/main/java/org/dcevm/Redefiner.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+package org.dcevm;
+
+import java.io.Closeable;
+import java.lang.instrument.UnmodifiableClassException;
+import java.util.Map;
+
+/**
+ * Interface to the class redefinition implementation (JDI-based, Instrumenattion API based)
+ *
+ * @author Ivan Dubrov
+ */
+public interface Redefiner extends Closeable {
+ void redefineClasses(Map<Class<?>, byte[]> classes) throws ClassNotFoundException, UnmodifiableClassException;
+}
diff --git a/framework/src/main/java/org/dcevm/TestClassAdapter.java b/framework/src/main/java/org/dcevm/TestClassAdapter.java
new file mode 100644
index 00000000..bd4b2daf
--- /dev/null
+++ b/framework/src/main/java/org/dcevm/TestClassAdapter.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+package org.dcevm;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.commons.Remapper;
+import org.objectweb.asm.commons.RemappingClassAdapter;
+import org.objectweb.asm.commons.RemappingMethodAdapter;
+
+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;
+ }
+ });
+ }
+
+ @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) {
+ if (name.equals("<init>") && isObject && owner.equals("java/lang/Object")) {
+ return;
+ }
+
+ super.visitMethodInsn(opcode, owner, stripMethodSuffix(name), desc);
+ }
+ };
+ }
+
+ private static String stripMethodSuffix(String name) {
+ int pos = name.indexOf(METHOD_SUFFIX);
+ return (pos != -1) ? name.substring(0, pos) : name;
+ }
+}
+