summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/bytecode
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/bytecode')
-rw-r--r--src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java151
-rw-r--r--src/cuchaz/enigma/bytecode/BytecodeTools.java287
2 files changed, 0 insertions, 438 deletions
diff --git a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java
deleted file mode 100644
index fc2bac3..0000000
--- a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java
+++ /dev/null
@@ -1,151 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12
13import java.util.Iterator;
14
15import javassist.bytecode.BadBytecode;
16import javassist.bytecode.Bytecode;
17import javassist.bytecode.CodeAttribute;
18import javassist.bytecode.CodeIterator;
19import javassist.bytecode.Opcode;
20
21public class BytecodeIndexIterator implements Iterator<BytecodeIndexIterator.Index> {
22
23 public static class Index {
24
25 private CodeIterator m_iter;
26 private int m_pos;
27 private boolean m_isWide;
28
29 protected Index(CodeIterator iter, int pos, boolean isWide) {
30 m_iter = iter;
31 m_pos = pos;
32 m_isWide = isWide;
33 }
34
35 public int getIndex() {
36 if (m_isWide) {
37 return m_iter.s16bitAt(m_pos);
38 } else {
39 return m_iter.byteAt(m_pos);
40 }
41 }
42
43 public void setIndex(int val) throws BadBytecode {
44 if (m_isWide) {
45 m_iter.write16bit(val, m_pos);
46 } else {
47 if (val < 256) {
48 // we can write the byte
49 m_iter.writeByte(val, m_pos);
50 } else {
51 // we need to upgrade this instruction to LDC_W
52 assert (m_iter.byteAt(m_pos - 1) == Opcode.LDC);
53 m_iter.insertGap(m_pos - 1, 1);
54 m_iter.writeByte(Opcode.LDC_W, m_pos - 1);
55 m_iter.write16bit(val, m_pos);
56 m_isWide = true;
57
58 // move the iterator to the next opcode
59 m_iter.move(m_pos + 2);
60 }
61 }
62
63 // sanity check
64 assert (val == getIndex());
65 }
66
67 public boolean isValid(Bytecode bytecode) {
68 return getIndex() >= 0 && getIndex() < bytecode.getConstPool().getSize();
69 }
70 }
71
72 private Bytecode m_bytecode;
73 private CodeAttribute m_attribute;
74 private CodeIterator m_iter;
75 private Index m_next;
76
77 public BytecodeIndexIterator(Bytecode bytecode) throws BadBytecode {
78 m_bytecode = bytecode;
79 m_attribute = bytecode.toCodeAttribute();
80 m_iter = m_attribute.iterator();
81
82 m_next = getNext();
83 }
84
85 @Override
86 public boolean hasNext() {
87 return m_next != null;
88 }
89
90 @Override
91 public Index next() {
92 Index out = m_next;
93 try {
94 m_next = getNext();
95 } catch (BadBytecode ex) {
96 throw new Error(ex);
97 }
98 return out;
99 }
100
101 @Override
102 public void remove() {
103 throw new UnsupportedOperationException();
104 }
105
106 private Index getNext() throws BadBytecode {
107 while (m_iter.hasNext()) {
108 int pos = m_iter.next();
109 int opcode = m_iter.byteAt(pos);
110 switch (opcode) {
111
112 // for only these opcodes, the next two bytes are a const pool reference
113 case Opcode.ANEWARRAY:
114 case Opcode.CHECKCAST:
115 case Opcode.INSTANCEOF:
116 case Opcode.INVOKEDYNAMIC:
117 case Opcode.INVOKEINTERFACE:
118 case Opcode.INVOKESPECIAL:
119 case Opcode.INVOKESTATIC:
120 case Opcode.INVOKEVIRTUAL:
121 case Opcode.LDC_W:
122 case Opcode.LDC2_W:
123 case Opcode.MULTIANEWARRAY:
124 case Opcode.NEW:
125 case Opcode.PUTFIELD:
126 case Opcode.PUTSTATIC:
127 case Opcode.GETFIELD:
128 case Opcode.GETSTATIC:
129 return new Index(m_iter, pos + 1, true);
130
131 case Opcode.LDC:
132 return new Index(m_iter, pos + 1, false);
133 }
134 }
135
136 return null;
137 }
138
139 public Iterable<Index> indices() {
140 return new Iterable<Index>() {
141 @Override
142 public Iterator<Index> iterator() {
143 return BytecodeIndexIterator.this;
144 }
145 };
146 }
147
148 public void saveChangesToBytecode() {
149 BytecodeTools.setBytecode(m_bytecode, m_attribute.getCode());
150 }
151}
diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java
deleted file mode 100644
index 2e456f4..0000000
--- a/src/cuchaz/enigma/bytecode/BytecodeTools.java
+++ /dev/null
@@ -1,287 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12
13import java.io.ByteArrayInputStream;
14import java.io.ByteArrayOutputStream;
15import java.io.DataInputStream;
16import java.io.DataOutputStream;
17import java.io.IOException;
18import java.util.List;
19import java.util.Map;
20import java.util.Set;
21
22import javassist.CtBehavior;
23import javassist.bytecode.BadBytecode;
24import javassist.bytecode.Bytecode;
25import javassist.bytecode.CodeAttribute;
26import javassist.bytecode.ConstPool;
27import javassist.bytecode.ExceptionTable;
28
29import com.google.common.collect.Lists;
30import com.google.common.collect.Maps;
31import com.google.common.collect.Sets;
32
33import cuchaz.enigma.Util;
34import cuchaz.enigma.bytecode.BytecodeIndexIterator.Index;
35import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
36
37public class BytecodeTools {
38
39 public static byte[] writeBytecode(Bytecode bytecode) throws IOException {
40
41 ByteArrayOutputStream buf = new ByteArrayOutputStream();
42 DataOutputStream out = new DataOutputStream(buf);
43
44 try {
45 // write the constant pool
46 new ConstPoolEditor(bytecode.getConstPool()).writePool(out);
47
48 // write metadata
49 out.writeShort(bytecode.getMaxStack());
50 out.writeShort(bytecode.getMaxLocals());
51 out.writeShort(bytecode.getStackDepth());
52
53 // write the code
54 out.writeShort(bytecode.getSize());
55 out.write(bytecode.get());
56
57 // write the exception table
58 int numEntries = bytecode.getExceptionTable().size();
59 out.writeShort(numEntries);
60 for (int i = 0; i < numEntries; i++) {
61 out.writeShort(bytecode.getExceptionTable().startPc(i));
62 out.writeShort(bytecode.getExceptionTable().endPc(i));
63 out.writeShort(bytecode.getExceptionTable().handlerPc(i));
64 out.writeShort(bytecode.getExceptionTable().catchType(i));
65 }
66
67 out.close();
68 return buf.toByteArray();
69 } catch (Exception ex) {
70 Util.closeQuietly(out);
71 throw new Error(ex);
72 }
73 }
74
75 public static Bytecode readBytecode(byte[] bytes) throws IOException {
76
77 ByteArrayInputStream buf = new ByteArrayInputStream(bytes);
78 DataInputStream in = new DataInputStream(buf);
79
80 try {
81 // read the constant pool entries and update the class
82 ConstPool pool = ConstPoolEditor.readPool(in);
83
84 // read metadata
85 int maxStack = in.readShort();
86 int maxLocals = in.readShort();
87 int stackDepth = in.readShort();
88
89 Bytecode bytecode = new Bytecode(pool, maxStack, maxLocals);
90 bytecode.setStackDepth(stackDepth);
91
92 // read the code
93 int size = in.readShort();
94 byte[] code = new byte[size];
95 in.read(code);
96 setBytecode(bytecode, code);
97
98 // read the exception table
99 int numEntries = in.readShort();
100 for (int i = 0; i < numEntries; i++) {
101 bytecode.getExceptionTable().add(in.readShort(), in.readShort(), in.readShort(), in.readShort());
102 }
103
104 in.close();
105 return bytecode;
106 } catch (Exception ex) {
107 Util.closeQuietly(in);
108 throw new Error(ex);
109 }
110 }
111
112 public static Bytecode prepareMethodForBytecode(CtBehavior behavior, Bytecode bytecode) throws BadBytecode {
113
114 // update the destination class const pool
115 bytecode = copyBytecodeToConstPool(behavior.getMethodInfo().getConstPool(), bytecode);
116
117 // update method locals and stack
118 CodeAttribute attribute = behavior.getMethodInfo().getCodeAttribute();
119 if (bytecode.getMaxLocals() > attribute.getMaxLocals()) {
120 attribute.setMaxLocals(bytecode.getMaxLocals());
121 }
122 if (bytecode.getMaxStack() > attribute.getMaxStack()) {
123 attribute.setMaxStack(bytecode.getMaxStack());
124 }
125
126 return bytecode;
127 }
128
129 public static Bytecode copyBytecodeToConstPool(ConstPool dest, Bytecode bytecode) throws BadBytecode {
130
131 // get the entries this bytecode needs from the const pool
132 Set<Integer> indices = Sets.newTreeSet();
133 ConstPoolEditor editor = new ConstPoolEditor(bytecode.getConstPool());
134 BytecodeIndexIterator iterator = new BytecodeIndexIterator(bytecode);
135 for (Index index : iterator.indices()) {
136 assert (index.isValid(bytecode));
137 InfoType.gatherIndexTree(indices, editor, index.getIndex());
138 }
139
140 Map<Integer,Integer> indexMap = Maps.newTreeMap();
141
142 ConstPool src = bytecode.getConstPool();
143 ConstPoolEditor editorSrc = new ConstPoolEditor(src);
144 ConstPoolEditor editorDest = new ConstPoolEditor(dest);
145
146 // copy entries over in order of level so the index mapping is easier
147 for (InfoType type : InfoType.getSortedByLevel()) {
148 for (int index : indices) {
149 ConstInfoAccessor entry = editorSrc.getItem(index);
150
151 // skip entries that aren't this type
152 if (entry.getType() != type) {
153 continue;
154 }
155
156 // make sure the source entry is valid before we copy it
157 assert (type.subIndicesAreValid(entry, editorSrc));
158 assert (type.selfIndexIsValid(entry, editorSrc));
159
160 // make a copy of the entry so we can modify it safely
161 ConstInfoAccessor entryCopy = editorSrc.getItem(index).copy();
162 assert (type.subIndicesAreValid(entryCopy, editorSrc));
163 assert (type.selfIndexIsValid(entryCopy, editorSrc));
164
165 // remap the indices
166 type.remapIndices(indexMap, entryCopy);
167 assert (type.subIndicesAreValid(entryCopy, editorDest));
168
169 // put the copy in the destination pool
170 int newIndex = editorDest.addItem(entryCopy.getItem());
171 entryCopy.setIndex(newIndex);
172 assert (type.selfIndexIsValid(entryCopy, editorDest)) : type + ", self: " + entryCopy + " dest: " + editorDest.getItem(entryCopy.getIndex());
173
174 // make sure the source entry is unchanged
175 assert (type.subIndicesAreValid(entry, editorSrc));
176 assert (type.selfIndexIsValid(entry, editorSrc));
177
178 // add the index mapping so we can update the bytecode later
179 if (indexMap.containsKey(index)) {
180 throw new Error("Entry at index " + index + " already copied!");
181 }
182 indexMap.put(index, newIndex);
183 }
184 }
185
186 // make a new bytecode
187 Bytecode newBytecode = new Bytecode(dest, bytecode.getMaxStack(), bytecode.getMaxLocals());
188 bytecode.setStackDepth(bytecode.getStackDepth());
189 setBytecode(newBytecode, bytecode.get());
190 setExceptionTable(newBytecode, bytecode.getExceptionTable());
191
192 // apply the mappings to the bytecode
193 BytecodeIndexIterator iter = new BytecodeIndexIterator(newBytecode);
194 for (Index index : iter.indices()) {
195 int oldIndex = index.getIndex();
196 Integer newIndex = indexMap.get(oldIndex);
197 if (newIndex != null) {
198 // make sure this mapping makes sense
199 InfoType typeSrc = editorSrc.getItem(oldIndex).getType();
200 InfoType typeDest = editorDest.getItem(newIndex).getType();
201 assert (typeSrc == typeDest);
202
203 // apply the mapping
204 index.setIndex(newIndex);
205 }
206 }
207 iter.saveChangesToBytecode();
208
209 // make sure all the indices are valid
210 iter = new BytecodeIndexIterator(newBytecode);
211 for (Index index : iter.indices()) {
212 assert (index.isValid(newBytecode));
213 }
214
215 return newBytecode;
216 }
217
218 public static void setBytecode(Bytecode dest, byte[] src) {
219 if (src.length > dest.getSize()) {
220 dest.addGap(src.length - dest.getSize());
221 }
222 assert (dest.getSize() == src.length);
223 for (int i = 0; i < src.length; i++) {
224 dest.write(i, src[i]);
225 }
226 }
227
228 public static void setExceptionTable(Bytecode dest, ExceptionTable src) {
229
230 // clear the dest exception table
231 int size = dest.getExceptionTable().size();
232 for (int i = size - 1; i >= 0; i--) {
233 dest.getExceptionTable().remove(i);
234 }
235
236 // copy the exception table
237 for (int i = 0; i < src.size(); i++) {
238 dest.getExceptionTable().add(src.startPc(i), src.endPc(i), src.handlerPc(i), src.catchType(i));
239 }
240 }
241
242 public static List<String> getParameterTypes(String signature) {
243 List<String> types = Lists.newArrayList();
244 for (int i = 0; i < signature.length();) {
245 char c = signature.charAt(i);
246
247 // handle parens
248 if (c == '(') {
249 i++;
250 c = signature.charAt(i);
251 }
252 if (c == ')') {
253 break;
254 }
255
256 // find a type
257 String type = null;
258
259 int arrayDim = 0;
260 while (c == '[') {
261 // advance to array type
262 arrayDim++;
263 i++;
264 c = signature.charAt(i);
265 }
266
267 if (c == 'L') {
268 // read class type
269 int pos = signature.indexOf(';', i + 1);
270 String className = signature.substring(i + 1, pos);
271 type = "L" + className + ";";
272 i = pos + 1;
273 } else {
274 // read primitive type
275 type = signature.substring(i, i + 1);
276 i++;
277 }
278
279 // was it an array?
280 while (arrayDim-- > 0) {
281 type = "[" + type;
282 }
283 types.add(type);
284 }
285 return types;
286 }
287}