summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/bytecode/InnerClassWriter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/bytecode/InnerClassWriter.java')
-rw-r--r--src/cuchaz/enigma/bytecode/InnerClassWriter.java132
1 files changed, 132 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java
new file mode 100644
index 0000000..bdb1b5d
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java
@@ -0,0 +1,132 @@
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 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12
13import java.util.Collection;
14import java.util.List;
15
16import com.google.common.collect.Lists;
17
18import javassist.CtClass;
19import javassist.bytecode.AccessFlag;
20import javassist.bytecode.ConstPool;
21import javassist.bytecode.EnclosingMethodAttribute;
22import javassist.bytecode.InnerClassesAttribute;
23import cuchaz.enigma.analysis.JarIndex;
24import cuchaz.enigma.mapping.BehaviorEntry;
25import cuchaz.enigma.mapping.ClassEntry;
26import cuchaz.enigma.mapping.EntryFactory;
27
28public class InnerClassWriter {
29
30 private JarIndex m_index;
31
32 public InnerClassWriter(JarIndex index) {
33 m_index = index;
34 }
35
36 public void write(CtClass c) {
37
38 // don't change anything if there's already an attribute there
39 InnerClassesAttribute oldAttr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag);
40 if (oldAttr != null) {
41 // bail!
42 return;
43 }
44
45 ClassEntry obfClassEntry = EntryFactory.getClassEntry(c);
46 List<ClassEntry> obfClassChain = m_index.getObfClassChain(obfClassEntry);
47
48 boolean isInnerClass = obfClassChain.size() > 1;
49 if (isInnerClass) {
50
51 // it's an inner class, rename it to the fully qualified name
52 c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName());
53
54 BehaviorEntry caller = m_index.getAnonymousClassCaller(obfClassEntry);
55 if (caller != null) {
56
57 // write the enclosing method attribute
58 if (caller.getName().equals("<clinit>")) {
59 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName()));
60 } else {
61 c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString()));
62 }
63 }
64 }
65
66 // does this class have any inner classes?
67 Collection<ClassEntry> obfInnerClassEntries = m_index.getInnerClasses(obfClassEntry);
68
69 if (isInnerClass || !obfInnerClassEntries.isEmpty()) {
70
71 // create an inner class attribute
72 InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool());
73 c.getClassFile().addAttribute(attr);
74
75 // write the ancestry, but not the outermost class
76 for (int i=1; i<obfClassChain.size(); i++) {
77 ClassEntry obfInnerClassEntry = obfClassChain.get(i);
78 writeInnerClass(attr, obfClassChain, obfInnerClassEntry);
79
80 // update references to use the fully qualified inner class name
81 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName());
82 }
83
84 // write the inner classes
85 for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) {
86
87 // extend the class chain
88 List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain);
89 extendedObfClassChain.add(obfInnerClassEntry);
90
91 writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry);
92
93 // update references to use the fully qualified inner class name
94 c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName());
95 }
96 }
97 }
98
99 private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) {
100
101 // get the new inner class name
102 ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain);
103 ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry();
104
105 // here's what the JVM spec says about the InnerClasses attribute
106 // append(inner, parent, 0 if anonymous else simple name, flags);
107
108 // update the attribute with this inner class
109 ConstPool constPool = attr.getConstPool();
110 int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName());
111 int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName());
112 int innerClassNameIndex = 0;
113 int accessFlags = AccessFlag.PUBLIC;
114 // TODO: need to figure out if we can put static or not
115 if (!m_index.isAnonymousClass(obfClassEntry)) {
116 innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName());
117 }
118
119 attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags);
120
121 /* DEBUG
122 System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)",
123 obfClassEntry,
124 attr.innerClass(attr.tableLength() - 1),
125 attr.outerClass(attr.tableLength() - 1),
126 attr.innerName(attr.tableLength() - 1),
127 Constants.NonePackage + "/" + obfInnerClassName,
128 obfClassEntry.getName()
129 ));
130 */
131 }
132}