From 2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Sat, 19 May 2018 17:02:46 +0200 Subject: Initial port to ASM --- .../java/cuchaz/enigma/bytecode/AccessFlags.java | 79 +++ .../cuchaz/enigma/bytecode/ClassProtectifier.java | 54 +-- .../cuchaz/enigma/bytecode/ClassPublifier.java | 58 +-- .../java/cuchaz/enigma/bytecode/ClassRenamer.java | 539 --------------------- .../cuchaz/enigma/bytecode/ConstPoolEditor.java | 264 ---------- src/main/java/cuchaz/enigma/bytecode/InfoType.java | 266 ---------- .../enigma/bytecode/MethodParametersAttribute.java | 87 ---- .../bytecode/accessors/ClassInfoAccessor.java | 56 --- .../bytecode/accessors/ConstInfoAccessor.java | 124 ----- .../accessors/InvokeDynamicInfoAccessor.java | 75 --- .../bytecode/accessors/MemberRefInfoAccessor.java | 75 --- .../accessors/MethodHandleInfoAccessor.java | 75 --- .../bytecode/accessors/MethodTypeInfoAccessor.java | 57 --- .../accessors/NameAndTypeInfoAccessor.java | 75 --- .../bytecode/accessors/StringInfoAccessor.java | 56 --- .../bytecode/accessors/Utf8InfoAccessor.java | 29 -- .../bytecode/translators/ClassTranslator.java | 161 ------ .../bytecode/translators/InnerClassWriter.java | 144 ------ .../translators/LocalVariableTranslator.java | 142 ------ .../translators/MethodParameterTranslator.java | 62 --- .../translators/TranslationClassVisitor.java | 106 ++++ .../translators/TranslationMethodVisitor.java | 82 ++++ 22 files changed, 324 insertions(+), 2342 deletions(-) create mode 100644 src/main/java/cuchaz/enigma/bytecode/AccessFlags.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/InfoType.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java (limited to 'src/main/java/cuchaz/enigma/bytecode') diff --git a/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java new file mode 100644 index 0000000..0999abf --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java @@ -0,0 +1,79 @@ +package cuchaz.enigma.bytecode; + +import org.objectweb.asm.Opcodes; + +import java.lang.reflect.Modifier; + +public class AccessFlags { + private static final int SYNTHETIC_FLAG = 0x00001000; + private static final int BRIDGED_FLAG = 0x00000040; + + private int flags; + + public AccessFlags(int flags) { + this.flags = flags; + } + + public boolean isPrivate() { + return Modifier.isPrivate(this.flags); + } + + public boolean isProtected() { + return Modifier.isProtected(this.flags); + } + + public boolean isPublic() { + return Modifier.isPublic(this.flags); + } + + public boolean isSynthetic() { + return (this.flags & SYNTHETIC_FLAG) != 0; + } + + public boolean isStatic() { + return Modifier.isStatic(this.flags); + } + + public AccessFlags setPrivate() { + this.setVisibility(Opcodes.ACC_PRIVATE); + return this; + } + + public AccessFlags setProtected() { + this.setVisibility(Opcodes.ACC_PROTECTED); + return this; + } + + public AccessFlags setPublic() { + this.setVisibility(Opcodes.ACC_PUBLIC); + return this; + } + + public AccessFlags setBridged() { + this.setVisibility(BRIDGED_FLAG); + return this; + } + + public void setVisibility(int visibility) { + this.resetVisibility(); + this.flags |= visibility; + } + + private void resetVisibility() { + this.flags &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC); + } + + public int getFlags() { + return this.flags; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags; + } + + @Override + public int hashCode() { + return flags; + } +} diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java index 6ec576e..9ed6db9 100644 --- a/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java +++ b/src/main/java/cuchaz/enigma/bytecode/ClassProtectifier.java @@ -11,41 +11,39 @@ package cuchaz.enigma.bytecode; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtField; -import javassist.bytecode.AccessFlag; -import javassist.bytecode.InnerClassesAttribute; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; -public class ClassProtectifier { +public class ClassProtectifier extends ClassVisitor { - public static CtClass protectify(CtClass c) { - - // protectify all the fields - for (CtField field : c.getDeclaredFields()) { - field.setModifiers(protectify(field.getModifiers())); - } + public ClassProtectifier(int api, ClassVisitor cv) { + super(api, cv); + } - // protectify all the methods and constructors - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - behavior.setModifiers(protectify(behavior.getModifiers())); - } + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + access = protectify(access); + return super.visitMethod(access, name, desc, signature, exceptions); + } - // protectify all the inner classes - InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (attr != null) { - for (int i = 0; i < attr.tableLength(); i++) { - attr.setAccessFlags(i, protectify(attr.accessFlags(i))); - } - } + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + access = protectify(access); + return super.visitField(access, name, desc, signature, value); + } - return c; + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + access = protectify(access); + super.visitInnerClass(name, outerName, innerName, access); } - private static int protectify(int flags) { - if (AccessFlag.isPrivate(flags)) { - flags = AccessFlag.setProtected(flags); + private static int protectify(int access) { + AccessFlags accessFlags = new AccessFlags(access); + if (accessFlags.isPrivate()) { + accessFlags.setProtected(); } - return flags; + return accessFlags.getFlags(); } } diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java index d627fe9..64de788 100644 --- a/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java +++ b/src/main/java/cuchaz/enigma/bytecode/ClassPublifier.java @@ -11,41 +11,45 @@ package cuchaz.enigma.bytecode; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtField; -import javassist.bytecode.AccessFlag; -import javassist.bytecode.InnerClassesAttribute; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; -public class ClassPublifier { +public class ClassPublifier extends ClassVisitor { - public static CtClass publify(CtClass c) { + public ClassPublifier(int api, ClassVisitor cv) { + super(api, cv); + } - // publify all the fields - for (CtField field : c.getDeclaredFields()) { - field.setModifiers(publify(field.getModifiers())); - } + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + access = publify(access); + super.visit(version, access, name, signature, superName, interfaces); + } - // publify all the methods and constructors - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - behavior.setModifiers(publify(behavior.getModifiers())); - } + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + access = publify(access); + return super.visitField(access, name, desc, signature, value); + } - // publify all the inner classes - InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (attr != null) { - for (int i = 0; i < attr.tableLength(); i++) { - attr.setAccessFlags(i, publify(attr.accessFlags(i))); - } - } + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + access = publify(access); + return super.visitMethod(access, name, desc, signature, exceptions); + } - return c; + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + access = publify(access); + super.visitInnerClass(name, outerName, innerName, access); } - private static int publify(int flags) { - if (!AccessFlag.isPublic(flags)) { - flags = AccessFlag.setPublic(flags); + private static int publify(int access) { + AccessFlags accessFlags = new AccessFlags(access); + if (!accessFlags.isPublic()) { + accessFlags.setPublic(); } - return flags; + return accessFlags.getFlags(); } } diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java deleted file mode 100644 index 62a838d..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java +++ /dev/null @@ -1,539 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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 cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.ClassNameReplacer; -import cuchaz.enigma.mapping.Mappings; -import cuchaz.enigma.mapping.Translator; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtField; -import javassist.Modifier; -import javassist.bytecode.*; -import javassist.bytecode.SignatureAttribute.*; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ClassRenamer { - - public static void applyModifier(Object obj, Mappings.EntryModifier modifier) { - int mod = -1; - if (obj instanceof CtField) - mod = ((CtField) obj).getModifiers(); - else if (obj instanceof CtBehavior) - mod = ((CtBehavior) obj).getModifiers(); - else if (obj instanceof CtClass) - mod = ((CtClass) obj).getModifiers(); - - if (mod != -1) { - switch (modifier) { - case PRIVATE: - mod = Modifier.setPrivate(mod); - break; - case PROTECTED: - mod = Modifier.setProtected(mod); - break; - case PUBLIC: - mod = Modifier.setPublic(mod); - break; - default: - break; - } - if (obj instanceof CtField) - ((CtField) obj).setModifiers(mod); - else if (obj instanceof CtBehavior) - ((CtBehavior) obj).setModifiers(mod); - else - ((CtClass) obj).setModifiers(mod); - } - } - - public static void renameClasses(CtClass c, final Translator translator) { - renameClasses(c, className -> { - ClassEntry entry = translator.translateEntry(new ClassEntry(className)); - if (entry != null) { - return entry.getName(); - } - return null; - }); - } - - public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) { - renameClasses(c, className -> { - ClassEntry entry = new ClassEntry(className); - if (entry.isInDefaultPackage()) { - return newPackageName + "/" + entry.getName(); - } - return null; - }); - } - - public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) { - renameClasses(c, className -> { - ClassEntry entry = new ClassEntry(className); - if (entry.getPackageName().equals(oldPackageName)) { - return entry.getSimpleName(); - } - return null; - }); - } - - @SuppressWarnings("unchecked") - public static void renameClasses(CtClass c, ClassNameReplacer replacer) { - - // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =( - - ReplacerClassMap map = new ReplacerClassMap(replacer); - ClassFile classFile = c.getClassFile(); - - // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo) - ConstPool constPool = c.getClassFile().getConstPool(); - constPool.renameClass(map); - - // rename class attributes - renameAttributes(classFile.getAttributes(), map, SignatureType.Class); - - // rename methods - for (MethodInfo methodInfo : (List) classFile.getMethods()) { - methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map)); - renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method); - } - - // rename fields - for (FieldInfo fieldInfo : (List) classFile.getFields()) { - fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map)); - renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field); - } - - // rename the class name itself last - // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass() - // we only want to replace exactly this class name - String newName = renameClassName(c.getName(), map); - if (newName != null) { - c.setName(newName); - } - - // replace simple names in the InnerClasses attribute too - InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (attr != null) { - for (int i = 0; i < attr.tableLength(); i++) { - - String innerName = attr.innerClass(i); - // get the inner class full name (which has already been translated) - ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); - - if (attr.innerNameIndex(i) != 0) { - // update the inner name - attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName())); - } - - /* DEBUG - System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); - */ - } - } - } - - @SuppressWarnings("unchecked") - private static void renameAttributes(List attributes, ReplacerClassMap map, SignatureType type) { - try { - - // make the rename class method accessible - Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class); - renameClassMethod.setAccessible(true); - - for (AttributeInfo attribute : attributes) { - if (attribute instanceof SignatureAttribute) { - // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell - SignatureAttribute signatureAttribute = (SignatureAttribute) attribute; - String newSignature = type.rename(signatureAttribute.getSignature(), map); - if (newSignature != null) { - signatureAttribute.setSignature(newSignature); - } - } else if (attribute instanceof CodeAttribute) { - // code attributes have signature attributes too (indirectly) - CodeAttribute codeAttribute = (CodeAttribute) attribute; - renameAttributes(codeAttribute.getAttributes(), map, type); - } else if (attribute instanceof LocalVariableTypeAttribute) { - // lvt attributes have signature attributes too - LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute; - renameLocalVariableTypeAttribute(localVariableAttribute, map); - } else { - renameClassMethod.invoke(attribute, map); - } - } - - } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - throw new Error("Unable to call javassist methods by reflection!", ex); - } - } - - private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { - - // adapted from LocalVariableAttribute.renameClass() - ConstPool cp = attribute.getConstPool(); - int n = attribute.tableLength(); - byte[] info = attribute.get(); - for (int i = 0; i < n; ++i) { - int pos = i * 10 + 2; - int index = ByteArray.readU16bit(info, pos + 6); - if (index != 0) { - String signature = cp.getUtf8Info(index); - String newSignature = renameLocalVariableSignature(signature, map); - if (newSignature != null) { - ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6); - } - } - } - } - - private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) { - - // for some reason, signatures with . in them don't count as field signatures - // looks like anonymous classes delimit with . in stead of $ - // convert the . to $, but keep track of how many we replace - // we need to put them back after we translate - int start = signature.lastIndexOf('$') + 1; - int numConverted = 0; - StringBuilder buf = new StringBuilder(signature); - for (int i = buf.length() - 1; i >= start; i--) { - char c = buf.charAt(i); - if (c == '.') { - buf.setCharAt(i, '$'); - numConverted++; - } - } - signature = buf.toString(); - - // translate - String newSignature = renameFieldSignature(signature, map); - if (newSignature != null) { - - // put the delimiters back - buf = new StringBuilder(newSignature); - for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) { - char c = buf.charAt(i); - if (c == '$') { - buf.setCharAt(i, '.'); - numConverted--; - } - } - assert (numConverted == 0); - newSignature = buf.toString(); - - return newSignature; - } - - return null; - } - - private static String renameClassSignature(String signature, ReplacerClassMap map) { - try { - ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map); - return type.encode(); - } catch (BadBytecode ex) { - throw new Error("Can't parse field signature: " + signature); - } - } - - private static String renameFieldSignature(String signature, ReplacerClassMap map) { - try { - ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map); - if (type != null) { - return type.encode(); - } - return null; - } catch (BadBytecode ex) { - throw new Error("Can't parse class signature: " + signature); - } - } - - private static String renameMethodSignature(String signature, ReplacerClassMap map) { - try { - MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map); - return type.encode(); - } catch (BadBytecode ex) { - throw new Error("Can't parse method signature: " + signature); - } - } - - private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) { - if (typeParamTypes != null) { - typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length); - for (int i = 0; i < typeParamTypes.length; i++) { - TypeParameter newParamType = renameType(typeParamTypes[i], map); - if (newParamType != null) { - typeParamTypes[i] = newParamType; - } - } - } - return typeParamTypes; - } - - private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) { - - TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map); - - ClassType superclassType = type.getSuperClass(); - if (superclassType != ClassType.OBJECT) { - ClassType newSuperclassType = renameType(superclassType, map); - if (newSuperclassType != null) { - superclassType = newSuperclassType; - } - } - - ClassType[] interfaceTypes = type.getInterfaces(); - if (interfaceTypes != null) { - interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); - for (int i = 0; i < interfaceTypes.length; i++) { - ClassType newInterfaceType = renameType(interfaceTypes[i], map); - if (newInterfaceType != null) { - interfaceTypes[i] = newInterfaceType; - } - } - } - - return new ClassSignature(typeParamTypes, superclassType, interfaceTypes); - } - - private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) { - - TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map); - - Type[] paramTypes = type.getParameterTypes(); - if (paramTypes != null) { - paramTypes = Arrays.copyOf(paramTypes, paramTypes.length); - for (int i = 0; i < paramTypes.length; i++) { - Type newParamType = renameType(paramTypes[i], map); - if (newParamType != null) { - paramTypes[i] = newParamType; - } - } - } - - Type returnType = type.getReturnType(); - if (returnType != null) { - Type newReturnType = renameType(returnType, map); - if (newReturnType != null) { - returnType = newReturnType; - } - } - - ObjectType[] exceptionTypes = type.getExceptionTypes(); - if (exceptionTypes != null) { - exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length); - for (int i = 0; i < exceptionTypes.length; i++) { - ObjectType newExceptionType = renameType(exceptionTypes[i], map); - if (newExceptionType != null) { - exceptionTypes[i] = newExceptionType; - } - } - } - - return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes); - } - - private static Type renameType(Type type, ReplacerClassMap map) { - if (type instanceof ObjectType) { - return renameType((ObjectType) type, map); - } else if (type instanceof BaseType) { - return renameType((BaseType) type, map); - } else { - throw new Error("Don't know how to rename type " + type.getClass()); - } - } - - private static ObjectType renameType(ObjectType type, ReplacerClassMap map) { - if (type instanceof ArrayType) { - return renameType((ArrayType) type, map); - } else if (type instanceof ClassType) { - return renameType((ClassType) type, map); - } else if (type instanceof TypeVariable) { - return renameType((TypeVariable) type, map); - } else { - throw new Error("Don't know how to rename type " + type.getClass()); - } - } - - private static BaseType renameType(BaseType type, ReplacerClassMap map) { - // don't have to rename primitives - return null; - } - - private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) { - // don't have to rename template args - return null; - } - - private static ClassType renameType(ClassType type, ReplacerClassMap map) { - - // translate type args - TypeArgument[] args = type.getTypeArguments(); - if (args != null) { - args = Arrays.copyOf(args, args.length); - for (int i = 0; i < args.length; i++) { - TypeArgument newType = renameType(args[i], map); - if (newType != null) { - args[i] = newType; - } - } - } - - if (type instanceof NestedClassType) { - NestedClassType nestedType = (NestedClassType) type; - - // translate the name - String name = getClassName(type); - String newName = map.get(name); - if (newName != null) { - name = new ClassEntry(newName).getInnermostClassName(); - } - - // translate the parent class too - ClassType parent = renameType(nestedType.getDeclaringClass(), map); - if (parent == null) { - parent = nestedType.getDeclaringClass(); - } - - return new NestedClassType(parent, name, args); - } else { - - // translate the name - String name = type.getName(); - String newName = renameClassName(name, map); - if (newName != null) { - name = newName; - } - - return new ClassType(name, args); - } - } - - private static String getClassName(ClassType type) { - if (type instanceof NestedClassType) { - NestedClassType nestedType = (NestedClassType) type; - return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$')); - } else { - return Descriptor.toJvmName(type.getName()); - } - } - - private static String renameClassName(String name, ReplacerClassMap map) { - String newName = map.get(Descriptor.toJvmName(name)); - if (newName != null) { - return Descriptor.toJavaName(newName); - } - return null; - } - - private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) { - ObjectType subType = type.getType(); - if (subType != null) { - ObjectType newSubType = renameType(subType, map); - if (newSubType != null) { - switch (type.getKind()) { - case ' ': - return new TypeArgument(newSubType); - case '+': - return TypeArgument.subclassOf(newSubType); - case '-': - return TypeArgument.superOf(newSubType); - default: - throw new Error("Unknown type kind: " + type.getKind()); - } - } - } - return null; - } - - private static ArrayType renameType(ArrayType type, ReplacerClassMap map) { - Type newSubType = renameType(type.getComponentType(), map); - if (newSubType != null) { - return new ArrayType(type.getDimension(), newSubType); - } - return null; - } - - private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) { - - ObjectType superclassType = type.getClassBound(); - if (superclassType != null) { - ObjectType newSuperclassType = renameType(superclassType, map); - if (newSuperclassType != null) { - superclassType = newSuperclassType; - } - } - - ObjectType[] interfaceTypes = type.getInterfaceBound(); - if (interfaceTypes != null) { - interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); - for (int i = 0; i < interfaceTypes.length; i++) { - ObjectType newInterfaceType = renameType(interfaceTypes[i], map); - if (newInterfaceType != null) { - interfaceTypes[i] = newInterfaceType; - } - } - } - - return new TypeParameter(type.getName(), superclassType, interfaceTypes); - } - - private enum SignatureType { - Class { - @Override - public String rename(String signature, ReplacerClassMap map) { - return renameClassSignature(signature, map); - } - }, - Field { - @Override - public String rename(String signature, ReplacerClassMap map) { - return renameFieldSignature(signature, map); - } - }, - Method { - @Override - public String rename(String signature, ReplacerClassMap map) { - return renameMethodSignature(signature, map); - } - }; - - public abstract String rename(String signature, ReplacerClassMap map); - } - - private static class ReplacerClassMap extends HashMap { - - private ClassNameReplacer replacer; - - public ReplacerClassMap(ClassNameReplacer replacer) { - this.replacer = replacer; - } - - @Override - public String get(Object obj) { - if (obj instanceof String) { - return get((String) obj); - } - return null; - } - - public String get(String className) { - return replacer.replace(className); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java deleted file mode 100644 index 1932730..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/ConstPoolEditor.java +++ /dev/null @@ -1,264 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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 cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; -import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; -import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; -import javassist.bytecode.ConstPool; -import javassist.bytecode.Descriptor; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.HashMap; - -public class ConstPoolEditor { - - private static Method getItem; - private static Method addItem; - private static Method addItem0; - private static Field items; - private static Field cache; - private static Field numItems; - private static Field objects; - private static Field elements; - private static Method methodWritePool; - private static Constructor constructorPool; - - static { - try { - getItem = ConstPool.class.getDeclaredMethod("getItem", int.class); - getItem.setAccessible(true); - - addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo")); - addItem.setAccessible(true); - - addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo")); - addItem0.setAccessible(true); - - items = ConstPool.class.getDeclaredField("items"); - items.setAccessible(true); - - cache = ConstPool.class.getDeclaredField("itemsCache"); - cache.setAccessible(true); - - numItems = ConstPool.class.getDeclaredField("numOfItems"); - numItems.setAccessible(true); - - objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects"); - objects.setAccessible(true); - - elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements"); - elements.setAccessible(true); - - methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class); - methodWritePool.setAccessible(true); - - constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class); - constructorPool.setAccessible(true); - } catch (Exception ex) { - throw new Error(ex); - } - } - - private ConstPool pool; - - public ConstPoolEditor(ConstPool pool) { - this.pool = pool; - } - - public static ConstPool readPool(DataInputStream in) { - try { - return constructorPool.newInstance(in); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public static ConstPool newConstPool() { - // const pool expects the name of a class to initialize itself - // but we want an empty pool - // so give it a bogus name, and then clear the entries afterwards - ConstPool pool = new ConstPool("a"); - - ConstPoolEditor editor = new ConstPoolEditor(pool); - int size = pool.getSize(); - for (int i = 0; i < size - 1; i++) { - editor.removeLastItem(); - } - - // make sure the pool is actually empty - // although, in this case "empty" means one thing in it - // the JVM spec says index 0 should be reserved - assert (pool.getSize() == 1); - assert (editor.getItem(0) == null); - assert (editor.getItem(1) == null); - assert (editor.getItem(2) == null); - assert (editor.getItem(3) == null); - - // also, clear the cache - editor.getCache().clear(); - - return pool; - } - - public void writePool(DataOutputStream out) { - try { - methodWritePool.invoke(this.pool, out); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public String getMemberrefClassname(int memberrefIndex) { - return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex))); - } - - public String getMemberrefName(int memberrefIndex) { - return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex))); - } - - public String getMemberrefType(int memberrefIndex) { - return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex))); - } - - public ConstInfoAccessor getItem(int index) { - try { - Object entry = getItem.invoke(this.pool, index); - if (entry == null) { - return null; - } - return new ConstInfoAccessor(entry); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public int addItem(Object item) { - try { - return (Integer) addItem.invoke(this.pool, item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public int addItemForceNew(Object item) { - try { - return (Integer) addItem0.invoke(this.pool, item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - @SuppressWarnings("rawtypes") - public void removeLastItem() { - try { - // remove the item from the cache - HashMap cache = getCache(); - if (cache != null) { - Object item = getItem(this.pool.getSize() - 1); - cache.remove(item); - } - - // remove the actual item - // based off of LongVector.addElement() - Object item = items.get(this.pool); - Object[][] object = (Object[][]) objects.get(items); - int numElements = (Integer) elements.get(items) - 1; - int nth = numElements >> 7; - int offset = numElements & (128 - 1); - object[nth][offset] = null; - - // decrement the number of items - elements.set(item, numElements); - numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1); - } catch (Exception ex) { - throw new Error(ex); - } - } - - @SuppressWarnings("rawtypes") - public HashMap getCache() { - try { - return (HashMap) cache.get(this.pool); - } catch (Exception ex) { - throw new Error(ex); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) { - // NOTE: when changing values, we always need to copy-on-write - try { - // get the memberref item - Object item = getItem(memberrefIndex).getItem(); - - // update the cache - HashMap cache = getCache(); - if (cache != null) { - cache.remove(item); - } - - new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType)); - - // update the cache - if (cache != null) { - cache.put(item, item); - } - } catch (Exception ex) { - throw new Error(ex); - } - - // make sure the change worked - assert (newName.equals(getMemberrefName(memberrefIndex))); - assert (newType.equals(getMemberrefType(memberrefIndex))); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void changeClassName(int classNameIndex, String newName) { - // NOTE: when changing values, we always need to copy-on-write - try { - // get the class item - Object item = getItem(classNameIndex).getItem(); - - // update the cache - HashMap cache = getCache(); - if (cache != null) { - cache.remove(item); - } - - // add the new name and repoint the name-and-type to it - new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName)); - - // update the cache - if (cache != null) { - cache.put(item, item); - } - } catch (Exception ex) { - throw new Error(ex); - } - } - - public String dump() { - StringBuilder buf = new StringBuilder(); - for (int i = 1; i < this.pool.getSize(); i++) { - buf.append(String.format("%4d", i)); - buf.append(" "); - buf.append(getItem(i)); - buf.append("\n"); - } - return buf.toString(); - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/InfoType.java b/src/main/java/cuchaz/enigma/bytecode/InfoType.java deleted file mode 100644 index 9013d58..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/InfoType.java +++ /dev/null @@ -1,266 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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 com.google.common.collect.Maps; -import cuchaz.enigma.bytecode.accessors.*; - -import java.util.Collection; -import java.util.Map; - -public enum InfoType { - - Utf8Info(1), - IntegerInfo(3), - FloatInfo(4), - LongInfo(5), - DoubleInfo(6), - ClassInfo(7) { - @Override - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); - gatherIndexTree(indices, editor, accessor.getNameIndex()); - } - - @Override - public void remapIndices(Map map, ConstInfoAccessor entry) { - ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); - accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); - } - - @Override - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); - ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); - return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); - } - }, - StringInfo(8) { - @Override - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); - gatherIndexTree(indices, editor, accessor.getStringIndex()); - } - - @Override - public void remapIndices(Map map, ConstInfoAccessor entry) { - StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); - accessor.setStringIndex(remapIndex(map, accessor.getStringIndex())); - } - - @Override - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); - ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex()); - return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); - } - }, - FieldRefInfo(9) { - @Override - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); - gatherIndexTree(indices, editor, accessor.getClassIndex()); - gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); - } - - @Override - public void remapIndices(Map map, ConstInfoAccessor entry) { - MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); - accessor.setClassIndex(remapIndex(map, accessor.getClassIndex())); - accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); - } - - @Override - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); - ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex()); - ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); - return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); - } - }, - // same as FieldRefInfo - MethodRefInfo(10) { - @Override - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - FieldRefInfo.gatherIndexTree(indices, editor, entry); - } - - @Override - public void remapIndices(Map map, ConstInfoAccessor entry) { - FieldRefInfo.remapIndices(map, entry); - } - - @Override - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - return FieldRefInfo.subIndicesAreValid(entry, pool); - } - }, - // same as FieldRefInfo - InterfaceMethodRefInfo(11) { - @Override - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - FieldRefInfo.gatherIndexTree(indices, editor, entry); - } - - @Override - public void remapIndices(Map map, ConstInfoAccessor entry) { - FieldRefInfo.remapIndices(map, entry); - } - - @Override - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - return FieldRefInfo.subIndicesAreValid(entry, pool); - } - }, - NameAndTypeInfo(12) { - @Override - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); - gatherIndexTree(indices, editor, accessor.getNameIndex()); - gatherIndexTree(indices, editor, accessor.getTypeIndex()); - } - - @Override - public void remapIndices(Map map, ConstInfoAccessor entry) { - NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); - accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); - accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); - } - - @Override - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); - ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); - ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); - return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); - } - }, - MethodHandleInfo(15) { - @Override - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); - gatherIndexTree(indices, editor, accessor.getTypeIndex()); - gatherIndexTree(indices, editor, accessor.getMethodRefIndex()); - } - - @Override - public void remapIndices(Map map, ConstInfoAccessor entry) { - MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); - accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); - accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex())); - } - - @Override - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); - ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); - ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex()); - return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); - } - }, - MethodTypeInfo(16) { - @Override - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); - gatherIndexTree(indices, editor, accessor.getTypeIndex()); - } - - @Override - public void remapIndices(Map map, ConstInfoAccessor entry) { - MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); - accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); - } - - @Override - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); - ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); - return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); - } - }, - InvokeDynamicInfo(18) { - @Override - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); - gatherIndexTree(indices, editor, accessor.getBootstrapIndex()); - gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); - } - - @Override - public void remapIndices(Map map, ConstInfoAccessor entry) { - InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); - accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex())); - accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); - } - - @Override - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); - ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex()); - ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); - return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); - } - }; - - private static Map types; - - static { - types = Maps.newTreeMap(); - for (InfoType type : values()) { - types.put(type.getTag(), type); - } - } - - private int tag; - - InfoType(int tag) { - this.tag = tag; - } - - public static InfoType getByTag(int tag) { - return types.get(tag); - } - - public static void gatherIndexTree(Collection indices, ConstPoolEditor editor, int index) { - // add own index - indices.add(index); - - // recurse - ConstInfoAccessor entry = editor.getItem(index); - entry.getType().gatherIndexTree(indices, editor, entry); - } - - private static int remapIndex(Map map, int index) { - Integer newIndex = map.get(index); - if (newIndex == null) { - newIndex = index; - } - return newIndex; - } - - public int getTag() { - return this.tag; - } - - public void gatherIndexTree(Collection indices, ConstPoolEditor editor, ConstInfoAccessor entry) { - // by default, do nothing - } - - public void remapIndices(Map map, ConstInfoAccessor entry) { - // by default, do nothing - } - - public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { - // by default, everything is good - return true; - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java b/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java deleted file mode 100644 index 3f819ab..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/MethodParametersAttribute.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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 javassist.bytecode.AttributeInfo; -import javassist.bytecode.ConstPool; -import javassist.bytecode.MethodInfo; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class MethodParametersAttribute extends AttributeInfo { - - private MethodParametersAttribute(ConstPool pool, List parameterNameIndices) { - super(pool, "MethodParameters", writeStruct(parameterNameIndices)); - } - - public static void updateClass(MethodInfo info, List names) { - - // add the names to the class const pool - ConstPool constPool = info.getConstPool(); - List parameterNameIndices = new ArrayList<>(); - for (String name : names) { - if (name != null) { - parameterNameIndices.add(constPool.addUtf8Info(name)); - } else { - parameterNameIndices.add(0); - } - } - - // add the attribute to the method - info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices)); - } - - private static byte[] writeStruct(List parameterNameIndices) { - // JVM 8 Spec says the struct looks like this: - // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24 - // uint8 num_params - // for each param: - // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry - // uint16 access_flags -> don't care, just set to 0 - - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(buf); - - // NOTE: java hates unsigned integers, so we have to be careful here - // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument - // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte - // if the int is out of range, the byte stream won't look the way we want and weird things will happen - final int SIZEOF_UINT8 = 1; - final int SIZEOF_UINT16 = 2; - final int MAX_UINT8 = (1 << 8) - 1; - final int MAX_UINT16 = (1 << 16) - 1; - - try { - assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8); - out.writeByte(parameterNameIndices.size()); - - for (Integer index : parameterNameIndices) { - assert (index >= 0 && index <= MAX_UINT16); - out.writeShort(index); - - // just write 0 for the access flags - out.writeShort(0); - } - - out.close(); - byte[] data = buf.toByteArray(); - assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16)); - return data; - } catch (IOException ex) { - throw new Error(ex); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java deleted file mode 100644 index eaa6e90..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.accessors; - -import java.lang.reflect.Field; - -public class ClassInfoAccessor { - - private static Class clazz; - private static Field nameIndex; - - static { - try { - clazz = Class.forName("javassist.bytecode.ClassInfo"); - nameIndex = clazz.getDeclaredField("name"); - nameIndex.setAccessible(true); - } catch (Exception ex) { - throw new Error(ex); - } - } - - private Object item; - - public ClassInfoAccessor(Object item) { - this.item = item; - } - - public static boolean isType(ConstInfoAccessor accessor) { - return clazz.isAssignableFrom(accessor.getItem().getClass()); - } - - public int getNameIndex() { - try { - return (Integer) nameIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setNameIndex(int val) { - try { - nameIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java deleted file mode 100644 index 27d991a..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java +++ /dev/null @@ -1,124 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.accessors; - -import com.google.common.base.Charsets; -import cuchaz.enigma.bytecode.InfoType; - -import java.io.*; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -public class ConstInfoAccessor { - - private static Class clazz; - private static Field index; - private static Method getTag; - - static { - try { - clazz = Class.forName("javassist.bytecode.ConstInfo"); - index = clazz.getDeclaredField("index"); - index.setAccessible(true); - getTag = clazz.getMethod("getTag"); - getTag.setAccessible(true); - } catch (Exception ex) { - throw new Error(ex); - } - } - - private Object item; - - public ConstInfoAccessor(Object item) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null!"); - } - this.item = item; - } - - public Object getItem() { - return this.item; - } - - public int getIndex() { - try { - return (Integer) index.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public int getTag() { - try { - return (Integer) getTag.invoke(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public ConstInfoAccessor copy() { - return new ConstInfoAccessor(copyItem()); - } - - public Object copyItem() { - // I don't know of a simpler way to copy one of these silly things... - try { - // serialize the item - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(buf); - write(out); - - // deserialize the item - DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray())); - Object item = new ConstInfoAccessor(in).getItem(); - in.close(); - - return item; - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void write(DataOutputStream out) throws IOException { - try { - out.writeUTF(this.item.getClass().getName()); - out.writeInt(getIndex()); - - Method method = this.item.getClass().getMethod("write", DataOutputStream.class); - method.setAccessible(true); - method.invoke(this.item, out); - } catch (IOException ex) { - throw ex; - } catch (Exception ex) { - throw new Error(ex); - } - } - - @Override - public String toString() { - try { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8)); - Method print = this.item.getClass().getMethod("print", PrintWriter.class); - print.setAccessible(true); - print.invoke(this.item, out); - out.close(); - return buf.toString("UTF-8").replace("\n", ""); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public InfoType getType() { - return InfoType.getByTag(getTag()); - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java deleted file mode 100644 index aef3532..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.accessors; - -import java.lang.reflect.Field; - -public class InvokeDynamicInfoAccessor { - - private static Class clazz; - private static Field bootstrapIndex; - private static Field nameAndTypeIndex; - - static { - try { - clazz = Class.forName("javassist.bytecode.InvokeDynamicInfo"); - bootstrapIndex = clazz.getDeclaredField("bootstrap"); - bootstrapIndex.setAccessible(true); - nameAndTypeIndex = clazz.getDeclaredField("nameAndType"); - nameAndTypeIndex.setAccessible(true); - } catch (Exception ex) { - throw new Error(ex); - } - } - - private Object item; - - public InvokeDynamicInfoAccessor(Object item) { - this.item = item; - } - - public static boolean isType(ConstInfoAccessor accessor) { - return clazz.isAssignableFrom(accessor.getItem().getClass()); - } - - public int getBootstrapIndex() { - try { - return (Integer) bootstrapIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setBootstrapIndex(int val) { - try { - bootstrapIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public int getNameAndTypeIndex() { - try { - return (Integer) nameAndTypeIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setNameAndTypeIndex(int val) { - try { - nameAndTypeIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java deleted file mode 100644 index 058bb45..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.accessors; - -import java.lang.reflect.Field; - -public class MemberRefInfoAccessor { - - private static Class clazz; - private static Field classIndex; - private static Field nameAndTypeIndex; - - static { - try { - clazz = Class.forName("javassist.bytecode.MemberrefInfo"); - classIndex = clazz.getDeclaredField("classIndex"); - classIndex.setAccessible(true); - nameAndTypeIndex = clazz.getDeclaredField("nameAndTypeIndex"); - nameAndTypeIndex.setAccessible(true); - } catch (Exception ex) { - throw new Error(ex); - } - } - - private Object item; - - public MemberRefInfoAccessor(Object item) { - this.item = item; - } - - public static boolean isType(ConstInfoAccessor accessor) { - return clazz.isAssignableFrom(accessor.getItem().getClass()); - } - - public int getClassIndex() { - try { - return (Integer) classIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setClassIndex(int val) { - try { - classIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public int getNameAndTypeIndex() { - try { - return (Integer) nameAndTypeIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setNameAndTypeIndex(int val) { - try { - nameAndTypeIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java deleted file mode 100644 index 985e792..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.accessors; - -import java.lang.reflect.Field; - -public class MethodHandleInfoAccessor { - - private static Class clazz; - private static Field kindIndex; - private static Field indexIndex; - - static { - try { - clazz = Class.forName("javassist.bytecode.MethodHandleInfo"); - kindIndex = clazz.getDeclaredField("refKind"); - kindIndex.setAccessible(true); - indexIndex = clazz.getDeclaredField("refIndex"); - indexIndex.setAccessible(true); - } catch (Exception ex) { - throw new Error(ex); - } - } - - private Object item; - - public MethodHandleInfoAccessor(Object item) { - this.item = item; - } - - public static boolean isType(ConstInfoAccessor accessor) { - return clazz.isAssignableFrom(accessor.getItem().getClass()); - } - - public int getTypeIndex() { - try { - return (Integer) kindIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setTypeIndex(int val) { - try { - kindIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public int getMethodRefIndex() { - try { - return (Integer) indexIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setMethodRefIndex(int val) { - try { - indexIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java deleted file mode 100644 index 10b0cb0..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.accessors; - -import java.lang.reflect.Field; - -public class MethodTypeInfoAccessor { - - private static Class clazz; - private static Field descriptorIndex; - - static { - try { - clazz = Class.forName("javassist.bytecode.MethodTypeInfo"); - descriptorIndex = clazz.getDeclaredField("descriptor"); - descriptorIndex.setAccessible(true); - } catch (Exception ex) { - throw new Error(ex); - } - } - - private Object item; - - public MethodTypeInfoAccessor(Object item) { - this.item = item; - } - - public static boolean isType(ConstInfoAccessor accessor) { - return clazz.isAssignableFrom(accessor.getItem().getClass()); - } - - public int getTypeIndex() { - try { - return (Integer) descriptorIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setTypeIndex(int val) { - try { - descriptorIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } - -} diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java deleted file mode 100644 index cc7fdbe..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.accessors; - -import java.lang.reflect.Field; - -public class NameAndTypeInfoAccessor { - - private static Class clazz; - private static Field nameIndex; - private static Field typeIndex; - - static { - try { - clazz = Class.forName("javassist.bytecode.NameAndTypeInfo"); - nameIndex = clazz.getDeclaredField("memberName"); - nameIndex.setAccessible(true); - typeIndex = clazz.getDeclaredField("typeDescriptor"); - typeIndex.setAccessible(true); - } catch (Exception ex) { - throw new Error(ex); - } - } - - private Object item; - - public NameAndTypeInfoAccessor(Object item) { - this.item = item; - } - - public static boolean isType(ConstInfoAccessor accessor) { - return clazz.isAssignableFrom(accessor.getItem().getClass()); - } - - public int getNameIndex() { - try { - return (Integer) nameIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setNameIndex(int val) { - try { - nameIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public int getTypeIndex() { - try { - return (Integer) typeIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setTypeIndex(int val) { - try { - typeIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java deleted file mode 100644 index 5c68d4a..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.accessors; - -import java.lang.reflect.Field; - -public class StringInfoAccessor { - - private static Class clazz; - private static Field stringIndex; - - static { - try { - clazz = Class.forName("javassist.bytecode.StringInfo"); - stringIndex = clazz.getDeclaredField("string"); - stringIndex.setAccessible(true); - } catch (Exception ex) { - throw new Error(ex); - } - } - - private Object item; - - public StringInfoAccessor(Object item) { - this.item = item; - } - - public static boolean isType(ConstInfoAccessor accessor) { - return clazz.isAssignableFrom(accessor.getItem().getClass()); - } - - public int getStringIndex() { - try { - return (Integer) stringIndex.get(this.item); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public void setStringIndex(int val) { - try { - stringIndex.set(this.item, val); - } catch (Exception ex) { - throw new Error(ex); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java deleted file mode 100644 index cc3b41b..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.accessors; - -public class Utf8InfoAccessor { - - private static Class clazz; - - static { - try { - clazz = Class.forName("javassist.bytecode.Utf8Info"); - } catch (Exception ex) { - throw new Error(ex); - } - } - - public static boolean isType(ConstInfoAccessor accessor) { - return clazz.isAssignableFrom(accessor.getItem().getClass()); - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java deleted file mode 100644 index 4ac5a8b..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java +++ /dev/null @@ -1,161 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.translators; - -import cuchaz.enigma.bytecode.ClassRenamer; -import cuchaz.enigma.bytecode.ConstPoolEditor; -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtField; -import javassist.CtMethod; -import javassist.bytecode.*; - -public class ClassTranslator { - - public static void translate(Translator translator, CtClass c) { - - // NOTE: the order of these translations is very important - - // translate all the field and method references in the code by editing the constant pool - ConstPool constants = c.getClassFile().getConstPool(); - ConstPoolEditor editor = new ConstPoolEditor(constants); - for (int i = 1; i < constants.getSize(); i++) { - switch (constants.getTag(i)) { - - case ConstPool.CONST_Fieldref: { - - // translate the name and type - FieldEntry entry = EntryFactory.getFieldEntry( - Descriptor.toJvmName(constants.getFieldrefClassName(i)), - constants.getFieldrefName(i), - constants.getFieldrefType(i) - ); - FieldEntry translatedEntry = translator.translateEntry(entry); - if (!entry.equals(translatedEntry)) { - editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); - } - } - break; - - case ConstPool.CONST_Methodref: - case ConstPool.CONST_InterfaceMethodref: { - - // translate the name and type (ie signature) - BehaviorEntry entry = EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(editor.getMemberrefClassname(i)), - editor.getMemberrefName(i), - editor.getMemberrefType(i) - ); - BehaviorEntry translatedEntry = translator.translateEntry(entry); - if (!entry.equals(translatedEntry)) { - editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); - } - } - break; - default: - break; - } - } - - ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); - Mappings.EntryModifier modifier = translator.getModifier(classEntry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(c, modifier); - - // translate all the fields - for (CtField field : c.getDeclaredFields()) { - - // translate the name - FieldEntry entry = EntryFactory.getFieldEntry(field); - String translatedName = translator.translate(entry); - modifier = translator.getModifier(entry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(field, modifier); - - if (translatedName != null) { - field.setName(translatedName); - } - - // translate the type - Type translatedType = translator.translateType(entry.getType()); - field.getFieldInfo().setDescriptor(translatedType.toString()); - } - - // translate all the methods and constructors - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); - - modifier = translator.getModifier(entry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(behavior, modifier); - - if (behavior instanceof CtMethod) { - CtMethod method = (CtMethod) behavior; - - // translate the name - String translatedName = translator.translate(entry); - if (translatedName != null) { - method.setName(translatedName); - } - } - - if (entry.getSignature() != null) { - // translate the signature - Signature translatedSignature = translator.translateSignature(entry.getSignature()); - behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); - } - } - - // translate the EnclosingMethod attribute - EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); - if (enclosingMethodAttr != null) { - - if (enclosingMethodAttr.methodIndex() == 0) { - BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); - BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry); - c.getClassFile().addAttribute(new EnclosingMethodAttribute( - constants, - deobfBehaviorEntry.getClassName() - )); - } else { - BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(enclosingMethodAttr.className()), - enclosingMethodAttr.methodName(), - enclosingMethodAttr.methodDescriptor() - ); - BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry); - c.getClassFile().addAttribute(new EnclosingMethodAttribute( - constants, - deobfBehaviorEntry.getClassName(), - deobfBehaviorEntry.getName(), - deobfBehaviorEntry.getSignature().toString() - )); - } - } - - // translate all the class names referenced in the code - // the above code only changed method/field/reference names and types, but not the rest of the class references - ClassRenamer.renameClasses(c, translator); - - // translate the source file attribute too - ClassEntry deobfClassEntry = translator.translateEntry(classEntry); - if (deobfClassEntry != null) { - String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; - c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); - } - InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (attr != null) - InnerClassWriter.changeModifier(c, attr, translator); - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java deleted file mode 100644 index 0e35938..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java +++ /dev/null @@ -1,144 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.translators; - -import com.google.common.collect.Lists; -import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.bytecode.ClassRenamer; -import cuchaz.enigma.mapping.*; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.NotFoundException; -import javassist.bytecode.*; - -import java.util.Collection; -import java.util.List; - -public class InnerClassWriter { - - // FIXME: modifier is not applied to inner class - public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) { - ClassPool pool = c.getClassPool(); - for (int i = 0; i < attr.tableLength(); i++) { - - String innerName = attr.innerClass(i); - // get the inner class full name (which has already been translated) - ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); - try { - CtClass innerClass = pool.get(innerName); - Mappings.EntryModifier modifier = translator.getModifier(classEntry); - if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) - ClassRenamer.applyModifier(innerClass, modifier); - } catch (NotFoundException e) { - // This shouldn't be possible in theory - //e.printStackTrace(); - } - } - } - - public static void write(JarIndex index, CtClass c) { - - // don't change anything if there's already an attribute there - InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (oldAttr != null) { - // bail! - return; - } - - ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); - List obfClassChain = index.getObfClassChain(obfClassEntry); - - boolean isInnerClass = obfClassChain.size() > 1; - if (isInnerClass) { - - // it's an inner class, rename it to the fully qualified name - c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); - - BehaviorEntry caller = index.getAnonymousClassCaller(obfClassEntry); - if (caller != null) { - - // write the enclosing method attribute - if (caller.getName().equals("")) { - c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); - } else { - c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); - } - } - } - - // does this class have any inner classes? - Collection obfInnerClassEntries = index.getInnerClasses(obfClassEntry); - - if (isInnerClass || !obfInnerClassEntries.isEmpty()) { - - // create an inner class attribute - InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); - c.getClassFile().addAttribute(attr); - - // write the ancestry, but not the outermost class - for (int i = 1; i < obfClassChain.size(); i++) { - ClassEntry obfInnerClassEntry = obfClassChain.get(i); - writeInnerClass(index, attr, obfClassChain, obfInnerClassEntry); - - // update references to use the fully qualified inner class name - c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); - } - - // write the inner classes - for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { - - // extend the class chain - List extendedObfClassChain = Lists.newArrayList(obfClassChain); - extendedObfClassChain.add(obfInnerClassEntry); - - writeInnerClass(index, attr, extendedObfClassChain, obfInnerClassEntry); - - // update references to use the fully qualified inner class name - c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); - } - } - } - - private static void writeInnerClass(JarIndex index, InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { - - // get the new inner class name - ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); - ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); - - // here's what the JVM spec says about the InnerClasses attribute - // append(inner, parent, 0 if anonymous else simple name, flags); - - // update the attribute with this inner class - ConstPool constPool = attr.getConstPool(); - int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); - int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); - int innerClassNameIndex = 0; - int accessFlags = AccessFlag.PUBLIC; - // TODO: need to figure out if we can put static or not - if (!index.isAnonymousClass(obfClassEntry)) { - innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); - } - - attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); - - /* DEBUG - System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", - obfClassEntry, - attr.innerClass(attr.tableLength() - 1), - attr.outerClass(attr.tableLength() - 1), - attr.innerName(attr.tableLength() - 1), - Constants.NonePackage + "/" + obfInnerClassName, - obfClassEntry.getName() - )); - */ - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java deleted file mode 100644 index 51b3d2d..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.translators; - -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.bytecode.*; - -public class LocalVariableTranslator { - - public static void translate(Translator translator, CtClass c) { - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - // if there's a local variable table, just rename everything to v1, v2, v3, ... for now - CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); - if (codeAttribute == null) { - continue; - } - - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - ConstPool constants = c.getClassFile().getConstPool(); - - LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); - if (table != null) { - renameLVT(translator, behaviorEntry, constants, table, c); - } - - LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); - if (typeTable != null) { - renameLVTT(typeTable, table); - } - } - } - - // DEBUG - @SuppressWarnings("unused") - private static void dumpTable(LocalVariableAttribute table) { - for (int i = 0; i < table.tableLength(); i++) { - System.out.println(String.format("\t%d (%d): %s %s", - i, table.index(i), table.variableName(i), table.descriptor(i) - )); - } - } - - private static void renameLVT(Translator translator, BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) { - - // skip empty tables - if (table.tableLength() <= 0) { - return; - } - - // where do we start counting variables? - int starti = 0; - if (table.variableName(0).equals("this")) { - // skip the "this" variable - starti++; - } - - // rename method arguments first - int numArgs = 0; - if (behaviorEntry.getSignature() != null) { - numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); - boolean isNestedClassConstructor = false; - - // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! - if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) { - // Get the first arg type - Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); - - // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class - if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { - isNestedClassConstructor = true; - numArgs--; - } - } - - for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { - int argi = i - starti; - if (ctClass.isEnum()) - argi += 2; - String argName = translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); - if (argName == null) { - int argIndex = isNestedClassConstructor ? argi + 1 : argi; - if (ctClass.isEnum()) - argIndex -= 2; - Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex); - // Unfortunately each of these have different name getters, so they have different code paths - if (argType.isPrimitive()) { - Type.Primitive argCls = argType.getPrimitive(); - argName = "a" + argCls.name() + (argIndex + 1); - } else if (argType.isArray()) { - // List types would require this whole block again, so just go with aListx - argName = "aList" + (argIndex + 1); - } else if (argType.isClass()) { - ClassEntry argClsTrans = translator.translateEntry(argType.getClassEntry()); - argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1); - } else { - argName = "a" + (argIndex + 1); - } - } - renameVariable(table, i, constants.addUtf8Info(argName)); - } - } - - // then rename the rest of the args, if any - for (int i = starti + numArgs; i < table.tableLength(); i++) { - int firstIndex = Math.min(table.index(starti + numArgs), table.index(i)); - renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); - } - } - - private static void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { - // rename args to the same names as in the LVT - for (int i = 0; i < typeTable.tableLength(); i++) { - renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); - } - } - - private static void renameVariable(LocalVariableAttribute table, int i, int stringId) { - // based off of LocalVariableAttribute.nameIndex() - ByteArray.write16bit(stringId, table.get(), i * 10 + 6); - } - - private static int getNameIndex(LocalVariableAttribute table, int index) { - for (int i = 0; i < table.tableLength(); i++) { - if (table.index(i) == index) { - return table.nameIndex(i); - } - } - return 0; - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java deleted file mode 100644 index 4e632b9..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.translators; - -import cuchaz.enigma.bytecode.MethodParametersAttribute; -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.bytecode.CodeAttribute; -import javassist.bytecode.LocalVariableAttribute; - -import java.util.ArrayList; -import java.util.List; - -public class MethodParameterTranslator { - - public static void translate(Translator translator, CtClass c) { - - // Procyon will read method arguments from the "MethodParameters" attribute, so write those - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - // if there's a local variable table here, don't write a MethodParameters attribute - // let the local variable writer deal with it instead - // procyon starts doing really weird things if we give it both attributes - CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); - if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { - continue; - } - - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - - // get the number of arguments - Signature signature = behaviorEntry.getSignature(); - if (signature == null) { - // static initializers have no signatures, or arguments - continue; - } - int numParams = signature.getArgumentTypes().size(); - if (numParams <= 0) { - continue; - } - - // get the list of argument names - List names = new ArrayList<>(numParams); - for (int i = 0; i < numParams; i++) { - names.add(translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); - } - - // save the mappings to the class - MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java new file mode 100644 index 0000000..24bfe24 --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2015 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.translators; + +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.bytecode.AccessFlags; +import cuchaz.enigma.mapping.*; +import org.objectweb.asm.*; + +import java.util.regex.Pattern; + +public class TranslationClassVisitor extends ClassVisitor { + private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*"); + + private final Translator translator; + private final JarIndex jarIndex; + private final ReferencedEntryPool entryPool; + + private ClassDefEntry obfClassEntry; + + public TranslationClassVisitor(Translator translator, JarIndex jarIndex, ReferencedEntryPool entryPool, int api, ClassVisitor cv) { + super(api, cv); + this.translator = translator; + this.jarIndex = jarIndex; + this.entryPool = entryPool; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if (signature != null && OBJECT_PATTERN.matcher(signature).matches()) { + signature = signature.replaceAll(":Ljava/lang/Object;:", "::"); + } + obfClassEntry = new ClassDefEntry(name, new AccessFlags(access)); + ClassDefEntry entry = translator.getTranslatedClassDef(obfClassEntry); + ClassEntry superEntry = translator.getTranslatedClass(entryPool.getClass(superName)); + String[] translatedInterfaces = new String[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + translatedInterfaces[i] = translator.getTranslatedClass(entryPool.getClass(interfaces[i])).getName(); + } + super.visit(version, entry.getAccess().getFlags(), entry.getName(), signature, superEntry.getName(), translatedInterfaces); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + FieldDefEntry entry = new FieldDefEntry(obfClassEntry, name, new TypeDescriptor(desc), new AccessFlags(access)); + FieldDefEntry translatedEntry = translator.getTranslatedFieldDef(entry); + return super.visitField(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), signature, value); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodDefEntry entry = new MethodDefEntry(obfClassEntry, name, new MethodDescriptor(desc), new AccessFlags(access)); + MethodDefEntry translatedEntry = translator.getTranslatedMethodDef(entry); + if (jarIndex.getBridgedMethod(entry) != null) { + translatedEntry.getAccess().setBridged(); + } + MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), name, desc, signature, exceptions); + return new TranslationMethodVisitor(translator, translatedEntry, api, mv); + } + + @Override + public void visitOuterClass(String owner, String name, String desc) { + if (name != null) { + ClassEntry ownerEntry = translator.getTranslatedClass(entryPool.getClass(owner)); + ClassEntry entry = translator.getTranslatedClass(entryPool.getClass(name)); + String translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)).toString(); + super.visitOuterClass(ownerEntry.getName(), entry.getName(), translatedDesc); + } else { + super.visitOuterClass(owner, name, desc); + } + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + // TODO: Implement + return super.visitAnnotation(desc, visible); + } + + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { + // TODO: Implement + return super.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + // If this is not an anonymous class + if (innerName != null && outerName != null) { + ClassDefEntry translatedEntry = translator.getTranslatedClassDef(new ClassDefEntry(innerName, new AccessFlags(access))); + ClassEntry outerEntry = translator.getTranslatedClass(entryPool.getClass(outerName)); + ClassEntry innerEntry = translator.getTranslatedClass(entryPool.getClass(innerName)); + super.visitInnerClass(translatedEntry.getName(), outerEntry.getName(), innerEntry.getName(), translatedEntry.getAccess().getFlags()); + } else { + super.visitInnerClass(name, outerName, innerName, access); + } + } +} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java new file mode 100644 index 0000000..e40becc --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java @@ -0,0 +1,82 @@ +package cuchaz.enigma.bytecode.translators; + +import cuchaz.enigma.mapping.*; +import org.objectweb.asm.*; + +import java.util.Locale; + +public class TranslationMethodVisitor extends MethodVisitor { + private final MethodDefEntry methodEntry; + private final Translator translator; + + public TranslationMethodVisitor(Translator translator, MethodDefEntry methodEntry, int api, MethodVisitor mv) { + super(api, mv); + this.translator = translator; + this.methodEntry = methodEntry; + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + FieldEntry entry = new FieldEntry(new ClassEntry(owner), name, new TypeDescriptor(desc)); + FieldEntry translatedEntry = translator.getTranslatedField(entry); + if (translatedEntry != null) { + super.visitFieldInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString()); + } else { + super.visitFieldInsn(opcode, owner, name, desc); + } + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + MethodEntry entry = new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc)); + MethodEntry translatedEntry = translator.getTranslatedMethod(entry); + if (translatedEntry != null) { + super.visitMethodInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString(), itf); + } else { + super.visitMethodInsn(opcode, owner, name, desc, itf); + } + } + + @Override + public void visitAttribute(Attribute attr) { + // TODO: Implement + super.visitAttribute(attr); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + // TODO: Implement + return super.visitAnnotation(desc, visible); + } + + @Override + public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { + LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, index, name, new TypeDescriptor(desc)); + LocalVariableDefEntry translatedEntry = translator.getTranslatedVariableDef(entry); + String translatedName = translatedEntry.getName(); + // TODO: Better name inference + if (translatedName.equals(entry.getName())) { + TypeDescriptor argDesc = translatedEntry.getDesc(); + int nameIndex = translatedEntry.getNamedIndex() + 1; + String prefix = translatedEntry.getNamedIndex() < methodEntry.getDesc().getArgumentDescs().size() ? "a" : "v"; + StringBuilder nameBuilder = new StringBuilder(prefix); + // Unfortunately each of these have different name getters, so they have different code paths + if (argDesc.isPrimitive()) { + TypeDescriptor.Primitive argCls = argDesc.getPrimitive(); + nameBuilder.append(argCls.name()); + } else if (argDesc.isArray()) { + // List types would require this whole block again, so just go with aListx + nameBuilder.append(nameIndex); + } else if (argDesc.isType()) { + String typeName = argDesc.getOwnerEntry().getSimpleName().replace("$", ""); + typeName = typeName.substring(0, 1).toUpperCase(Locale.ROOT) + typeName.substring(1); + nameBuilder.append(typeName); + } + if (methodEntry.getDesc().getArgumentDescs().size() > 1) { + nameBuilder.append(nameIndex); + } + translatedName = nameBuilder.toString(); + } + super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), signature, start, end, index); + } +} -- cgit v1.2.3