/******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public * License v3.0 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ package cuchaz.enigma.bytecode; import java.util.Map; import java.util.Set; import javassist.ClassMap; import javassist.CtBehavior; import javassist.CtClass; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.InnerClassesAttribute; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.SignatureUpdater; import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; public class ClassRenamer { public static void renameClasses( CtClass c, Map map ) { // build the map used by javassist ClassMap nameMap = new ClassMap(); for( Map.Entry entry : map.entrySet() ) { nameMap.put( entry.getKey().getName(), entry.getValue().getName() ); } c.replaceClassName( nameMap ); // replace simple names in the InnerClasses attribute too ConstPool constants = c.getClassFile().getConstPool(); InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); if( attr != null ) { for( int i=0; i ATTR: %s,%s,%s", classEntry, attr.outerClass( i ), attr.innerClass( i ), attr.innerName( i ) ) ); */ } } } public static Set getAllClassEntries( final CtClass c ) { // get the classes that javassist knows about final Set entries = Sets.newHashSet(); ClassMap map = new ClassMap( ) { @Override public Object get( Object obj ) { if( obj instanceof String ) { String str = (String)obj; // javassist throws a lot of weird things at this map // I either have to implement my on class scanner, or just try to filter out the weirdness // I'm opting to filter out the weirdness for now // skip anything with generic arguments if( str.indexOf( '<' ) >= 0 || str.indexOf( '>' ) >= 0 || str.indexOf( ';' ) >= 0 ) { return null; } // convert path/to/class.inner to path/to/class$inner str = str.replace( '.', '$' ); // remember everything else entries.add( new ClassEntry( str ) ); } return null; } private static final long serialVersionUID = -202160293602070641L; }; c.replaceClassName( map ); return entries; } public static void moveAllClassesOutOfDefaultPackage( CtClass c, String newPackageName ) { // rename all classes Map map = Maps.newHashMap(); for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) ) { if( classEntry.isInDefaultPackage() ) { map.put( classEntry, new ClassEntry( newPackageName + "/" + classEntry.getName() ) ); } } ClassRenamer.renameClasses( c, map ); // TEMP for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) ) { if( classEntry.isInDefaultPackage() ) { throw new Error( "!!! " + classEntry ); } } // TEMP for( CtBehavior behavior : c.getDeclaredBehaviors() ) { if( behavior.getSignature() == null ) { continue; } SignatureUpdater.update( behavior.getSignature(), new ClassNameUpdater( ) { @Override public String update( String className ) { ClassEntry classEntry = new ClassEntry( className ); if( classEntry.isInDefaultPackage() ) { throw new Error( "!!! " + className ); } return className; } } ); } } }