diff options
| author | 2018-05-19 17:02:46 +0200 | |
|---|---|---|
| committer | 2018-05-19 17:02:46 +0200 | |
| commit | 2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021 (patch) | |
| tree | 14c8b1e806449ace1641a1dbafae162855f79670 /src/main/java/cuchaz/enigma/bytecode | |
| parent | Fix build (diff) | |
| download | enigma-fork-2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021.tar.gz enigma-fork-2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021.tar.xz enigma-fork-2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021.zip | |
Initial port to ASM
Diffstat (limited to 'src/main/java/cuchaz/enigma/bytecode')
22 files changed, 324 insertions, 2342 deletions
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 @@ | |||
| 1 | package cuchaz.enigma.bytecode; | ||
| 2 | |||
| 3 | import org.objectweb.asm.Opcodes; | ||
| 4 | |||
| 5 | import java.lang.reflect.Modifier; | ||
| 6 | |||
| 7 | public class AccessFlags { | ||
| 8 | private static final int SYNTHETIC_FLAG = 0x00001000; | ||
| 9 | private static final int BRIDGED_FLAG = 0x00000040; | ||
| 10 | |||
| 11 | private int flags; | ||
| 12 | |||
| 13 | public AccessFlags(int flags) { | ||
| 14 | this.flags = flags; | ||
| 15 | } | ||
| 16 | |||
| 17 | public boolean isPrivate() { | ||
| 18 | return Modifier.isPrivate(this.flags); | ||
| 19 | } | ||
| 20 | |||
| 21 | public boolean isProtected() { | ||
| 22 | return Modifier.isProtected(this.flags); | ||
| 23 | } | ||
| 24 | |||
| 25 | public boolean isPublic() { | ||
| 26 | return Modifier.isPublic(this.flags); | ||
| 27 | } | ||
| 28 | |||
| 29 | public boolean isSynthetic() { | ||
| 30 | return (this.flags & SYNTHETIC_FLAG) != 0; | ||
| 31 | } | ||
| 32 | |||
| 33 | public boolean isStatic() { | ||
| 34 | return Modifier.isStatic(this.flags); | ||
| 35 | } | ||
| 36 | |||
| 37 | public AccessFlags setPrivate() { | ||
| 38 | this.setVisibility(Opcodes.ACC_PRIVATE); | ||
| 39 | return this; | ||
| 40 | } | ||
| 41 | |||
| 42 | public AccessFlags setProtected() { | ||
| 43 | this.setVisibility(Opcodes.ACC_PROTECTED); | ||
| 44 | return this; | ||
| 45 | } | ||
| 46 | |||
| 47 | public AccessFlags setPublic() { | ||
| 48 | this.setVisibility(Opcodes.ACC_PUBLIC); | ||
| 49 | return this; | ||
| 50 | } | ||
| 51 | |||
| 52 | public AccessFlags setBridged() { | ||
| 53 | this.setVisibility(BRIDGED_FLAG); | ||
| 54 | return this; | ||
| 55 | } | ||
| 56 | |||
| 57 | public void setVisibility(int visibility) { | ||
| 58 | this.resetVisibility(); | ||
| 59 | this.flags |= visibility; | ||
| 60 | } | ||
| 61 | |||
| 62 | private void resetVisibility() { | ||
| 63 | this.flags &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC); | ||
| 64 | } | ||
| 65 | |||
| 66 | public int getFlags() { | ||
| 67 | return this.flags; | ||
| 68 | } | ||
| 69 | |||
| 70 | @Override | ||
| 71 | public boolean equals(Object obj) { | ||
| 72 | return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags; | ||
| 73 | } | ||
| 74 | |||
| 75 | @Override | ||
| 76 | public int hashCode() { | ||
| 77 | return flags; | ||
| 78 | } | ||
| 79 | } | ||
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 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 13 | 13 | ||
| 14 | import javassist.CtBehavior; | 14 | import org.objectweb.asm.ClassVisitor; |
| 15 | import javassist.CtClass; | 15 | import org.objectweb.asm.FieldVisitor; |
| 16 | import javassist.CtField; | 16 | import org.objectweb.asm.MethodVisitor; |
| 17 | import javassist.bytecode.AccessFlag; | ||
| 18 | import javassist.bytecode.InnerClassesAttribute; | ||
| 19 | 17 | ||
| 20 | public class ClassProtectifier { | 18 | public class ClassProtectifier extends ClassVisitor { |
| 21 | 19 | ||
| 22 | public static CtClass protectify(CtClass c) { | 20 | public ClassProtectifier(int api, ClassVisitor cv) { |
| 23 | 21 | super(api, cv); | |
| 24 | // protectify all the fields | 22 | } |
| 25 | for (CtField field : c.getDeclaredFields()) { | ||
| 26 | field.setModifiers(protectify(field.getModifiers())); | ||
| 27 | } | ||
| 28 | 23 | ||
| 29 | // protectify all the methods and constructors | 24 | @Override |
| 30 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 25 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { |
| 31 | behavior.setModifiers(protectify(behavior.getModifiers())); | 26 | access = protectify(access); |
| 32 | } | 27 | return super.visitMethod(access, name, desc, signature, exceptions); |
| 28 | } | ||
| 33 | 29 | ||
| 34 | // protectify all the inner classes | 30 | @Override |
| 35 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | 31 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { |
| 36 | if (attr != null) { | 32 | access = protectify(access); |
| 37 | for (int i = 0; i < attr.tableLength(); i++) { | 33 | return super.visitField(access, name, desc, signature, value); |
| 38 | attr.setAccessFlags(i, protectify(attr.accessFlags(i))); | 34 | } |
| 39 | } | ||
| 40 | } | ||
| 41 | 35 | ||
| 42 | return c; | 36 | @Override |
| 37 | public void visitInnerClass(String name, String outerName, String innerName, int access) { | ||
| 38 | access = protectify(access); | ||
| 39 | super.visitInnerClass(name, outerName, innerName, access); | ||
| 43 | } | 40 | } |
| 44 | 41 | ||
| 45 | private static int protectify(int flags) { | 42 | private static int protectify(int access) { |
| 46 | if (AccessFlag.isPrivate(flags)) { | 43 | AccessFlags accessFlags = new AccessFlags(access); |
| 47 | flags = AccessFlag.setProtected(flags); | 44 | if (accessFlags.isPrivate()) { |
| 45 | accessFlags.setProtected(); | ||
| 48 | } | 46 | } |
| 49 | return flags; | 47 | return accessFlags.getFlags(); |
| 50 | } | 48 | } |
| 51 | } | 49 | } |
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 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.bytecode; | 12 | package cuchaz.enigma.bytecode; |
| 13 | 13 | ||
| 14 | import javassist.CtBehavior; | 14 | import org.objectweb.asm.ClassVisitor; |
| 15 | import javassist.CtClass; | 15 | import org.objectweb.asm.FieldVisitor; |
| 16 | import javassist.CtField; | 16 | import org.objectweb.asm.MethodVisitor; |
| 17 | import javassist.bytecode.AccessFlag; | ||
| 18 | import javassist.bytecode.InnerClassesAttribute; | ||
| 19 | 17 | ||
| 20 | public class ClassPublifier { | 18 | public class ClassPublifier extends ClassVisitor { |
| 21 | 19 | ||
| 22 | public static CtClass publify(CtClass c) { | 20 | public ClassPublifier(int api, ClassVisitor cv) { |
| 21 | super(api, cv); | ||
| 22 | } | ||
| 23 | 23 | ||
| 24 | // publify all the fields | 24 | @Override |
| 25 | for (CtField field : c.getDeclaredFields()) { | 25 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { |
| 26 | field.setModifiers(publify(field.getModifiers())); | 26 | access = publify(access); |
| 27 | } | 27 | super.visit(version, access, name, signature, superName, interfaces); |
| 28 | } | ||
| 28 | 29 | ||
| 29 | // publify all the methods and constructors | 30 | @Override |
| 30 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 31 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { |
| 31 | behavior.setModifiers(publify(behavior.getModifiers())); | 32 | access = publify(access); |
| 32 | } | 33 | return super.visitField(access, name, desc, signature, value); |
| 34 | } | ||
| 33 | 35 | ||
| 34 | // publify all the inner classes | 36 | @Override |
| 35 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | 37 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { |
| 36 | if (attr != null) { | 38 | access = publify(access); |
| 37 | for (int i = 0; i < attr.tableLength(); i++) { | 39 | return super.visitMethod(access, name, desc, signature, exceptions); |
| 38 | attr.setAccessFlags(i, publify(attr.accessFlags(i))); | 40 | } |
| 39 | } | ||
| 40 | } | ||
| 41 | 41 | ||
| 42 | return c; | 42 | @Override |
| 43 | public void visitInnerClass(String name, String outerName, String innerName, int access) { | ||
| 44 | access = publify(access); | ||
| 45 | super.visitInnerClass(name, outerName, innerName, access); | ||
| 43 | } | 46 | } |
| 44 | 47 | ||
| 45 | private static int publify(int flags) { | 48 | private static int publify(int access) { |
| 46 | if (!AccessFlag.isPublic(flags)) { | 49 | AccessFlags accessFlags = new AccessFlags(access); |
| 47 | flags = AccessFlag.setPublic(flags); | 50 | if (!accessFlags.isPublic()) { |
| 51 | accessFlags.setPublic(); | ||
| 48 | } | 52 | } |
| 49 | return flags; | 53 | return accessFlags.getFlags(); |
| 50 | } | 54 | } |
| 51 | } | 55 | } |
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 15 | import cuchaz.enigma.mapping.ClassNameReplacer; | ||
| 16 | import cuchaz.enigma.mapping.Mappings; | ||
| 17 | import cuchaz.enigma.mapping.Translator; | ||
| 18 | import javassist.CtBehavior; | ||
| 19 | import javassist.CtClass; | ||
| 20 | import javassist.CtField; | ||
| 21 | import javassist.Modifier; | ||
| 22 | import javassist.bytecode.*; | ||
| 23 | import javassist.bytecode.SignatureAttribute.*; | ||
| 24 | |||
| 25 | import java.lang.reflect.InvocationTargetException; | ||
| 26 | import java.lang.reflect.Method; | ||
| 27 | import java.util.Arrays; | ||
| 28 | import java.util.HashMap; | ||
| 29 | import java.util.List; | ||
| 30 | import java.util.Map; | ||
| 31 | |||
| 32 | public class ClassRenamer { | ||
| 33 | |||
| 34 | public static void applyModifier(Object obj, Mappings.EntryModifier modifier) { | ||
| 35 | int mod = -1; | ||
| 36 | if (obj instanceof CtField) | ||
| 37 | mod = ((CtField) obj).getModifiers(); | ||
| 38 | else if (obj instanceof CtBehavior) | ||
| 39 | mod = ((CtBehavior) obj).getModifiers(); | ||
| 40 | else if (obj instanceof CtClass) | ||
| 41 | mod = ((CtClass) obj).getModifiers(); | ||
| 42 | |||
| 43 | if (mod != -1) { | ||
| 44 | switch (modifier) { | ||
| 45 | case PRIVATE: | ||
| 46 | mod = Modifier.setPrivate(mod); | ||
| 47 | break; | ||
| 48 | case PROTECTED: | ||
| 49 | mod = Modifier.setProtected(mod); | ||
| 50 | break; | ||
| 51 | case PUBLIC: | ||
| 52 | mod = Modifier.setPublic(mod); | ||
| 53 | break; | ||
| 54 | default: | ||
| 55 | break; | ||
| 56 | } | ||
| 57 | if (obj instanceof CtField) | ||
| 58 | ((CtField) obj).setModifiers(mod); | ||
| 59 | else if (obj instanceof CtBehavior) | ||
| 60 | ((CtBehavior) obj).setModifiers(mod); | ||
| 61 | else | ||
| 62 | ((CtClass) obj).setModifiers(mod); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | public static void renameClasses(CtClass c, final Translator translator) { | ||
| 67 | renameClasses(c, className -> { | ||
| 68 | ClassEntry entry = translator.translateEntry(new ClassEntry(className)); | ||
| 69 | if (entry != null) { | ||
| 70 | return entry.getName(); | ||
| 71 | } | ||
| 72 | return null; | ||
| 73 | }); | ||
| 74 | } | ||
| 75 | |||
| 76 | public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) { | ||
| 77 | renameClasses(c, className -> { | ||
| 78 | ClassEntry entry = new ClassEntry(className); | ||
| 79 | if (entry.isInDefaultPackage()) { | ||
| 80 | return newPackageName + "/" + entry.getName(); | ||
| 81 | } | ||
| 82 | return null; | ||
| 83 | }); | ||
| 84 | } | ||
| 85 | |||
| 86 | public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) { | ||
| 87 | renameClasses(c, className -> { | ||
| 88 | ClassEntry entry = new ClassEntry(className); | ||
| 89 | if (entry.getPackageName().equals(oldPackageName)) { | ||
| 90 | return entry.getSimpleName(); | ||
| 91 | } | ||
| 92 | return null; | ||
| 93 | }); | ||
| 94 | } | ||
| 95 | |||
| 96 | @SuppressWarnings("unchecked") | ||
| 97 | public static void renameClasses(CtClass c, ClassNameReplacer replacer) { | ||
| 98 | |||
| 99 | // sadly, we can't use CtClass.renameClass() because SignatureAttribute.renameClass() is extremely buggy =( | ||
| 100 | |||
| 101 | ReplacerClassMap map = new ReplacerClassMap(replacer); | ||
| 102 | ClassFile classFile = c.getClassFile(); | ||
| 103 | |||
| 104 | // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo) | ||
| 105 | ConstPool constPool = c.getClassFile().getConstPool(); | ||
| 106 | constPool.renameClass(map); | ||
| 107 | |||
| 108 | // rename class attributes | ||
| 109 | renameAttributes(classFile.getAttributes(), map, SignatureType.Class); | ||
| 110 | |||
| 111 | // rename methods | ||
| 112 | for (MethodInfo methodInfo : (List<MethodInfo>) classFile.getMethods()) { | ||
| 113 | methodInfo.setDescriptor(Descriptor.rename(methodInfo.getDescriptor(), map)); | ||
| 114 | renameAttributes(methodInfo.getAttributes(), map, SignatureType.Method); | ||
| 115 | } | ||
| 116 | |||
| 117 | // rename fields | ||
| 118 | for (FieldInfo fieldInfo : (List<FieldInfo>) classFile.getFields()) { | ||
| 119 | fieldInfo.setDescriptor(Descriptor.rename(fieldInfo.getDescriptor(), map)); | ||
| 120 | renameAttributes(fieldInfo.getAttributes(), map, SignatureType.Field); | ||
| 121 | } | ||
| 122 | |||
| 123 | // rename the class name itself last | ||
| 124 | // NOTE: don't use the map here, because setName() calls the buggy SignatureAttribute.renameClass() | ||
| 125 | // we only want to replace exactly this class name | ||
| 126 | String newName = renameClassName(c.getName(), map); | ||
| 127 | if (newName != null) { | ||
| 128 | c.setName(newName); | ||
| 129 | } | ||
| 130 | |||
| 131 | // replace simple names in the InnerClasses attribute too | ||
| 132 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 133 | if (attr != null) { | ||
| 134 | for (int i = 0; i < attr.tableLength(); i++) { | ||
| 135 | |||
| 136 | String innerName = attr.innerClass(i); | ||
| 137 | // get the inner class full name (which has already been translated) | ||
| 138 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); | ||
| 139 | |||
| 140 | if (attr.innerNameIndex(i) != 0) { | ||
| 141 | // update the inner name | ||
| 142 | attr.setInnerNameIndex(i, constPool.addUtf8Info(classEntry.getInnermostClassName())); | ||
| 143 | } | ||
| 144 | |||
| 145 | /* DEBUG | ||
| 146 | System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); | ||
| 147 | */ | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | @SuppressWarnings("unchecked") | ||
| 153 | private static void renameAttributes(List<AttributeInfo> attributes, ReplacerClassMap map, SignatureType type) { | ||
| 154 | try { | ||
| 155 | |||
| 156 | // make the rename class method accessible | ||
| 157 | Method renameClassMethod = AttributeInfo.class.getDeclaredMethod("renameClass", Map.class); | ||
| 158 | renameClassMethod.setAccessible(true); | ||
| 159 | |||
| 160 | for (AttributeInfo attribute : attributes) { | ||
| 161 | if (attribute instanceof SignatureAttribute) { | ||
| 162 | // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell | ||
| 163 | SignatureAttribute signatureAttribute = (SignatureAttribute) attribute; | ||
| 164 | String newSignature = type.rename(signatureAttribute.getSignature(), map); | ||
| 165 | if (newSignature != null) { | ||
| 166 | signatureAttribute.setSignature(newSignature); | ||
| 167 | } | ||
| 168 | } else if (attribute instanceof CodeAttribute) { | ||
| 169 | // code attributes have signature attributes too (indirectly) | ||
| 170 | CodeAttribute codeAttribute = (CodeAttribute) attribute; | ||
| 171 | renameAttributes(codeAttribute.getAttributes(), map, type); | ||
| 172 | } else if (attribute instanceof LocalVariableTypeAttribute) { | ||
| 173 | // lvt attributes have signature attributes too | ||
| 174 | LocalVariableTypeAttribute localVariableAttribute = (LocalVariableTypeAttribute) attribute; | ||
| 175 | renameLocalVariableTypeAttribute(localVariableAttribute, map); | ||
| 176 | } else { | ||
| 177 | renameClassMethod.invoke(attribute, map); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { | ||
| 182 | throw new Error("Unable to call javassist methods by reflection!", ex); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { | ||
| 187 | |||
| 188 | // adapted from LocalVariableAttribute.renameClass() | ||
| 189 | ConstPool cp = attribute.getConstPool(); | ||
| 190 | int n = attribute.tableLength(); | ||
| 191 | byte[] info = attribute.get(); | ||
| 192 | for (int i = 0; i < n; ++i) { | ||
| 193 | int pos = i * 10 + 2; | ||
| 194 | int index = ByteArray.readU16bit(info, pos + 6); | ||
| 195 | if (index != 0) { | ||
| 196 | String signature = cp.getUtf8Info(index); | ||
| 197 | String newSignature = renameLocalVariableSignature(signature, map); | ||
| 198 | if (newSignature != null) { | ||
| 199 | ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) { | ||
| 206 | |||
| 207 | // for some reason, signatures with . in them don't count as field signatures | ||
| 208 | // looks like anonymous classes delimit with . in stead of $ | ||
| 209 | // convert the . to $, but keep track of how many we replace | ||
| 210 | // we need to put them back after we translate | ||
| 211 | int start = signature.lastIndexOf('$') + 1; | ||
| 212 | int numConverted = 0; | ||
| 213 | StringBuilder buf = new StringBuilder(signature); | ||
| 214 | for (int i = buf.length() - 1; i >= start; i--) { | ||
| 215 | char c = buf.charAt(i); | ||
| 216 | if (c == '.') { | ||
| 217 | buf.setCharAt(i, '$'); | ||
| 218 | numConverted++; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | signature = buf.toString(); | ||
| 222 | |||
| 223 | // translate | ||
| 224 | String newSignature = renameFieldSignature(signature, map); | ||
| 225 | if (newSignature != null) { | ||
| 226 | |||
| 227 | // put the delimiters back | ||
| 228 | buf = new StringBuilder(newSignature); | ||
| 229 | for (int i = buf.length() - 1; i >= 0 && numConverted > 0; i--) { | ||
| 230 | char c = buf.charAt(i); | ||
| 231 | if (c == '$') { | ||
| 232 | buf.setCharAt(i, '.'); | ||
| 233 | numConverted--; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | assert (numConverted == 0); | ||
| 237 | newSignature = buf.toString(); | ||
| 238 | |||
| 239 | return newSignature; | ||
| 240 | } | ||
| 241 | |||
| 242 | return null; | ||
| 243 | } | ||
| 244 | |||
| 245 | private static String renameClassSignature(String signature, ReplacerClassMap map) { | ||
| 246 | try { | ||
| 247 | ClassSignature type = renameType(SignatureAttribute.toClassSignature(signature), map); | ||
| 248 | return type.encode(); | ||
| 249 | } catch (BadBytecode ex) { | ||
| 250 | throw new Error("Can't parse field signature: " + signature); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | private static String renameFieldSignature(String signature, ReplacerClassMap map) { | ||
| 255 | try { | ||
| 256 | ObjectType type = renameType(SignatureAttribute.toFieldSignature(signature), map); | ||
| 257 | if (type != null) { | ||
| 258 | return type.encode(); | ||
| 259 | } | ||
| 260 | return null; | ||
| 261 | } catch (BadBytecode ex) { | ||
| 262 | throw new Error("Can't parse class signature: " + signature); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | private static String renameMethodSignature(String signature, ReplacerClassMap map) { | ||
| 267 | try { | ||
| 268 | MethodSignature type = renameType(SignatureAttribute.toMethodSignature(signature), map); | ||
| 269 | return type.encode(); | ||
| 270 | } catch (BadBytecode ex) { | ||
| 271 | throw new Error("Can't parse method signature: " + signature); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | private static TypeParameter[] renameTypeParameter(TypeParameter[] typeParamTypes, ReplacerClassMap map) { | ||
| 276 | if (typeParamTypes != null) { | ||
| 277 | typeParamTypes = Arrays.copyOf(typeParamTypes, typeParamTypes.length); | ||
| 278 | for (int i = 0; i < typeParamTypes.length; i++) { | ||
| 279 | TypeParameter newParamType = renameType(typeParamTypes[i], map); | ||
| 280 | if (newParamType != null) { | ||
| 281 | typeParamTypes[i] = newParamType; | ||
| 282 | } | ||
| 283 | } | ||
| 284 | } | ||
| 285 | return typeParamTypes; | ||
| 286 | } | ||
| 287 | |||
| 288 | private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) { | ||
| 289 | |||
| 290 | TypeParameter[] typeParamTypes = renameTypeParameter(type.getParameters(), map); | ||
| 291 | |||
| 292 | ClassType superclassType = type.getSuperClass(); | ||
| 293 | if (superclassType != ClassType.OBJECT) { | ||
| 294 | ClassType newSuperclassType = renameType(superclassType, map); | ||
| 295 | if (newSuperclassType != null) { | ||
| 296 | superclassType = newSuperclassType; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | ClassType[] interfaceTypes = type.getInterfaces(); | ||
| 301 | if (interfaceTypes != null) { | ||
| 302 | interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); | ||
| 303 | for (int i = 0; i < interfaceTypes.length; i++) { | ||
| 304 | ClassType newInterfaceType = renameType(interfaceTypes[i], map); | ||
| 305 | if (newInterfaceType != null) { | ||
| 306 | interfaceTypes[i] = newInterfaceType; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | return new ClassSignature(typeParamTypes, superclassType, interfaceTypes); | ||
| 312 | } | ||
| 313 | |||
| 314 | private static MethodSignature renameType(MethodSignature type, ReplacerClassMap map) { | ||
| 315 | |||
| 316 | TypeParameter[] typeParamTypes = renameTypeParameter(type.getTypeParameters(), map); | ||
| 317 | |||
| 318 | Type[] paramTypes = type.getParameterTypes(); | ||
| 319 | if (paramTypes != null) { | ||
| 320 | paramTypes = Arrays.copyOf(paramTypes, paramTypes.length); | ||
| 321 | for (int i = 0; i < paramTypes.length; i++) { | ||
| 322 | Type newParamType = renameType(paramTypes[i], map); | ||
| 323 | if (newParamType != null) { | ||
| 324 | paramTypes[i] = newParamType; | ||
| 325 | } | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | Type returnType = type.getReturnType(); | ||
| 330 | if (returnType != null) { | ||
| 331 | Type newReturnType = renameType(returnType, map); | ||
| 332 | if (newReturnType != null) { | ||
| 333 | returnType = newReturnType; | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | ObjectType[] exceptionTypes = type.getExceptionTypes(); | ||
| 338 | if (exceptionTypes != null) { | ||
| 339 | exceptionTypes = Arrays.copyOf(exceptionTypes, exceptionTypes.length); | ||
| 340 | for (int i = 0; i < exceptionTypes.length; i++) { | ||
| 341 | ObjectType newExceptionType = renameType(exceptionTypes[i], map); | ||
| 342 | if (newExceptionType != null) { | ||
| 343 | exceptionTypes[i] = newExceptionType; | ||
| 344 | } | ||
| 345 | } | ||
| 346 | } | ||
| 347 | |||
| 348 | return new MethodSignature(typeParamTypes, paramTypes, returnType, exceptionTypes); | ||
| 349 | } | ||
| 350 | |||
| 351 | private static Type renameType(Type type, ReplacerClassMap map) { | ||
| 352 | if (type instanceof ObjectType) { | ||
| 353 | return renameType((ObjectType) type, map); | ||
| 354 | } else if (type instanceof BaseType) { | ||
| 355 | return renameType((BaseType) type, map); | ||
| 356 | } else { | ||
| 357 | throw new Error("Don't know how to rename type " + type.getClass()); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | private static ObjectType renameType(ObjectType type, ReplacerClassMap map) { | ||
| 362 | if (type instanceof ArrayType) { | ||
| 363 | return renameType((ArrayType) type, map); | ||
| 364 | } else if (type instanceof ClassType) { | ||
| 365 | return renameType((ClassType) type, map); | ||
| 366 | } else if (type instanceof TypeVariable) { | ||
| 367 | return renameType((TypeVariable) type, map); | ||
| 368 | } else { | ||
| 369 | throw new Error("Don't know how to rename type " + type.getClass()); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | private static BaseType renameType(BaseType type, ReplacerClassMap map) { | ||
| 374 | // don't have to rename primitives | ||
| 375 | return null; | ||
| 376 | } | ||
| 377 | |||
| 378 | private static TypeVariable renameType(TypeVariable type, ReplacerClassMap map) { | ||
| 379 | // don't have to rename template args | ||
| 380 | return null; | ||
| 381 | } | ||
| 382 | |||
| 383 | private static ClassType renameType(ClassType type, ReplacerClassMap map) { | ||
| 384 | |||
| 385 | // translate type args | ||
| 386 | TypeArgument[] args = type.getTypeArguments(); | ||
| 387 | if (args != null) { | ||
| 388 | args = Arrays.copyOf(args, args.length); | ||
| 389 | for (int i = 0; i < args.length; i++) { | ||
| 390 | TypeArgument newType = renameType(args[i], map); | ||
| 391 | if (newType != null) { | ||
| 392 | args[i] = newType; | ||
| 393 | } | ||
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 397 | if (type instanceof NestedClassType) { | ||
| 398 | NestedClassType nestedType = (NestedClassType) type; | ||
| 399 | |||
| 400 | // translate the name | ||
| 401 | String name = getClassName(type); | ||
| 402 | String newName = map.get(name); | ||
| 403 | if (newName != null) { | ||
| 404 | name = new ClassEntry(newName).getInnermostClassName(); | ||
| 405 | } | ||
| 406 | |||
| 407 | // translate the parent class too | ||
| 408 | ClassType parent = renameType(nestedType.getDeclaringClass(), map); | ||
| 409 | if (parent == null) { | ||
| 410 | parent = nestedType.getDeclaringClass(); | ||
| 411 | } | ||
| 412 | |||
| 413 | return new NestedClassType(parent, name, args); | ||
| 414 | } else { | ||
| 415 | |||
| 416 | // translate the name | ||
| 417 | String name = type.getName(); | ||
| 418 | String newName = renameClassName(name, map); | ||
| 419 | if (newName != null) { | ||
| 420 | name = newName; | ||
| 421 | } | ||
| 422 | |||
| 423 | return new ClassType(name, args); | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | private static String getClassName(ClassType type) { | ||
| 428 | if (type instanceof NestedClassType) { | ||
| 429 | NestedClassType nestedType = (NestedClassType) type; | ||
| 430 | return getClassName(nestedType.getDeclaringClass()) + "$" + Descriptor.toJvmName(type.getName().replace('.', '$')); | ||
| 431 | } else { | ||
| 432 | return Descriptor.toJvmName(type.getName()); | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | private static String renameClassName(String name, ReplacerClassMap map) { | ||
| 437 | String newName = map.get(Descriptor.toJvmName(name)); | ||
| 438 | if (newName != null) { | ||
| 439 | return Descriptor.toJavaName(newName); | ||
| 440 | } | ||
| 441 | return null; | ||
| 442 | } | ||
| 443 | |||
| 444 | private static TypeArgument renameType(TypeArgument type, ReplacerClassMap map) { | ||
| 445 | ObjectType subType = type.getType(); | ||
| 446 | if (subType != null) { | ||
| 447 | ObjectType newSubType = renameType(subType, map); | ||
| 448 | if (newSubType != null) { | ||
| 449 | switch (type.getKind()) { | ||
| 450 | case ' ': | ||
| 451 | return new TypeArgument(newSubType); | ||
| 452 | case '+': | ||
| 453 | return TypeArgument.subclassOf(newSubType); | ||
| 454 | case '-': | ||
| 455 | return TypeArgument.superOf(newSubType); | ||
| 456 | default: | ||
| 457 | throw new Error("Unknown type kind: " + type.getKind()); | ||
| 458 | } | ||
| 459 | } | ||
| 460 | } | ||
| 461 | return null; | ||
| 462 | } | ||
| 463 | |||
| 464 | private static ArrayType renameType(ArrayType type, ReplacerClassMap map) { | ||
| 465 | Type newSubType = renameType(type.getComponentType(), map); | ||
| 466 | if (newSubType != null) { | ||
| 467 | return new ArrayType(type.getDimension(), newSubType); | ||
| 468 | } | ||
| 469 | return null; | ||
| 470 | } | ||
| 471 | |||
| 472 | private static TypeParameter renameType(TypeParameter type, ReplacerClassMap map) { | ||
| 473 | |||
| 474 | ObjectType superclassType = type.getClassBound(); | ||
| 475 | if (superclassType != null) { | ||
| 476 | ObjectType newSuperclassType = renameType(superclassType, map); | ||
| 477 | if (newSuperclassType != null) { | ||
| 478 | superclassType = newSuperclassType; | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 | ObjectType[] interfaceTypes = type.getInterfaceBound(); | ||
| 483 | if (interfaceTypes != null) { | ||
| 484 | interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); | ||
| 485 | for (int i = 0; i < interfaceTypes.length; i++) { | ||
| 486 | ObjectType newInterfaceType = renameType(interfaceTypes[i], map); | ||
| 487 | if (newInterfaceType != null) { | ||
| 488 | interfaceTypes[i] = newInterfaceType; | ||
| 489 | } | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | return new TypeParameter(type.getName(), superclassType, interfaceTypes); | ||
| 494 | } | ||
| 495 | |||
| 496 | private enum SignatureType { | ||
| 497 | Class { | ||
| 498 | @Override | ||
| 499 | public String rename(String signature, ReplacerClassMap map) { | ||
| 500 | return renameClassSignature(signature, map); | ||
| 501 | } | ||
| 502 | }, | ||
| 503 | Field { | ||
| 504 | @Override | ||
| 505 | public String rename(String signature, ReplacerClassMap map) { | ||
| 506 | return renameFieldSignature(signature, map); | ||
| 507 | } | ||
| 508 | }, | ||
| 509 | Method { | ||
| 510 | @Override | ||
| 511 | public String rename(String signature, ReplacerClassMap map) { | ||
| 512 | return renameMethodSignature(signature, map); | ||
| 513 | } | ||
| 514 | }; | ||
| 515 | |||
| 516 | public abstract String rename(String signature, ReplacerClassMap map); | ||
| 517 | } | ||
| 518 | |||
| 519 | private static class ReplacerClassMap extends HashMap<String, String> { | ||
| 520 | |||
| 521 | private ClassNameReplacer replacer; | ||
| 522 | |||
| 523 | public ReplacerClassMap(ClassNameReplacer replacer) { | ||
| 524 | this.replacer = replacer; | ||
| 525 | } | ||
| 526 | |||
| 527 | @Override | ||
| 528 | public String get(Object obj) { | ||
| 529 | if (obj instanceof String) { | ||
| 530 | return get((String) obj); | ||
| 531 | } | ||
| 532 | return null; | ||
| 533 | } | ||
| 534 | |||
| 535 | public String get(String className) { | ||
| 536 | return replacer.replace(className); | ||
| 537 | } | ||
| 538 | } | ||
| 539 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode; | ||
| 13 | |||
| 14 | import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; | ||
| 15 | import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; | ||
| 16 | import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; | ||
| 17 | import javassist.bytecode.ConstPool; | ||
| 18 | import javassist.bytecode.Descriptor; | ||
| 19 | |||
| 20 | import java.io.DataInputStream; | ||
| 21 | import java.io.DataOutputStream; | ||
| 22 | import java.lang.reflect.Constructor; | ||
| 23 | import java.lang.reflect.Field; | ||
| 24 | import java.lang.reflect.Method; | ||
| 25 | import java.util.HashMap; | ||
| 26 | |||
| 27 | public class ConstPoolEditor { | ||
| 28 | |||
| 29 | private static Method getItem; | ||
| 30 | private static Method addItem; | ||
| 31 | private static Method addItem0; | ||
| 32 | private static Field items; | ||
| 33 | private static Field cache; | ||
| 34 | private static Field numItems; | ||
| 35 | private static Field objects; | ||
| 36 | private static Field elements; | ||
| 37 | private static Method methodWritePool; | ||
| 38 | private static Constructor<ConstPool> constructorPool; | ||
| 39 | |||
| 40 | static { | ||
| 41 | try { | ||
| 42 | getItem = ConstPool.class.getDeclaredMethod("getItem", int.class); | ||
| 43 | getItem.setAccessible(true); | ||
| 44 | |||
| 45 | addItem = ConstPool.class.getDeclaredMethod("addItem", Class.forName("javassist.bytecode.ConstInfo")); | ||
| 46 | addItem.setAccessible(true); | ||
| 47 | |||
| 48 | addItem0 = ConstPool.class.getDeclaredMethod("addItem0", Class.forName("javassist.bytecode.ConstInfo")); | ||
| 49 | addItem0.setAccessible(true); | ||
| 50 | |||
| 51 | items = ConstPool.class.getDeclaredField("items"); | ||
| 52 | items.setAccessible(true); | ||
| 53 | |||
| 54 | cache = ConstPool.class.getDeclaredField("itemsCache"); | ||
| 55 | cache.setAccessible(true); | ||
| 56 | |||
| 57 | numItems = ConstPool.class.getDeclaredField("numOfItems"); | ||
| 58 | numItems.setAccessible(true); | ||
| 59 | |||
| 60 | objects = Class.forName("javassist.bytecode.LongVector").getDeclaredField("objects"); | ||
| 61 | objects.setAccessible(true); | ||
| 62 | |||
| 63 | elements = Class.forName("javassist.bytecode.LongVector").getDeclaredField("elements"); | ||
| 64 | elements.setAccessible(true); | ||
| 65 | |||
| 66 | methodWritePool = ConstPool.class.getDeclaredMethod("write", DataOutputStream.class); | ||
| 67 | methodWritePool.setAccessible(true); | ||
| 68 | |||
| 69 | constructorPool = ConstPool.class.getDeclaredConstructor(DataInputStream.class); | ||
| 70 | constructorPool.setAccessible(true); | ||
| 71 | } catch (Exception ex) { | ||
| 72 | throw new Error(ex); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | private ConstPool pool; | ||
| 77 | |||
| 78 | public ConstPoolEditor(ConstPool pool) { | ||
| 79 | this.pool = pool; | ||
| 80 | } | ||
| 81 | |||
| 82 | public static ConstPool readPool(DataInputStream in) { | ||
| 83 | try { | ||
| 84 | return constructorPool.newInstance(in); | ||
| 85 | } catch (Exception ex) { | ||
| 86 | throw new Error(ex); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | public static ConstPool newConstPool() { | ||
| 91 | // const pool expects the name of a class to initialize itself | ||
| 92 | // but we want an empty pool | ||
| 93 | // so give it a bogus name, and then clear the entries afterwards | ||
| 94 | ConstPool pool = new ConstPool("a"); | ||
| 95 | |||
| 96 | ConstPoolEditor editor = new ConstPoolEditor(pool); | ||
| 97 | int size = pool.getSize(); | ||
| 98 | for (int i = 0; i < size - 1; i++) { | ||
| 99 | editor.removeLastItem(); | ||
| 100 | } | ||
| 101 | |||
| 102 | // make sure the pool is actually empty | ||
| 103 | // although, in this case "empty" means one thing in it | ||
| 104 | // the JVM spec says index 0 should be reserved | ||
| 105 | assert (pool.getSize() == 1); | ||
| 106 | assert (editor.getItem(0) == null); | ||
| 107 | assert (editor.getItem(1) == null); | ||
| 108 | assert (editor.getItem(2) == null); | ||
| 109 | assert (editor.getItem(3) == null); | ||
| 110 | |||
| 111 | // also, clear the cache | ||
| 112 | editor.getCache().clear(); | ||
| 113 | |||
| 114 | return pool; | ||
| 115 | } | ||
| 116 | |||
| 117 | public void writePool(DataOutputStream out) { | ||
| 118 | try { | ||
| 119 | methodWritePool.invoke(this.pool, out); | ||
| 120 | } catch (Exception ex) { | ||
| 121 | throw new Error(ex); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | public String getMemberrefClassname(int memberrefIndex) { | ||
| 126 | return Descriptor.toJvmName(this.pool.getClassInfo(this.pool.getMemberClass(memberrefIndex))); | ||
| 127 | } | ||
| 128 | |||
| 129 | public String getMemberrefName(int memberrefIndex) { | ||
| 130 | return this.pool.getUtf8Info(this.pool.getNameAndTypeName(this.pool.getMemberNameAndType(memberrefIndex))); | ||
| 131 | } | ||
| 132 | |||
| 133 | public String getMemberrefType(int memberrefIndex) { | ||
| 134 | return this.pool.getUtf8Info(this.pool.getNameAndTypeDescriptor(this.pool.getMemberNameAndType(memberrefIndex))); | ||
| 135 | } | ||
| 136 | |||
| 137 | public ConstInfoAccessor getItem(int index) { | ||
| 138 | try { | ||
| 139 | Object entry = getItem.invoke(this.pool, index); | ||
| 140 | if (entry == null) { | ||
| 141 | return null; | ||
| 142 | } | ||
| 143 | return new ConstInfoAccessor(entry); | ||
| 144 | } catch (Exception ex) { | ||
| 145 | throw new Error(ex); | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | public int addItem(Object item) { | ||
| 150 | try { | ||
| 151 | return (Integer) addItem.invoke(this.pool, item); | ||
| 152 | } catch (Exception ex) { | ||
| 153 | throw new Error(ex); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | public int addItemForceNew(Object item) { | ||
| 158 | try { | ||
| 159 | return (Integer) addItem0.invoke(this.pool, item); | ||
| 160 | } catch (Exception ex) { | ||
| 161 | throw new Error(ex); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | @SuppressWarnings("rawtypes") | ||
| 166 | public void removeLastItem() { | ||
| 167 | try { | ||
| 168 | // remove the item from the cache | ||
| 169 | HashMap cache = getCache(); | ||
| 170 | if (cache != null) { | ||
| 171 | Object item = getItem(this.pool.getSize() - 1); | ||
| 172 | cache.remove(item); | ||
| 173 | } | ||
| 174 | |||
| 175 | // remove the actual item | ||
| 176 | // based off of LongVector.addElement() | ||
| 177 | Object item = items.get(this.pool); | ||
| 178 | Object[][] object = (Object[][]) objects.get(items); | ||
| 179 | int numElements = (Integer) elements.get(items) - 1; | ||
| 180 | int nth = numElements >> 7; | ||
| 181 | int offset = numElements & (128 - 1); | ||
| 182 | object[nth][offset] = null; | ||
| 183 | |||
| 184 | // decrement the number of items | ||
| 185 | elements.set(item, numElements); | ||
| 186 | numItems.set(this.pool, (Integer) numItems.get(this.pool) - 1); | ||
| 187 | } catch (Exception ex) { | ||
| 188 | throw new Error(ex); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | @SuppressWarnings("rawtypes") | ||
| 193 | public HashMap getCache() { | ||
| 194 | try { | ||
| 195 | return (HashMap) cache.get(this.pool); | ||
| 196 | } catch (Exception ex) { | ||
| 197 | throw new Error(ex); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | @SuppressWarnings({ "rawtypes", "unchecked" }) | ||
| 202 | public void changeMemberrefNameAndType(int memberrefIndex, String newName, String newType) { | ||
| 203 | // NOTE: when changing values, we always need to copy-on-write | ||
| 204 | try { | ||
| 205 | // get the memberref item | ||
| 206 | Object item = getItem(memberrefIndex).getItem(); | ||
| 207 | |||
| 208 | // update the cache | ||
| 209 | HashMap cache = getCache(); | ||
| 210 | if (cache != null) { | ||
| 211 | cache.remove(item); | ||
| 212 | } | ||
| 213 | |||
| 214 | new MemberRefInfoAccessor(item).setNameAndTypeIndex(this.pool.addNameAndTypeInfo(newName, newType)); | ||
| 215 | |||
| 216 | // update the cache | ||
| 217 | if (cache != null) { | ||
| 218 | cache.put(item, item); | ||
| 219 | } | ||
| 220 | } catch (Exception ex) { | ||
| 221 | throw new Error(ex); | ||
| 222 | } | ||
| 223 | |||
| 224 | // make sure the change worked | ||
| 225 | assert (newName.equals(getMemberrefName(memberrefIndex))); | ||
| 226 | assert (newType.equals(getMemberrefType(memberrefIndex))); | ||
| 227 | } | ||
| 228 | |||
| 229 | @SuppressWarnings({ "rawtypes", "unchecked" }) | ||
| 230 | public void changeClassName(int classNameIndex, String newName) { | ||
| 231 | // NOTE: when changing values, we always need to copy-on-write | ||
| 232 | try { | ||
| 233 | // get the class item | ||
| 234 | Object item = getItem(classNameIndex).getItem(); | ||
| 235 | |||
| 236 | // update the cache | ||
| 237 | HashMap cache = getCache(); | ||
| 238 | if (cache != null) { | ||
| 239 | cache.remove(item); | ||
| 240 | } | ||
| 241 | |||
| 242 | // add the new name and repoint the name-and-type to it | ||
| 243 | new ClassInfoAccessor(item).setNameIndex(this.pool.addUtf8Info(newName)); | ||
| 244 | |||
| 245 | // update the cache | ||
| 246 | if (cache != null) { | ||
| 247 | cache.put(item, item); | ||
| 248 | } | ||
| 249 | } catch (Exception ex) { | ||
| 250 | throw new Error(ex); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | public String dump() { | ||
| 255 | StringBuilder buf = new StringBuilder(); | ||
| 256 | for (int i = 1; i < this.pool.getSize(); i++) { | ||
| 257 | buf.append(String.format("%4d", i)); | ||
| 258 | buf.append(" "); | ||
| 259 | buf.append(getItem(i)); | ||
| 260 | buf.append("\n"); | ||
| 261 | } | ||
| 262 | return buf.toString(); | ||
| 263 | } | ||
| 264 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode; | ||
| 13 | |||
| 14 | import com.google.common.collect.Maps; | ||
| 15 | import cuchaz.enigma.bytecode.accessors.*; | ||
| 16 | |||
| 17 | import java.util.Collection; | ||
| 18 | import java.util.Map; | ||
| 19 | |||
| 20 | public enum InfoType { | ||
| 21 | |||
| 22 | Utf8Info(1), | ||
| 23 | IntegerInfo(3), | ||
| 24 | FloatInfo(4), | ||
| 25 | LongInfo(5), | ||
| 26 | DoubleInfo(6), | ||
| 27 | ClassInfo(7) { | ||
| 28 | @Override | ||
| 29 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 30 | ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); | ||
| 31 | gatherIndexTree(indices, editor, accessor.getNameIndex()); | ||
| 32 | } | ||
| 33 | |||
| 34 | @Override | ||
| 35 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 36 | ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); | ||
| 37 | accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); | ||
| 38 | } | ||
| 39 | |||
| 40 | @Override | ||
| 41 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 42 | ClassInfoAccessor accessor = new ClassInfoAccessor(entry.getItem()); | ||
| 43 | ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); | ||
| 44 | return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag(); | ||
| 45 | } | ||
| 46 | }, | ||
| 47 | StringInfo(8) { | ||
| 48 | @Override | ||
| 49 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 50 | StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); | ||
| 51 | gatherIndexTree(indices, editor, accessor.getStringIndex()); | ||
| 52 | } | ||
| 53 | |||
| 54 | @Override | ||
| 55 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 56 | StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); | ||
| 57 | accessor.setStringIndex(remapIndex(map, accessor.getStringIndex())); | ||
| 58 | } | ||
| 59 | |||
| 60 | @Override | ||
| 61 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 62 | StringInfoAccessor accessor = new StringInfoAccessor(entry.getItem()); | ||
| 63 | ConstInfoAccessor stringEntry = pool.getItem(accessor.getStringIndex()); | ||
| 64 | return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag(); | ||
| 65 | } | ||
| 66 | }, | ||
| 67 | FieldRefInfo(9) { | ||
| 68 | @Override | ||
| 69 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 70 | MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); | ||
| 71 | gatherIndexTree(indices, editor, accessor.getClassIndex()); | ||
| 72 | gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); | ||
| 73 | } | ||
| 74 | |||
| 75 | @Override | ||
| 76 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 77 | MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); | ||
| 78 | accessor.setClassIndex(remapIndex(map, accessor.getClassIndex())); | ||
| 79 | accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); | ||
| 80 | } | ||
| 81 | |||
| 82 | @Override | ||
| 83 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 84 | MemberRefInfoAccessor accessor = new MemberRefInfoAccessor(entry.getItem()); | ||
| 85 | ConstInfoAccessor classEntry = pool.getItem(accessor.getClassIndex()); | ||
| 86 | ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); | ||
| 87 | return classEntry != null && classEntry.getTag() == ClassInfo.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); | ||
| 88 | } | ||
| 89 | }, | ||
| 90 | // same as FieldRefInfo | ||
| 91 | MethodRefInfo(10) { | ||
| 92 | @Override | ||
| 93 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 94 | FieldRefInfo.gatherIndexTree(indices, editor, entry); | ||
| 95 | } | ||
| 96 | |||
| 97 | @Override | ||
| 98 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 99 | FieldRefInfo.remapIndices(map, entry); | ||
| 100 | } | ||
| 101 | |||
| 102 | @Override | ||
| 103 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 104 | return FieldRefInfo.subIndicesAreValid(entry, pool); | ||
| 105 | } | ||
| 106 | }, | ||
| 107 | // same as FieldRefInfo | ||
| 108 | InterfaceMethodRefInfo(11) { | ||
| 109 | @Override | ||
| 110 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 111 | FieldRefInfo.gatherIndexTree(indices, editor, entry); | ||
| 112 | } | ||
| 113 | |||
| 114 | @Override | ||
| 115 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 116 | FieldRefInfo.remapIndices(map, entry); | ||
| 117 | } | ||
| 118 | |||
| 119 | @Override | ||
| 120 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 121 | return FieldRefInfo.subIndicesAreValid(entry, pool); | ||
| 122 | } | ||
| 123 | }, | ||
| 124 | NameAndTypeInfo(12) { | ||
| 125 | @Override | ||
| 126 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 127 | NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); | ||
| 128 | gatherIndexTree(indices, editor, accessor.getNameIndex()); | ||
| 129 | gatherIndexTree(indices, editor, accessor.getTypeIndex()); | ||
| 130 | } | ||
| 131 | |||
| 132 | @Override | ||
| 133 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 134 | NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); | ||
| 135 | accessor.setNameIndex(remapIndex(map, accessor.getNameIndex())); | ||
| 136 | accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); | ||
| 137 | } | ||
| 138 | |||
| 139 | @Override | ||
| 140 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 141 | NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor(entry.getItem()); | ||
| 142 | ConstInfoAccessor nameEntry = pool.getItem(accessor.getNameIndex()); | ||
| 143 | ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); | ||
| 144 | return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag() && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); | ||
| 145 | } | ||
| 146 | }, | ||
| 147 | MethodHandleInfo(15) { | ||
| 148 | @Override | ||
| 149 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 150 | MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); | ||
| 151 | gatherIndexTree(indices, editor, accessor.getTypeIndex()); | ||
| 152 | gatherIndexTree(indices, editor, accessor.getMethodRefIndex()); | ||
| 153 | } | ||
| 154 | |||
| 155 | @Override | ||
| 156 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 157 | MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); | ||
| 158 | accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); | ||
| 159 | accessor.setMethodRefIndex(remapIndex(map, accessor.getMethodRefIndex())); | ||
| 160 | } | ||
| 161 | |||
| 162 | @Override | ||
| 163 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 164 | MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor(entry.getItem()); | ||
| 165 | ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); | ||
| 166 | ConstInfoAccessor methodRefEntry = pool.getItem(accessor.getMethodRefIndex()); | ||
| 167 | return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag() && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag(); | ||
| 168 | } | ||
| 169 | }, | ||
| 170 | MethodTypeInfo(16) { | ||
| 171 | @Override | ||
| 172 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 173 | MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); | ||
| 174 | gatherIndexTree(indices, editor, accessor.getTypeIndex()); | ||
| 175 | } | ||
| 176 | |||
| 177 | @Override | ||
| 178 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 179 | MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); | ||
| 180 | accessor.setTypeIndex(remapIndex(map, accessor.getTypeIndex())); | ||
| 181 | } | ||
| 182 | |||
| 183 | @Override | ||
| 184 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 185 | MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor(entry.getItem()); | ||
| 186 | ConstInfoAccessor typeEntry = pool.getItem(accessor.getTypeIndex()); | ||
| 187 | return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag(); | ||
| 188 | } | ||
| 189 | }, | ||
| 190 | InvokeDynamicInfo(18) { | ||
| 191 | @Override | ||
| 192 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 193 | InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); | ||
| 194 | gatherIndexTree(indices, editor, accessor.getBootstrapIndex()); | ||
| 195 | gatherIndexTree(indices, editor, accessor.getNameAndTypeIndex()); | ||
| 196 | } | ||
| 197 | |||
| 198 | @Override | ||
| 199 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 200 | InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); | ||
| 201 | accessor.setBootstrapIndex(remapIndex(map, accessor.getBootstrapIndex())); | ||
| 202 | accessor.setNameAndTypeIndex(remapIndex(map, accessor.getNameAndTypeIndex())); | ||
| 203 | } | ||
| 204 | |||
| 205 | @Override | ||
| 206 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 207 | InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor(entry.getItem()); | ||
| 208 | ConstInfoAccessor bootstrapEntry = pool.getItem(accessor.getBootstrapIndex()); | ||
| 209 | ConstInfoAccessor nameAndTypeEntry = pool.getItem(accessor.getNameAndTypeIndex()); | ||
| 210 | return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag() && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag(); | ||
| 211 | } | ||
| 212 | }; | ||
| 213 | |||
| 214 | private static Map<Integer, InfoType> types; | ||
| 215 | |||
| 216 | static { | ||
| 217 | types = Maps.newTreeMap(); | ||
| 218 | for (InfoType type : values()) { | ||
| 219 | types.put(type.getTag(), type); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | private int tag; | ||
| 224 | |||
| 225 | InfoType(int tag) { | ||
| 226 | this.tag = tag; | ||
| 227 | } | ||
| 228 | |||
| 229 | public static InfoType getByTag(int tag) { | ||
| 230 | return types.get(tag); | ||
| 231 | } | ||
| 232 | |||
| 233 | public static void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, int index) { | ||
| 234 | // add own index | ||
| 235 | indices.add(index); | ||
| 236 | |||
| 237 | // recurse | ||
| 238 | ConstInfoAccessor entry = editor.getItem(index); | ||
| 239 | entry.getType().gatherIndexTree(indices, editor, entry); | ||
| 240 | } | ||
| 241 | |||
| 242 | private static int remapIndex(Map<Integer, Integer> map, int index) { | ||
| 243 | Integer newIndex = map.get(index); | ||
| 244 | if (newIndex == null) { | ||
| 245 | newIndex = index; | ||
| 246 | } | ||
| 247 | return newIndex; | ||
| 248 | } | ||
| 249 | |||
| 250 | public int getTag() { | ||
| 251 | return this.tag; | ||
| 252 | } | ||
| 253 | |||
| 254 | public void gatherIndexTree(Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry) { | ||
| 255 | // by default, do nothing | ||
| 256 | } | ||
| 257 | |||
| 258 | public void remapIndices(Map<Integer, Integer> map, ConstInfoAccessor entry) { | ||
| 259 | // by default, do nothing | ||
| 260 | } | ||
| 261 | |||
| 262 | public boolean subIndicesAreValid(ConstInfoAccessor entry, ConstPoolEditor pool) { | ||
| 263 | // by default, everything is good | ||
| 264 | return true; | ||
| 265 | } | ||
| 266 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode; | ||
| 13 | |||
| 14 | import javassist.bytecode.AttributeInfo; | ||
| 15 | import javassist.bytecode.ConstPool; | ||
| 16 | import javassist.bytecode.MethodInfo; | ||
| 17 | |||
| 18 | import java.io.ByteArrayOutputStream; | ||
| 19 | import java.io.DataOutputStream; | ||
| 20 | import java.io.IOException; | ||
| 21 | import java.util.ArrayList; | ||
| 22 | import java.util.List; | ||
| 23 | |||
| 24 | public class MethodParametersAttribute extends AttributeInfo { | ||
| 25 | |||
| 26 | private MethodParametersAttribute(ConstPool pool, List<Integer> parameterNameIndices) { | ||
| 27 | super(pool, "MethodParameters", writeStruct(parameterNameIndices)); | ||
| 28 | } | ||
| 29 | |||
| 30 | public static void updateClass(MethodInfo info, List<String> names) { | ||
| 31 | |||
| 32 | // add the names to the class const pool | ||
| 33 | ConstPool constPool = info.getConstPool(); | ||
| 34 | List<Integer> parameterNameIndices = new ArrayList<>(); | ||
| 35 | for (String name : names) { | ||
| 36 | if (name != null) { | ||
| 37 | parameterNameIndices.add(constPool.addUtf8Info(name)); | ||
| 38 | } else { | ||
| 39 | parameterNameIndices.add(0); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | // add the attribute to the method | ||
| 44 | info.addAttribute(new MethodParametersAttribute(constPool, parameterNameIndices)); | ||
| 45 | } | ||
| 46 | |||
| 47 | private static byte[] writeStruct(List<Integer> parameterNameIndices) { | ||
| 48 | // JVM 8 Spec says the struct looks like this: | ||
| 49 | // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.24 | ||
| 50 | // uint8 num_params | ||
| 51 | // for each param: | ||
| 52 | // uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry | ||
| 53 | // uint16 access_flags -> don't care, just set to 0 | ||
| 54 | |||
| 55 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); | ||
| 56 | DataOutputStream out = new DataOutputStream(buf); | ||
| 57 | |||
| 58 | // NOTE: java hates unsigned integers, so we have to be careful here | ||
| 59 | // the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument | ||
| 60 | // as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte | ||
| 61 | // if the int is out of range, the byte stream won't look the way we want and weird things will happen | ||
| 62 | final int SIZEOF_UINT8 = 1; | ||
| 63 | final int SIZEOF_UINT16 = 2; | ||
| 64 | final int MAX_UINT8 = (1 << 8) - 1; | ||
| 65 | final int MAX_UINT16 = (1 << 16) - 1; | ||
| 66 | |||
| 67 | try { | ||
| 68 | assert (parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8); | ||
| 69 | out.writeByte(parameterNameIndices.size()); | ||
| 70 | |||
| 71 | for (Integer index : parameterNameIndices) { | ||
| 72 | assert (index >= 0 && index <= MAX_UINT16); | ||
| 73 | out.writeShort(index); | ||
| 74 | |||
| 75 | // just write 0 for the access flags | ||
| 76 | out.writeShort(0); | ||
| 77 | } | ||
| 78 | |||
| 79 | out.close(); | ||
| 80 | byte[] data = buf.toByteArray(); | ||
| 81 | assert (data.length == SIZEOF_UINT8 + parameterNameIndices.size() * (SIZEOF_UINT16 + SIZEOF_UINT16)); | ||
| 82 | return data; | ||
| 83 | } catch (IOException ex) { | ||
| 84 | throw new Error(ex); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.accessors; | ||
| 13 | |||
| 14 | import java.lang.reflect.Field; | ||
| 15 | |||
| 16 | public class ClassInfoAccessor { | ||
| 17 | |||
| 18 | private static Class<?> clazz; | ||
| 19 | private static Field nameIndex; | ||
| 20 | |||
| 21 | static { | ||
| 22 | try { | ||
| 23 | clazz = Class.forName("javassist.bytecode.ClassInfo"); | ||
| 24 | nameIndex = clazz.getDeclaredField("name"); | ||
| 25 | nameIndex.setAccessible(true); | ||
| 26 | } catch (Exception ex) { | ||
| 27 | throw new Error(ex); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | private Object item; | ||
| 32 | |||
| 33 | public ClassInfoAccessor(Object item) { | ||
| 34 | this.item = item; | ||
| 35 | } | ||
| 36 | |||
| 37 | public static boolean isType(ConstInfoAccessor accessor) { | ||
| 38 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | ||
| 39 | } | ||
| 40 | |||
| 41 | public int getNameIndex() { | ||
| 42 | try { | ||
| 43 | return (Integer) nameIndex.get(this.item); | ||
| 44 | } catch (Exception ex) { | ||
| 45 | throw new Error(ex); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | public void setNameIndex(int val) { | ||
| 50 | try { | ||
| 51 | nameIndex.set(this.item, val); | ||
| 52 | } catch (Exception ex) { | ||
| 53 | throw new Error(ex); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.accessors; | ||
| 13 | |||
| 14 | import com.google.common.base.Charsets; | ||
| 15 | import cuchaz.enigma.bytecode.InfoType; | ||
| 16 | |||
| 17 | import java.io.*; | ||
| 18 | import java.lang.reflect.Field; | ||
| 19 | import java.lang.reflect.Method; | ||
| 20 | |||
| 21 | public class ConstInfoAccessor { | ||
| 22 | |||
| 23 | private static Class<?> clazz; | ||
| 24 | private static Field index; | ||
| 25 | private static Method getTag; | ||
| 26 | |||
| 27 | static { | ||
| 28 | try { | ||
| 29 | clazz = Class.forName("javassist.bytecode.ConstInfo"); | ||
| 30 | index = clazz.getDeclaredField("index"); | ||
| 31 | index.setAccessible(true); | ||
| 32 | getTag = clazz.getMethod("getTag"); | ||
| 33 | getTag.setAccessible(true); | ||
| 34 | } catch (Exception ex) { | ||
| 35 | throw new Error(ex); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | private Object item; | ||
| 40 | |||
| 41 | public ConstInfoAccessor(Object item) { | ||
| 42 | if (item == null) { | ||
| 43 | throw new IllegalArgumentException("item cannot be null!"); | ||
| 44 | } | ||
| 45 | this.item = item; | ||
| 46 | } | ||
| 47 | |||
| 48 | public Object getItem() { | ||
| 49 | return this.item; | ||
| 50 | } | ||
| 51 | |||
| 52 | public int getIndex() { | ||
| 53 | try { | ||
| 54 | return (Integer) index.get(this.item); | ||
| 55 | } catch (Exception ex) { | ||
| 56 | throw new Error(ex); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | public int getTag() { | ||
| 61 | try { | ||
| 62 | return (Integer) getTag.invoke(this.item); | ||
| 63 | } catch (Exception ex) { | ||
| 64 | throw new Error(ex); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | public ConstInfoAccessor copy() { | ||
| 69 | return new ConstInfoAccessor(copyItem()); | ||
| 70 | } | ||
| 71 | |||
| 72 | public Object copyItem() { | ||
| 73 | // I don't know of a simpler way to copy one of these silly things... | ||
| 74 | try { | ||
| 75 | // serialize the item | ||
| 76 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); | ||
| 77 | DataOutputStream out = new DataOutputStream(buf); | ||
| 78 | write(out); | ||
| 79 | |||
| 80 | // deserialize the item | ||
| 81 | DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf.toByteArray())); | ||
| 82 | Object item = new ConstInfoAccessor(in).getItem(); | ||
| 83 | in.close(); | ||
| 84 | |||
| 85 | return item; | ||
| 86 | } catch (Exception ex) { | ||
| 87 | throw new Error(ex); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | public void write(DataOutputStream out) throws IOException { | ||
| 92 | try { | ||
| 93 | out.writeUTF(this.item.getClass().getName()); | ||
| 94 | out.writeInt(getIndex()); | ||
| 95 | |||
| 96 | Method method = this.item.getClass().getMethod("write", DataOutputStream.class); | ||
| 97 | method.setAccessible(true); | ||
| 98 | method.invoke(this.item, out); | ||
| 99 | } catch (IOException ex) { | ||
| 100 | throw ex; | ||
| 101 | } catch (Exception ex) { | ||
| 102 | throw new Error(ex); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | @Override | ||
| 107 | public String toString() { | ||
| 108 | try { | ||
| 109 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); | ||
| 110 | PrintWriter out = new PrintWriter(new OutputStreamWriter(buf, Charsets.UTF_8)); | ||
| 111 | Method print = this.item.getClass().getMethod("print", PrintWriter.class); | ||
| 112 | print.setAccessible(true); | ||
| 113 | print.invoke(this.item, out); | ||
| 114 | out.close(); | ||
| 115 | return buf.toString("UTF-8").replace("\n", ""); | ||
| 116 | } catch (Exception ex) { | ||
| 117 | throw new Error(ex); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | public InfoType getType() { | ||
| 122 | return InfoType.getByTag(getTag()); | ||
| 123 | } | ||
| 124 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.accessors; | ||
| 13 | |||
| 14 | import java.lang.reflect.Field; | ||
| 15 | |||
| 16 | public class InvokeDynamicInfoAccessor { | ||
| 17 | |||
| 18 | private static Class<?> clazz; | ||
| 19 | private static Field bootstrapIndex; | ||
| 20 | private static Field nameAndTypeIndex; | ||
| 21 | |||
| 22 | static { | ||
| 23 | try { | ||
| 24 | clazz = Class.forName("javassist.bytecode.InvokeDynamicInfo"); | ||
| 25 | bootstrapIndex = clazz.getDeclaredField("bootstrap"); | ||
| 26 | bootstrapIndex.setAccessible(true); | ||
| 27 | nameAndTypeIndex = clazz.getDeclaredField("nameAndType"); | ||
| 28 | nameAndTypeIndex.setAccessible(true); | ||
| 29 | } catch (Exception ex) { | ||
| 30 | throw new Error(ex); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | private Object item; | ||
| 35 | |||
| 36 | public InvokeDynamicInfoAccessor(Object item) { | ||
| 37 | this.item = item; | ||
| 38 | } | ||
| 39 | |||
| 40 | public static boolean isType(ConstInfoAccessor accessor) { | ||
| 41 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | ||
| 42 | } | ||
| 43 | |||
| 44 | public int getBootstrapIndex() { | ||
| 45 | try { | ||
| 46 | return (Integer) bootstrapIndex.get(this.item); | ||
| 47 | } catch (Exception ex) { | ||
| 48 | throw new Error(ex); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | public void setBootstrapIndex(int val) { | ||
| 53 | try { | ||
| 54 | bootstrapIndex.set(this.item, val); | ||
| 55 | } catch (Exception ex) { | ||
| 56 | throw new Error(ex); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | public int getNameAndTypeIndex() { | ||
| 61 | try { | ||
| 62 | return (Integer) nameAndTypeIndex.get(this.item); | ||
| 63 | } catch (Exception ex) { | ||
| 64 | throw new Error(ex); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | public void setNameAndTypeIndex(int val) { | ||
| 69 | try { | ||
| 70 | nameAndTypeIndex.set(this.item, val); | ||
| 71 | } catch (Exception ex) { | ||
| 72 | throw new Error(ex); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.accessors; | ||
| 13 | |||
| 14 | import java.lang.reflect.Field; | ||
| 15 | |||
| 16 | public class MemberRefInfoAccessor { | ||
| 17 | |||
| 18 | private static Class<?> clazz; | ||
| 19 | private static Field classIndex; | ||
| 20 | private static Field nameAndTypeIndex; | ||
| 21 | |||
| 22 | static { | ||
| 23 | try { | ||
| 24 | clazz = Class.forName("javassist.bytecode.MemberrefInfo"); | ||
| 25 | classIndex = clazz.getDeclaredField("classIndex"); | ||
| 26 | classIndex.setAccessible(true); | ||
| 27 | nameAndTypeIndex = clazz.getDeclaredField("nameAndTypeIndex"); | ||
| 28 | nameAndTypeIndex.setAccessible(true); | ||
| 29 | } catch (Exception ex) { | ||
| 30 | throw new Error(ex); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | private Object item; | ||
| 35 | |||
| 36 | public MemberRefInfoAccessor(Object item) { | ||
| 37 | this.item = item; | ||
| 38 | } | ||
| 39 | |||
| 40 | public static boolean isType(ConstInfoAccessor accessor) { | ||
| 41 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | ||
| 42 | } | ||
| 43 | |||
| 44 | public int getClassIndex() { | ||
| 45 | try { | ||
| 46 | return (Integer) classIndex.get(this.item); | ||
| 47 | } catch (Exception ex) { | ||
| 48 | throw new Error(ex); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | public void setClassIndex(int val) { | ||
| 53 | try { | ||
| 54 | classIndex.set(this.item, val); | ||
| 55 | } catch (Exception ex) { | ||
| 56 | throw new Error(ex); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | public int getNameAndTypeIndex() { | ||
| 61 | try { | ||
| 62 | return (Integer) nameAndTypeIndex.get(this.item); | ||
| 63 | } catch (Exception ex) { | ||
| 64 | throw new Error(ex); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | public void setNameAndTypeIndex(int val) { | ||
| 69 | try { | ||
| 70 | nameAndTypeIndex.set(this.item, val); | ||
| 71 | } catch (Exception ex) { | ||
| 72 | throw new Error(ex); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.accessors; | ||
| 13 | |||
| 14 | import java.lang.reflect.Field; | ||
| 15 | |||
| 16 | public class MethodHandleInfoAccessor { | ||
| 17 | |||
| 18 | private static Class<?> clazz; | ||
| 19 | private static Field kindIndex; | ||
| 20 | private static Field indexIndex; | ||
| 21 | |||
| 22 | static { | ||
| 23 | try { | ||
| 24 | clazz = Class.forName("javassist.bytecode.MethodHandleInfo"); | ||
| 25 | kindIndex = clazz.getDeclaredField("refKind"); | ||
| 26 | kindIndex.setAccessible(true); | ||
| 27 | indexIndex = clazz.getDeclaredField("refIndex"); | ||
| 28 | indexIndex.setAccessible(true); | ||
| 29 | } catch (Exception ex) { | ||
| 30 | throw new Error(ex); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | private Object item; | ||
| 35 | |||
| 36 | public MethodHandleInfoAccessor(Object item) { | ||
| 37 | this.item = item; | ||
| 38 | } | ||
| 39 | |||
| 40 | public static boolean isType(ConstInfoAccessor accessor) { | ||
| 41 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | ||
| 42 | } | ||
| 43 | |||
| 44 | public int getTypeIndex() { | ||
| 45 | try { | ||
| 46 | return (Integer) kindIndex.get(this.item); | ||
| 47 | } catch (Exception ex) { | ||
| 48 | throw new Error(ex); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | public void setTypeIndex(int val) { | ||
| 53 | try { | ||
| 54 | kindIndex.set(this.item, val); | ||
| 55 | } catch (Exception ex) { | ||
| 56 | throw new Error(ex); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | public int getMethodRefIndex() { | ||
| 61 | try { | ||
| 62 | return (Integer) indexIndex.get(this.item); | ||
| 63 | } catch (Exception ex) { | ||
| 64 | throw new Error(ex); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | public void setMethodRefIndex(int val) { | ||
| 69 | try { | ||
| 70 | indexIndex.set(this.item, val); | ||
| 71 | } catch (Exception ex) { | ||
| 72 | throw new Error(ex); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.accessors; | ||
| 13 | |||
| 14 | import java.lang.reflect.Field; | ||
| 15 | |||
| 16 | public class MethodTypeInfoAccessor { | ||
| 17 | |||
| 18 | private static Class<?> clazz; | ||
| 19 | private static Field descriptorIndex; | ||
| 20 | |||
| 21 | static { | ||
| 22 | try { | ||
| 23 | clazz = Class.forName("javassist.bytecode.MethodTypeInfo"); | ||
| 24 | descriptorIndex = clazz.getDeclaredField("descriptor"); | ||
| 25 | descriptorIndex.setAccessible(true); | ||
| 26 | } catch (Exception ex) { | ||
| 27 | throw new Error(ex); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | private Object item; | ||
| 32 | |||
| 33 | public MethodTypeInfoAccessor(Object item) { | ||
| 34 | this.item = item; | ||
| 35 | } | ||
| 36 | |||
| 37 | public static boolean isType(ConstInfoAccessor accessor) { | ||
| 38 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | ||
| 39 | } | ||
| 40 | |||
| 41 | public int getTypeIndex() { | ||
| 42 | try { | ||
| 43 | return (Integer) descriptorIndex.get(this.item); | ||
| 44 | } catch (Exception ex) { | ||
| 45 | throw new Error(ex); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | public void setTypeIndex(int val) { | ||
| 50 | try { | ||
| 51 | descriptorIndex.set(this.item, val); | ||
| 52 | } catch (Exception ex) { | ||
| 53 | throw new Error(ex); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.accessors; | ||
| 13 | |||
| 14 | import java.lang.reflect.Field; | ||
| 15 | |||
| 16 | public class NameAndTypeInfoAccessor { | ||
| 17 | |||
| 18 | private static Class<?> clazz; | ||
| 19 | private static Field nameIndex; | ||
| 20 | private static Field typeIndex; | ||
| 21 | |||
| 22 | static { | ||
| 23 | try { | ||
| 24 | clazz = Class.forName("javassist.bytecode.NameAndTypeInfo"); | ||
| 25 | nameIndex = clazz.getDeclaredField("memberName"); | ||
| 26 | nameIndex.setAccessible(true); | ||
| 27 | typeIndex = clazz.getDeclaredField("typeDescriptor"); | ||
| 28 | typeIndex.setAccessible(true); | ||
| 29 | } catch (Exception ex) { | ||
| 30 | throw new Error(ex); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | private Object item; | ||
| 35 | |||
| 36 | public NameAndTypeInfoAccessor(Object item) { | ||
| 37 | this.item = item; | ||
| 38 | } | ||
| 39 | |||
| 40 | public static boolean isType(ConstInfoAccessor accessor) { | ||
| 41 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | ||
| 42 | } | ||
| 43 | |||
| 44 | public int getNameIndex() { | ||
| 45 | try { | ||
| 46 | return (Integer) nameIndex.get(this.item); | ||
| 47 | } catch (Exception ex) { | ||
| 48 | throw new Error(ex); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | public void setNameIndex(int val) { | ||
| 53 | try { | ||
| 54 | nameIndex.set(this.item, val); | ||
| 55 | } catch (Exception ex) { | ||
| 56 | throw new Error(ex); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | public int getTypeIndex() { | ||
| 61 | try { | ||
| 62 | return (Integer) typeIndex.get(this.item); | ||
| 63 | } catch (Exception ex) { | ||
| 64 | throw new Error(ex); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | public void setTypeIndex(int val) { | ||
| 69 | try { | ||
| 70 | typeIndex.set(this.item, val); | ||
| 71 | } catch (Exception ex) { | ||
| 72 | throw new Error(ex); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.accessors; | ||
| 13 | |||
| 14 | import java.lang.reflect.Field; | ||
| 15 | |||
| 16 | public class StringInfoAccessor { | ||
| 17 | |||
| 18 | private static Class<?> clazz; | ||
| 19 | private static Field stringIndex; | ||
| 20 | |||
| 21 | static { | ||
| 22 | try { | ||
| 23 | clazz = Class.forName("javassist.bytecode.StringInfo"); | ||
| 24 | stringIndex = clazz.getDeclaredField("string"); | ||
| 25 | stringIndex.setAccessible(true); | ||
| 26 | } catch (Exception ex) { | ||
| 27 | throw new Error(ex); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | private Object item; | ||
| 32 | |||
| 33 | public StringInfoAccessor(Object item) { | ||
| 34 | this.item = item; | ||
| 35 | } | ||
| 36 | |||
| 37 | public static boolean isType(ConstInfoAccessor accessor) { | ||
| 38 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | ||
| 39 | } | ||
| 40 | |||
| 41 | public int getStringIndex() { | ||
| 42 | try { | ||
| 43 | return (Integer) stringIndex.get(this.item); | ||
| 44 | } catch (Exception ex) { | ||
| 45 | throw new Error(ex); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | public void setStringIndex(int val) { | ||
| 50 | try { | ||
| 51 | stringIndex.set(this.item, val); | ||
| 52 | } catch (Exception ex) { | ||
| 53 | throw new Error(ex); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.accessors; | ||
| 13 | |||
| 14 | public class Utf8InfoAccessor { | ||
| 15 | |||
| 16 | private static Class<?> clazz; | ||
| 17 | |||
| 18 | static { | ||
| 19 | try { | ||
| 20 | clazz = Class.forName("javassist.bytecode.Utf8Info"); | ||
| 21 | } catch (Exception ex) { | ||
| 22 | throw new Error(ex); | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | public static boolean isType(ConstInfoAccessor accessor) { | ||
| 27 | return clazz.isAssignableFrom(accessor.getItem().getClass()); | ||
| 28 | } | ||
| 29 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.translators; | ||
| 13 | |||
| 14 | import cuchaz.enigma.bytecode.ClassRenamer; | ||
| 15 | import cuchaz.enigma.bytecode.ConstPoolEditor; | ||
| 16 | import cuchaz.enigma.mapping.*; | ||
| 17 | import javassist.CtBehavior; | ||
| 18 | import javassist.CtClass; | ||
| 19 | import javassist.CtField; | ||
| 20 | import javassist.CtMethod; | ||
| 21 | import javassist.bytecode.*; | ||
| 22 | |||
| 23 | public class ClassTranslator { | ||
| 24 | |||
| 25 | public static void translate(Translator translator, CtClass c) { | ||
| 26 | |||
| 27 | // NOTE: the order of these translations is very important | ||
| 28 | |||
| 29 | // translate all the field and method references in the code by editing the constant pool | ||
| 30 | ConstPool constants = c.getClassFile().getConstPool(); | ||
| 31 | ConstPoolEditor editor = new ConstPoolEditor(constants); | ||
| 32 | for (int i = 1; i < constants.getSize(); i++) { | ||
| 33 | switch (constants.getTag(i)) { | ||
| 34 | |||
| 35 | case ConstPool.CONST_Fieldref: { | ||
| 36 | |||
| 37 | // translate the name and type | ||
| 38 | FieldEntry entry = EntryFactory.getFieldEntry( | ||
| 39 | Descriptor.toJvmName(constants.getFieldrefClassName(i)), | ||
| 40 | constants.getFieldrefName(i), | ||
| 41 | constants.getFieldrefType(i) | ||
| 42 | ); | ||
| 43 | FieldEntry translatedEntry = translator.translateEntry(entry); | ||
| 44 | if (!entry.equals(translatedEntry)) { | ||
| 45 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | break; | ||
| 49 | |||
| 50 | case ConstPool.CONST_Methodref: | ||
| 51 | case ConstPool.CONST_InterfaceMethodref: { | ||
| 52 | |||
| 53 | // translate the name and type (ie signature) | ||
| 54 | BehaviorEntry entry = EntryFactory.getBehaviorEntry( | ||
| 55 | Descriptor.toJvmName(editor.getMemberrefClassname(i)), | ||
| 56 | editor.getMemberrefName(i), | ||
| 57 | editor.getMemberrefType(i) | ||
| 58 | ); | ||
| 59 | BehaviorEntry translatedEntry = translator.translateEntry(entry); | ||
| 60 | if (!entry.equals(translatedEntry)) { | ||
| 61 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | break; | ||
| 65 | default: | ||
| 66 | break; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); | ||
| 71 | Mappings.EntryModifier modifier = translator.getModifier(classEntry); | ||
| 72 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) | ||
| 73 | ClassRenamer.applyModifier(c, modifier); | ||
| 74 | |||
| 75 | // translate all the fields | ||
| 76 | for (CtField field : c.getDeclaredFields()) { | ||
| 77 | |||
| 78 | // translate the name | ||
| 79 | FieldEntry entry = EntryFactory.getFieldEntry(field); | ||
| 80 | String translatedName = translator.translate(entry); | ||
| 81 | modifier = translator.getModifier(entry); | ||
| 82 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) | ||
| 83 | ClassRenamer.applyModifier(field, modifier); | ||
| 84 | |||
| 85 | if (translatedName != null) { | ||
| 86 | field.setName(translatedName); | ||
| 87 | } | ||
| 88 | |||
| 89 | // translate the type | ||
| 90 | Type translatedType = translator.translateType(entry.getType()); | ||
| 91 | field.getFieldInfo().setDescriptor(translatedType.toString()); | ||
| 92 | } | ||
| 93 | |||
| 94 | // translate all the methods and constructors | ||
| 95 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | ||
| 96 | |||
| 97 | BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); | ||
| 98 | |||
| 99 | modifier = translator.getModifier(entry); | ||
| 100 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) | ||
| 101 | ClassRenamer.applyModifier(behavior, modifier); | ||
| 102 | |||
| 103 | if (behavior instanceof CtMethod) { | ||
| 104 | CtMethod method = (CtMethod) behavior; | ||
| 105 | |||
| 106 | // translate the name | ||
| 107 | String translatedName = translator.translate(entry); | ||
| 108 | if (translatedName != null) { | ||
| 109 | method.setName(translatedName); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | if (entry.getSignature() != null) { | ||
| 114 | // translate the signature | ||
| 115 | Signature translatedSignature = translator.translateSignature(entry.getSignature()); | ||
| 116 | behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | // translate the EnclosingMethod attribute | ||
| 121 | EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); | ||
| 122 | if (enclosingMethodAttr != null) { | ||
| 123 | |||
| 124 | if (enclosingMethodAttr.methodIndex() == 0) { | ||
| 125 | BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); | ||
| 126 | BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry); | ||
| 127 | c.getClassFile().addAttribute(new EnclosingMethodAttribute( | ||
| 128 | constants, | ||
| 129 | deobfBehaviorEntry.getClassName() | ||
| 130 | )); | ||
| 131 | } else { | ||
| 132 | BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( | ||
| 133 | Descriptor.toJvmName(enclosingMethodAttr.className()), | ||
| 134 | enclosingMethodAttr.methodName(), | ||
| 135 | enclosingMethodAttr.methodDescriptor() | ||
| 136 | ); | ||
| 137 | BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry); | ||
| 138 | c.getClassFile().addAttribute(new EnclosingMethodAttribute( | ||
| 139 | constants, | ||
| 140 | deobfBehaviorEntry.getClassName(), | ||
| 141 | deobfBehaviorEntry.getName(), | ||
| 142 | deobfBehaviorEntry.getSignature().toString() | ||
| 143 | )); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | // translate all the class names referenced in the code | ||
| 148 | // the above code only changed method/field/reference names and types, but not the rest of the class references | ||
| 149 | ClassRenamer.renameClasses(c, translator); | ||
| 150 | |||
| 151 | // translate the source file attribute too | ||
| 152 | ClassEntry deobfClassEntry = translator.translateEntry(classEntry); | ||
| 153 | if (deobfClassEntry != null) { | ||
| 154 | String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; | ||
| 155 | c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); | ||
| 156 | } | ||
| 157 | InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 158 | if (attr != null) | ||
| 159 | InnerClassWriter.changeModifier(c, attr, translator); | ||
| 160 | } | ||
| 161 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.translators; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import cuchaz.enigma.analysis.JarIndex; | ||
| 16 | import cuchaz.enigma.bytecode.ClassRenamer; | ||
| 17 | import cuchaz.enigma.mapping.*; | ||
| 18 | import javassist.ClassPool; | ||
| 19 | import javassist.CtClass; | ||
| 20 | import javassist.NotFoundException; | ||
| 21 | import javassist.bytecode.*; | ||
| 22 | |||
| 23 | import java.util.Collection; | ||
| 24 | import java.util.List; | ||
| 25 | |||
| 26 | public class InnerClassWriter { | ||
| 27 | |||
| 28 | // FIXME: modifier is not applied to inner class | ||
| 29 | public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) { | ||
| 30 | ClassPool pool = c.getClassPool(); | ||
| 31 | for (int i = 0; i < attr.tableLength(); i++) { | ||
| 32 | |||
| 33 | String innerName = attr.innerClass(i); | ||
| 34 | // get the inner class full name (which has already been translated) | ||
| 35 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); | ||
| 36 | try { | ||
| 37 | CtClass innerClass = pool.get(innerName); | ||
| 38 | Mappings.EntryModifier modifier = translator.getModifier(classEntry); | ||
| 39 | if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) | ||
| 40 | ClassRenamer.applyModifier(innerClass, modifier); | ||
| 41 | } catch (NotFoundException e) { | ||
| 42 | // This shouldn't be possible in theory | ||
| 43 | //e.printStackTrace(); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | public static void write(JarIndex index, CtClass c) { | ||
| 49 | |||
| 50 | // don't change anything if there's already an attribute there | ||
| 51 | InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 52 | if (oldAttr != null) { | ||
| 53 | // bail! | ||
| 54 | return; | ||
| 55 | } | ||
| 56 | |||
| 57 | ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); | ||
| 58 | List<ClassEntry> obfClassChain = index.getObfClassChain(obfClassEntry); | ||
| 59 | |||
| 60 | boolean isInnerClass = obfClassChain.size() > 1; | ||
| 61 | if (isInnerClass) { | ||
| 62 | |||
| 63 | // it's an inner class, rename it to the fully qualified name | ||
| 64 | c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); | ||
| 65 | |||
| 66 | BehaviorEntry caller = index.getAnonymousClassCaller(obfClassEntry); | ||
| 67 | if (caller != null) { | ||
| 68 | |||
| 69 | // write the enclosing method attribute | ||
| 70 | if (caller.getName().equals("<clinit>")) { | ||
| 71 | c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); | ||
| 72 | } else { | ||
| 73 | c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | // does this class have any inner classes? | ||
| 79 | Collection<ClassEntry> obfInnerClassEntries = index.getInnerClasses(obfClassEntry); | ||
| 80 | |||
| 81 | if (isInnerClass || !obfInnerClassEntries.isEmpty()) { | ||
| 82 | |||
| 83 | // create an inner class attribute | ||
| 84 | InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); | ||
| 85 | c.getClassFile().addAttribute(attr); | ||
| 86 | |||
| 87 | // write the ancestry, but not the outermost class | ||
| 88 | for (int i = 1; i < obfClassChain.size(); i++) { | ||
| 89 | ClassEntry obfInnerClassEntry = obfClassChain.get(i); | ||
| 90 | writeInnerClass(index, attr, obfClassChain, obfInnerClassEntry); | ||
| 91 | |||
| 92 | // update references to use the fully qualified inner class name | ||
| 93 | c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); | ||
| 94 | } | ||
| 95 | |||
| 96 | // write the inner classes | ||
| 97 | for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { | ||
| 98 | |||
| 99 | // extend the class chain | ||
| 100 | List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain); | ||
| 101 | extendedObfClassChain.add(obfInnerClassEntry); | ||
| 102 | |||
| 103 | writeInnerClass(index, attr, extendedObfClassChain, obfInnerClassEntry); | ||
| 104 | |||
| 105 | // update references to use the fully qualified inner class name | ||
| 106 | c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | private static void writeInnerClass(JarIndex index, InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) { | ||
| 112 | |||
| 113 | // get the new inner class name | ||
| 114 | ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); | ||
| 115 | ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); | ||
| 116 | |||
| 117 | // here's what the JVM spec says about the InnerClasses attribute | ||
| 118 | // append(inner, parent, 0 if anonymous else simple name, flags); | ||
| 119 | |||
| 120 | // update the attribute with this inner class | ||
| 121 | ConstPool constPool = attr.getConstPool(); | ||
| 122 | int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); | ||
| 123 | int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); | ||
| 124 | int innerClassNameIndex = 0; | ||
| 125 | int accessFlags = AccessFlag.PUBLIC; | ||
| 126 | // TODO: need to figure out if we can put static or not | ||
| 127 | if (!index.isAnonymousClass(obfClassEntry)) { | ||
| 128 | innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); | ||
| 129 | } | ||
| 130 | |||
| 131 | attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); | ||
| 132 | |||
| 133 | /* DEBUG | ||
| 134 | System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", | ||
| 135 | obfClassEntry, | ||
| 136 | attr.innerClass(attr.tableLength() - 1), | ||
| 137 | attr.outerClass(attr.tableLength() - 1), | ||
| 138 | attr.innerName(attr.tableLength() - 1), | ||
| 139 | Constants.NonePackage + "/" + obfInnerClassName, | ||
| 140 | obfClassEntry.getName() | ||
| 141 | )); | ||
| 142 | */ | ||
| 143 | } | ||
| 144 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.translators; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.*; | ||
| 15 | import javassist.CtBehavior; | ||
| 16 | import javassist.CtClass; | ||
| 17 | import javassist.bytecode.*; | ||
| 18 | |||
| 19 | public class LocalVariableTranslator { | ||
| 20 | |||
| 21 | public static void translate(Translator translator, CtClass c) { | ||
| 22 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | ||
| 23 | |||
| 24 | // if there's a local variable table, just rename everything to v1, v2, v3, ... for now | ||
| 25 | CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); | ||
| 26 | if (codeAttribute == null) { | ||
| 27 | continue; | ||
| 28 | } | ||
| 29 | |||
| 30 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | ||
| 31 | ConstPool constants = c.getClassFile().getConstPool(); | ||
| 32 | |||
| 33 | LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); | ||
| 34 | if (table != null) { | ||
| 35 | renameLVT(translator, behaviorEntry, constants, table, c); | ||
| 36 | } | ||
| 37 | |||
| 38 | LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); | ||
| 39 | if (typeTable != null) { | ||
| 40 | renameLVTT(typeTable, table); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | // DEBUG | ||
| 46 | @SuppressWarnings("unused") | ||
| 47 | private static void dumpTable(LocalVariableAttribute table) { | ||
| 48 | for (int i = 0; i < table.tableLength(); i++) { | ||
| 49 | System.out.println(String.format("\t%d (%d): %s %s", | ||
| 50 | i, table.index(i), table.variableName(i), table.descriptor(i) | ||
| 51 | )); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | private static void renameLVT(Translator translator, BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) { | ||
| 56 | |||
| 57 | // skip empty tables | ||
| 58 | if (table.tableLength() <= 0) { | ||
| 59 | return; | ||
| 60 | } | ||
| 61 | |||
| 62 | // where do we start counting variables? | ||
| 63 | int starti = 0; | ||
| 64 | if (table.variableName(0).equals("this")) { | ||
| 65 | // skip the "this" variable | ||
| 66 | starti++; | ||
| 67 | } | ||
| 68 | |||
| 69 | // rename method arguments first | ||
| 70 | int numArgs = 0; | ||
| 71 | if (behaviorEntry.getSignature() != null) { | ||
| 72 | numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); | ||
| 73 | boolean isNestedClassConstructor = false; | ||
| 74 | |||
| 75 | // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! | ||
| 76 | if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) { | ||
| 77 | // Get the first arg type | ||
| 78 | Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); | ||
| 79 | |||
| 80 | // 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 | ||
| 81 | if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { | ||
| 82 | isNestedClassConstructor = true; | ||
| 83 | numArgs--; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { | ||
| 88 | int argi = i - starti; | ||
| 89 | if (ctClass.isEnum()) | ||
| 90 | argi += 2; | ||
| 91 | String argName = translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); | ||
| 92 | if (argName == null) { | ||
| 93 | int argIndex = isNestedClassConstructor ? argi + 1 : argi; | ||
| 94 | if (ctClass.isEnum()) | ||
| 95 | argIndex -= 2; | ||
| 96 | Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex); | ||
| 97 | // Unfortunately each of these have different name getters, so they have different code paths | ||
| 98 | if (argType.isPrimitive()) { | ||
| 99 | Type.Primitive argCls = argType.getPrimitive(); | ||
| 100 | argName = "a" + argCls.name() + (argIndex + 1); | ||
| 101 | } else if (argType.isArray()) { | ||
| 102 | // List types would require this whole block again, so just go with aListx | ||
| 103 | argName = "aList" + (argIndex + 1); | ||
| 104 | } else if (argType.isClass()) { | ||
| 105 | ClassEntry argClsTrans = translator.translateEntry(argType.getClassEntry()); | ||
| 106 | argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1); | ||
| 107 | } else { | ||
| 108 | argName = "a" + (argIndex + 1); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | renameVariable(table, i, constants.addUtf8Info(argName)); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | // then rename the rest of the args, if any | ||
| 116 | for (int i = starti + numArgs; i < table.tableLength(); i++) { | ||
| 117 | int firstIndex = Math.min(table.index(starti + numArgs), table.index(i)); | ||
| 118 | renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | private static void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { | ||
| 123 | // rename args to the same names as in the LVT | ||
| 124 | for (int i = 0; i < typeTable.tableLength(); i++) { | ||
| 125 | renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | private static void renameVariable(LocalVariableAttribute table, int i, int stringId) { | ||
| 130 | // based off of LocalVariableAttribute.nameIndex() | ||
| 131 | ByteArray.write16bit(stringId, table.get(), i * 10 + 6); | ||
| 132 | } | ||
| 133 | |||
| 134 | private static int getNameIndex(LocalVariableAttribute table, int index) { | ||
| 135 | for (int i = 0; i < table.tableLength(); i++) { | ||
| 136 | if (table.index(i) == index) { | ||
| 137 | return table.nameIndex(i); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.translators; | ||
| 13 | |||
| 14 | import cuchaz.enigma.bytecode.MethodParametersAttribute; | ||
| 15 | import cuchaz.enigma.mapping.*; | ||
| 16 | import javassist.CtBehavior; | ||
| 17 | import javassist.CtClass; | ||
| 18 | import javassist.bytecode.CodeAttribute; | ||
| 19 | import javassist.bytecode.LocalVariableAttribute; | ||
| 20 | |||
| 21 | import java.util.ArrayList; | ||
| 22 | import java.util.List; | ||
| 23 | |||
| 24 | public class MethodParameterTranslator { | ||
| 25 | |||
| 26 | public static void translate(Translator translator, CtClass c) { | ||
| 27 | |||
| 28 | // Procyon will read method arguments from the "MethodParameters" attribute, so write those | ||
| 29 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | ||
| 30 | |||
| 31 | // if there's a local variable table here, don't write a MethodParameters attribute | ||
| 32 | // let the local variable writer deal with it instead | ||
| 33 | // procyon starts doing really weird things if we give it both attributes | ||
| 34 | CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); | ||
| 35 | if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { | ||
| 36 | continue; | ||
| 37 | } | ||
| 38 | |||
| 39 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | ||
| 40 | |||
| 41 | // get the number of arguments | ||
| 42 | Signature signature = behaviorEntry.getSignature(); | ||
| 43 | if (signature == null) { | ||
| 44 | // static initializers have no signatures, or arguments | ||
| 45 | continue; | ||
| 46 | } | ||
| 47 | int numParams = signature.getArgumentTypes().size(); | ||
| 48 | if (numParams <= 0) { | ||
| 49 | continue; | ||
| 50 | } | ||
| 51 | |||
| 52 | // get the list of argument names | ||
| 53 | List<String> names = new ArrayList<>(numParams); | ||
| 54 | for (int i = 0; i < numParams; i++) { | ||
| 55 | names.add(translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); | ||
| 56 | } | ||
| 57 | |||
| 58 | // save the mappings to the class | ||
| 59 | MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
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 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.bytecode.translators; | ||
| 13 | |||
| 14 | import cuchaz.enigma.analysis.JarIndex; | ||
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 16 | import cuchaz.enigma.mapping.*; | ||
| 17 | import org.objectweb.asm.*; | ||
| 18 | |||
| 19 | import java.util.regex.Pattern; | ||
| 20 | |||
| 21 | public class TranslationClassVisitor extends ClassVisitor { | ||
| 22 | private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*"); | ||
| 23 | |||
| 24 | private final Translator translator; | ||
| 25 | private final JarIndex jarIndex; | ||
| 26 | private final ReferencedEntryPool entryPool; | ||
| 27 | |||
| 28 | private ClassDefEntry obfClassEntry; | ||
| 29 | |||
| 30 | public TranslationClassVisitor(Translator translator, JarIndex jarIndex, ReferencedEntryPool entryPool, int api, ClassVisitor cv) { | ||
| 31 | super(api, cv); | ||
| 32 | this.translator = translator; | ||
| 33 | this.jarIndex = jarIndex; | ||
| 34 | this.entryPool = entryPool; | ||
| 35 | } | ||
| 36 | |||
| 37 | @Override | ||
| 38 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||
| 39 | if (signature != null && OBJECT_PATTERN.matcher(signature).matches()) { | ||
| 40 | signature = signature.replaceAll(":Ljava/lang/Object;:", "::"); | ||
| 41 | } | ||
| 42 | obfClassEntry = new ClassDefEntry(name, new AccessFlags(access)); | ||
| 43 | ClassDefEntry entry = translator.getTranslatedClassDef(obfClassEntry); | ||
| 44 | ClassEntry superEntry = translator.getTranslatedClass(entryPool.getClass(superName)); | ||
| 45 | String[] translatedInterfaces = new String[interfaces.length]; | ||
| 46 | for (int i = 0; i < interfaces.length; i++) { | ||
| 47 | translatedInterfaces[i] = translator.getTranslatedClass(entryPool.getClass(interfaces[i])).getName(); | ||
| 48 | } | ||
| 49 | super.visit(version, entry.getAccess().getFlags(), entry.getName(), signature, superEntry.getName(), translatedInterfaces); | ||
| 50 | } | ||
| 51 | |||
| 52 | @Override | ||
| 53 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { | ||
| 54 | FieldDefEntry entry = new FieldDefEntry(obfClassEntry, name, new TypeDescriptor(desc), new AccessFlags(access)); | ||
| 55 | FieldDefEntry translatedEntry = translator.getTranslatedFieldDef(entry); | ||
| 56 | return super.visitField(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), signature, value); | ||
| 57 | } | ||
| 58 | |||
| 59 | @Override | ||
| 60 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | ||
| 61 | MethodDefEntry entry = new MethodDefEntry(obfClassEntry, name, new MethodDescriptor(desc), new AccessFlags(access)); | ||
| 62 | MethodDefEntry translatedEntry = translator.getTranslatedMethodDef(entry); | ||
| 63 | if (jarIndex.getBridgedMethod(entry) != null) { | ||
| 64 | translatedEntry.getAccess().setBridged(); | ||
| 65 | } | ||
| 66 | MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), name, desc, signature, exceptions); | ||
| 67 | return new TranslationMethodVisitor(translator, translatedEntry, api, mv); | ||
| 68 | } | ||
| 69 | |||
| 70 | @Override | ||
| 71 | public void visitOuterClass(String owner, String name, String desc) { | ||
| 72 | if (name != null) { | ||
| 73 | ClassEntry ownerEntry = translator.getTranslatedClass(entryPool.getClass(owner)); | ||
| 74 | ClassEntry entry = translator.getTranslatedClass(entryPool.getClass(name)); | ||
| 75 | String translatedDesc = translator.getTranslatedTypeDesc(new TypeDescriptor(desc)).toString(); | ||
| 76 | super.visitOuterClass(ownerEntry.getName(), entry.getName(), translatedDesc); | ||
| 77 | } else { | ||
| 78 | super.visitOuterClass(owner, name, desc); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | @Override | ||
| 83 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { | ||
| 84 | // TODO: Implement | ||
| 85 | return super.visitAnnotation(desc, visible); | ||
| 86 | } | ||
| 87 | |||
| 88 | @Override | ||
| 89 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { | ||
| 90 | // TODO: Implement | ||
| 91 | return super.visitTypeAnnotation(typeRef, typePath, desc, visible); | ||
| 92 | } | ||
| 93 | |||
| 94 | @Override | ||
| 95 | public void visitInnerClass(String name, String outerName, String innerName, int access) { | ||
| 96 | // If this is not an anonymous class | ||
| 97 | if (innerName != null && outerName != null) { | ||
| 98 | ClassDefEntry translatedEntry = translator.getTranslatedClassDef(new ClassDefEntry(innerName, new AccessFlags(access))); | ||
| 99 | ClassEntry outerEntry = translator.getTranslatedClass(entryPool.getClass(outerName)); | ||
| 100 | ClassEntry innerEntry = translator.getTranslatedClass(entryPool.getClass(innerName)); | ||
| 101 | super.visitInnerClass(translatedEntry.getName(), outerEntry.getName(), innerEntry.getName(), translatedEntry.getAccess().getFlags()); | ||
| 102 | } else { | ||
| 103 | super.visitInnerClass(name, outerName, innerName, access); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
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 @@ | |||
| 1 | package cuchaz.enigma.bytecode.translators; | ||
| 2 | |||
| 3 | import cuchaz.enigma.mapping.*; | ||
| 4 | import org.objectweb.asm.*; | ||
| 5 | |||
| 6 | import java.util.Locale; | ||
| 7 | |||
| 8 | public class TranslationMethodVisitor extends MethodVisitor { | ||
| 9 | private final MethodDefEntry methodEntry; | ||
| 10 | private final Translator translator; | ||
| 11 | |||
| 12 | public TranslationMethodVisitor(Translator translator, MethodDefEntry methodEntry, int api, MethodVisitor mv) { | ||
| 13 | super(api, mv); | ||
| 14 | this.translator = translator; | ||
| 15 | this.methodEntry = methodEntry; | ||
| 16 | } | ||
| 17 | |||
| 18 | @Override | ||
| 19 | public void visitFieldInsn(int opcode, String owner, String name, String desc) { | ||
| 20 | FieldEntry entry = new FieldEntry(new ClassEntry(owner), name, new TypeDescriptor(desc)); | ||
| 21 | FieldEntry translatedEntry = translator.getTranslatedField(entry); | ||
| 22 | if (translatedEntry != null) { | ||
| 23 | super.visitFieldInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString()); | ||
| 24 | } else { | ||
| 25 | super.visitFieldInsn(opcode, owner, name, desc); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | @Override | ||
| 30 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { | ||
| 31 | MethodEntry entry = new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc)); | ||
| 32 | MethodEntry translatedEntry = translator.getTranslatedMethod(entry); | ||
| 33 | if (translatedEntry != null) { | ||
| 34 | super.visitMethodInsn(opcode, translatedEntry.getClassName(), translatedEntry.getName(), translatedEntry.getDesc().toString(), itf); | ||
| 35 | } else { | ||
| 36 | super.visitMethodInsn(opcode, owner, name, desc, itf); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | @Override | ||
| 41 | public void visitAttribute(Attribute attr) { | ||
| 42 | // TODO: Implement | ||
| 43 | super.visitAttribute(attr); | ||
| 44 | } | ||
| 45 | |||
| 46 | @Override | ||
| 47 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { | ||
| 48 | // TODO: Implement | ||
| 49 | return super.visitAnnotation(desc, visible); | ||
| 50 | } | ||
| 51 | |||
| 52 | @Override | ||
| 53 | public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { | ||
| 54 | LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, index, name, new TypeDescriptor(desc)); | ||
| 55 | LocalVariableDefEntry translatedEntry = translator.getTranslatedVariableDef(entry); | ||
| 56 | String translatedName = translatedEntry.getName(); | ||
| 57 | // TODO: Better name inference | ||
| 58 | if (translatedName.equals(entry.getName())) { | ||
| 59 | TypeDescriptor argDesc = translatedEntry.getDesc(); | ||
| 60 | int nameIndex = translatedEntry.getNamedIndex() + 1; | ||
| 61 | String prefix = translatedEntry.getNamedIndex() < methodEntry.getDesc().getArgumentDescs().size() ? "a" : "v"; | ||
| 62 | StringBuilder nameBuilder = new StringBuilder(prefix); | ||
| 63 | // Unfortunately each of these have different name getters, so they have different code paths | ||
| 64 | if (argDesc.isPrimitive()) { | ||
| 65 | TypeDescriptor.Primitive argCls = argDesc.getPrimitive(); | ||
| 66 | nameBuilder.append(argCls.name()); | ||
| 67 | } else if (argDesc.isArray()) { | ||
| 68 | // List types would require this whole block again, so just go with aListx | ||
| 69 | nameBuilder.append(nameIndex); | ||
| 70 | } else if (argDesc.isType()) { | ||
| 71 | String typeName = argDesc.getOwnerEntry().getSimpleName().replace("$", ""); | ||
| 72 | typeName = typeName.substring(0, 1).toUpperCase(Locale.ROOT) + typeName.substring(1); | ||
| 73 | nameBuilder.append(typeName); | ||
| 74 | } | ||
| 75 | if (methodEntry.getDesc().getArgumentDescs().size() > 1) { | ||
| 76 | nameBuilder.append(nameIndex); | ||
| 77 | } | ||
| 78 | translatedName = nameBuilder.toString(); | ||
| 79 | } | ||
| 80 | super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), signature, start, end, index); | ||
| 81 | } | ||
| 82 | } | ||