/******************************************************************************* * Copyright (c) 2010 Eric Bodden. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eric Bodden - initial API and implementation ******************************************************************************/ package de.bodden.tamiflex.normalizer; import static de.bodden.tamiflex.normalizer.Hasher.dotted; import static de.bodden.tamiflex.normalizer.Hasher.slashed; import java.util.Map; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.RemappingClassAdapter; /** * Provides functionality to rename references to generated classes, including fully-qualified * type names in String constants. */ public class ClassRenamer { /** * Exception that is thrown if the from/to map contains no entry for the generated class * with name <code>typeName</code>. */ @SuppressWarnings("serial") public static class NoHashedNameException extends RuntimeException{ public NoHashedNameException(String typeName) { super(typeName); } } /** * Renames references to generated classes according to the mapping <code>fromTo</code> * in the bytecode <code>classBytes</code>. * @param fromTo This map must contain, for every generated class c an entry that maps c to * some other valid class name. Generated classes are such classes whose name is matched by * {@link Hasher#containsGeneratedClassName(String)}. * @param classBytes The bytecode in which the renaming should take place. This array wil remain * unmodified. * @return The bytecode containing the renamed references. */ public static byte[] replaceClassNamesInBytes(final Map<String, String> fromTo, byte[] classBytes) { ClassReader creader = new ClassReader(classBytes); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); RemappingClassAdapter visitor = new RemappingClassAdapter(writer,new Remapper(){ //rename a type reference @Override public String map(String typeName) { String newName = fromTo.get(typeName); if(Hasher.containsGeneratedClassName(typeName) && newName==null) { throw new NoHashedNameException(typeName); } if(newName!=null) typeName = newName; return super.map(typeName); } }) { //visit the body of the method @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); mv = new RemappingStringConstantAdapter(mv, new StringRemapper() { //rename any string constants @Override public String remapStringConstant(String constant) { //string constants will refer to the type using a dotted name; replace dots by slashes... String slashed = slashed(constant); String to = fromTo.get(slashed); if(Hasher.containsGeneratedClassName(slashed) && to==null) { throw new NoHashedNameException(slashed); } if(to!=null) constant = dotted(to); return super.remapStringConstant(constant); } }); return mv; } }; creader.accept(visitor, 0); return writer.toByteArray(); } }