diff options
| author | 2015-01-13 23:25:04 -0500 | |
|---|---|---|
| committer | 2015-01-13 23:25:04 -0500 | |
| commit | 959cb5fd4f9586ec3bd265b452fe25fe1db82e3f (patch) | |
| tree | bdd8a2c52c2fe053ba3460614bde8542e5378dbe /src/cuchaz/enigma/bytecode/BytecodeTools.java | |
| parent | got rid of gradle in favor of ivy+ssjb (diff) | |
| download | enigma-fork-959cb5fd4f9586ec3bd265b452fe25fe1db82e3f.tar.gz enigma-fork-959cb5fd4f9586ec3bd265b452fe25fe1db82e3f.tar.xz enigma-fork-959cb5fd4f9586ec3bd265b452fe25fe1db82e3f.zip | |
source format change
don't hate me too much if you were planning a big merge. =P
Diffstat (limited to 'src/cuchaz/enigma/bytecode/BytecodeTools.java')
| -rw-r--r-- | src/cuchaz/enigma/bytecode/BytecodeTools.java | 269 |
1 files changed, 115 insertions, 154 deletions
diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java index 4407a90..2e456f4 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeTools.java +++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java | |||
| @@ -34,256 +34,222 @@ import cuchaz.enigma.Util; | |||
| 34 | import cuchaz.enigma.bytecode.BytecodeIndexIterator.Index; | 34 | import cuchaz.enigma.bytecode.BytecodeIndexIterator.Index; |
| 35 | import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; | 35 | import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; |
| 36 | 36 | ||
| 37 | public class BytecodeTools | 37 | public class BytecodeTools { |
| 38 | { | 38 | |
| 39 | public static byte[] writeBytecode( Bytecode bytecode ) | 39 | public static byte[] writeBytecode(Bytecode bytecode) throws IOException { |
| 40 | throws IOException | 40 | |
| 41 | { | ||
| 42 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); | 41 | ByteArrayOutputStream buf = new ByteArrayOutputStream(); |
| 43 | DataOutputStream out = new DataOutputStream( buf ); | 42 | DataOutputStream out = new DataOutputStream(buf); |
| 44 | try | 43 | |
| 45 | { | 44 | try { |
| 46 | // write the constant pool | 45 | // write the constant pool |
| 47 | new ConstPoolEditor( bytecode.getConstPool() ).writePool( out ); | 46 | new ConstPoolEditor(bytecode.getConstPool()).writePool(out); |
| 48 | 47 | ||
| 49 | // write metadata | 48 | // write metadata |
| 50 | out.writeShort( bytecode.getMaxStack() ); | 49 | out.writeShort(bytecode.getMaxStack()); |
| 51 | out.writeShort( bytecode.getMaxLocals() ); | 50 | out.writeShort(bytecode.getMaxLocals()); |
| 52 | out.writeShort( bytecode.getStackDepth() ); | 51 | out.writeShort(bytecode.getStackDepth()); |
| 53 | 52 | ||
| 54 | // write the code | 53 | // write the code |
| 55 | out.writeShort( bytecode.getSize() ); | 54 | out.writeShort(bytecode.getSize()); |
| 56 | out.write( bytecode.get() ); | 55 | out.write(bytecode.get()); |
| 57 | 56 | ||
| 58 | // write the exception table | 57 | // write the exception table |
| 59 | int numEntries = bytecode.getExceptionTable().size(); | 58 | int numEntries = bytecode.getExceptionTable().size(); |
| 60 | out.writeShort( numEntries ); | 59 | out.writeShort(numEntries); |
| 61 | for( int i=0; i<numEntries; i++ ) | 60 | for (int i = 0; i < numEntries; i++) { |
| 62 | { | 61 | out.writeShort(bytecode.getExceptionTable().startPc(i)); |
| 63 | out.writeShort( bytecode.getExceptionTable().startPc( i ) ); | 62 | out.writeShort(bytecode.getExceptionTable().endPc(i)); |
| 64 | out.writeShort( bytecode.getExceptionTable().endPc( i ) ); | 63 | out.writeShort(bytecode.getExceptionTable().handlerPc(i)); |
| 65 | out.writeShort( bytecode.getExceptionTable().handlerPc( i ) ); | 64 | out.writeShort(bytecode.getExceptionTable().catchType(i)); |
| 66 | out.writeShort( bytecode.getExceptionTable().catchType( i ) ); | ||
| 67 | } | 65 | } |
| 68 | 66 | ||
| 69 | out.close(); | 67 | out.close(); |
| 70 | return buf.toByteArray(); | 68 | return buf.toByteArray(); |
| 71 | } | 69 | } catch (Exception ex) { |
| 72 | catch( Exception ex ) | 70 | Util.closeQuietly(out); |
| 73 | { | 71 | throw new Error(ex); |
| 74 | Util.closeQuietly( out ); | ||
| 75 | throw new Error( ex ); | ||
| 76 | } | 72 | } |
| 77 | } | 73 | } |
| 78 | 74 | ||
| 79 | public static Bytecode readBytecode( byte[] bytes ) | 75 | public static Bytecode readBytecode(byte[] bytes) throws IOException { |
| 80 | throws IOException | 76 | |
| 81 | { | 77 | ByteArrayInputStream buf = new ByteArrayInputStream(bytes); |
| 82 | ByteArrayInputStream buf = new ByteArrayInputStream( bytes ); | 78 | DataInputStream in = new DataInputStream(buf); |
| 83 | DataInputStream in = new DataInputStream( buf ); | 79 | |
| 84 | try | 80 | try { |
| 85 | { | ||
| 86 | // read the constant pool entries and update the class | 81 | // read the constant pool entries and update the class |
| 87 | ConstPool pool = ConstPoolEditor.readPool( in ); | 82 | ConstPool pool = ConstPoolEditor.readPool(in); |
| 88 | 83 | ||
| 89 | // read metadata | 84 | // read metadata |
| 90 | int maxStack = in.readShort(); | 85 | int maxStack = in.readShort(); |
| 91 | int maxLocals = in.readShort(); | 86 | int maxLocals = in.readShort(); |
| 92 | int stackDepth = in.readShort(); | 87 | int stackDepth = in.readShort(); |
| 93 | 88 | ||
| 94 | Bytecode bytecode = new Bytecode( pool, maxStack, maxLocals ); | 89 | Bytecode bytecode = new Bytecode(pool, maxStack, maxLocals); |
| 95 | bytecode.setStackDepth( stackDepth ); | 90 | bytecode.setStackDepth(stackDepth); |
| 96 | 91 | ||
| 97 | // read the code | 92 | // read the code |
| 98 | int size = in.readShort(); | 93 | int size = in.readShort(); |
| 99 | byte[] code = new byte[size]; | 94 | byte[] code = new byte[size]; |
| 100 | in.read( code ); | 95 | in.read(code); |
| 101 | setBytecode( bytecode, code ); | 96 | setBytecode(bytecode, code); |
| 102 | 97 | ||
| 103 | // read the exception table | 98 | // read the exception table |
| 104 | int numEntries = in.readShort(); | 99 | int numEntries = in.readShort(); |
| 105 | for( int i=0; i<numEntries; i++ ) | 100 | for (int i = 0; i < numEntries; i++) { |
| 106 | { | 101 | bytecode.getExceptionTable().add(in.readShort(), in.readShort(), in.readShort(), in.readShort()); |
| 107 | bytecode.getExceptionTable().add( in.readShort(), in.readShort(), in.readShort(), in.readShort() ); | ||
| 108 | } | 102 | } |
| 109 | 103 | ||
| 110 | in.close(); | 104 | in.close(); |
| 111 | return bytecode; | 105 | return bytecode; |
| 112 | } | 106 | } catch (Exception ex) { |
| 113 | catch( Exception ex ) | 107 | Util.closeQuietly(in); |
| 114 | { | 108 | throw new Error(ex); |
| 115 | Util.closeQuietly( in ); | ||
| 116 | throw new Error( ex ); | ||
| 117 | } | 109 | } |
| 118 | } | 110 | } |
| 119 | 111 | ||
| 120 | public static Bytecode prepareMethodForBytecode( CtBehavior behavior, Bytecode bytecode ) | 112 | public static Bytecode prepareMethodForBytecode(CtBehavior behavior, Bytecode bytecode) throws BadBytecode { |
| 121 | throws BadBytecode | 113 | |
| 122 | { | ||
| 123 | // update the destination class const pool | 114 | // update the destination class const pool |
| 124 | bytecode = copyBytecodeToConstPool( behavior.getMethodInfo().getConstPool(), bytecode ); | 115 | bytecode = copyBytecodeToConstPool(behavior.getMethodInfo().getConstPool(), bytecode); |
| 125 | 116 | ||
| 126 | // update method locals and stack | 117 | // update method locals and stack |
| 127 | CodeAttribute attribute = behavior.getMethodInfo().getCodeAttribute(); | 118 | CodeAttribute attribute = behavior.getMethodInfo().getCodeAttribute(); |
| 128 | if( bytecode.getMaxLocals() > attribute.getMaxLocals() ) | 119 | if (bytecode.getMaxLocals() > attribute.getMaxLocals()) { |
| 129 | { | 120 | attribute.setMaxLocals(bytecode.getMaxLocals()); |
| 130 | attribute.setMaxLocals( bytecode.getMaxLocals() ); | ||
| 131 | } | 121 | } |
| 132 | if( bytecode.getMaxStack() > attribute.getMaxStack() ) | 122 | if (bytecode.getMaxStack() > attribute.getMaxStack()) { |
| 133 | { | 123 | attribute.setMaxStack(bytecode.getMaxStack()); |
| 134 | attribute.setMaxStack( bytecode.getMaxStack() ); | ||
| 135 | } | 124 | } |
| 136 | 125 | ||
| 137 | return bytecode; | 126 | return bytecode; |
| 138 | } | 127 | } |
| 139 | 128 | ||
| 140 | public static Bytecode copyBytecodeToConstPool( ConstPool dest, Bytecode bytecode ) | 129 | public static Bytecode copyBytecodeToConstPool(ConstPool dest, Bytecode bytecode) throws BadBytecode { |
| 141 | throws BadBytecode | 130 | |
| 142 | { | ||
| 143 | // get the entries this bytecode needs from the const pool | 131 | // get the entries this bytecode needs from the const pool |
| 144 | Set<Integer> indices = Sets.newTreeSet(); | 132 | Set<Integer> indices = Sets.newTreeSet(); |
| 145 | ConstPoolEditor editor = new ConstPoolEditor( bytecode.getConstPool() ); | 133 | ConstPoolEditor editor = new ConstPoolEditor(bytecode.getConstPool()); |
| 146 | BytecodeIndexIterator iterator = new BytecodeIndexIterator( bytecode ); | 134 | BytecodeIndexIterator iterator = new BytecodeIndexIterator(bytecode); |
| 147 | for( Index index : iterator.indices() ) | 135 | for (Index index : iterator.indices()) { |
| 148 | { | 136 | assert (index.isValid(bytecode)); |
| 149 | assert( index.isValid( bytecode ) ); | 137 | InfoType.gatherIndexTree(indices, editor, index.getIndex()); |
| 150 | InfoType.gatherIndexTree( indices, editor, index.getIndex() ); | ||
| 151 | } | 138 | } |
| 152 | 139 | ||
| 153 | Map<Integer,Integer> indexMap = Maps.newTreeMap(); | 140 | Map<Integer,Integer> indexMap = Maps.newTreeMap(); |
| 154 | 141 | ||
| 155 | ConstPool src = bytecode.getConstPool(); | 142 | ConstPool src = bytecode.getConstPool(); |
| 156 | ConstPoolEditor editorSrc = new ConstPoolEditor( src ); | 143 | ConstPoolEditor editorSrc = new ConstPoolEditor(src); |
| 157 | ConstPoolEditor editorDest = new ConstPoolEditor( dest ); | 144 | ConstPoolEditor editorDest = new ConstPoolEditor(dest); |
| 158 | 145 | ||
| 159 | // copy entries over in order of level so the index mapping is easier | 146 | // copy entries over in order of level so the index mapping is easier |
| 160 | for( InfoType type : InfoType.getSortedByLevel() ) | 147 | for (InfoType type : InfoType.getSortedByLevel()) { |
| 161 | { | 148 | for (int index : indices) { |
| 162 | for( int index : indices ) | 149 | ConstInfoAccessor entry = editorSrc.getItem(index); |
| 163 | { | ||
| 164 | ConstInfoAccessor entry = editorSrc.getItem( index ); | ||
| 165 | 150 | ||
| 166 | // skip entries that aren't this type | 151 | // skip entries that aren't this type |
| 167 | if( entry.getType() != type ) | 152 | if (entry.getType() != type) { |
| 168 | { | ||
| 169 | continue; | 153 | continue; |
| 170 | } | 154 | } |
| 171 | 155 | ||
| 172 | // make sure the source entry is valid before we copy it | 156 | // make sure the source entry is valid before we copy it |
| 173 | assert( type.subIndicesAreValid( entry, editorSrc ) ); | 157 | assert (type.subIndicesAreValid(entry, editorSrc)); |
| 174 | assert( type.selfIndexIsValid( entry, editorSrc ) ); | 158 | assert (type.selfIndexIsValid(entry, editorSrc)); |
| 175 | 159 | ||
| 176 | // make a copy of the entry so we can modify it safely | 160 | // make a copy of the entry so we can modify it safely |
| 177 | ConstInfoAccessor entryCopy = editorSrc.getItem( index ).copy(); | 161 | ConstInfoAccessor entryCopy = editorSrc.getItem(index).copy(); |
| 178 | assert( type.subIndicesAreValid( entryCopy, editorSrc ) ); | 162 | assert (type.subIndicesAreValid(entryCopy, editorSrc)); |
| 179 | assert( type.selfIndexIsValid( entryCopy, editorSrc ) ); | 163 | assert (type.selfIndexIsValid(entryCopy, editorSrc)); |
| 180 | 164 | ||
| 181 | // remap the indices | 165 | // remap the indices |
| 182 | type.remapIndices( indexMap, entryCopy ); | 166 | type.remapIndices(indexMap, entryCopy); |
| 183 | assert( type.subIndicesAreValid( entryCopy, editorDest ) ); | 167 | assert (type.subIndicesAreValid(entryCopy, editorDest)); |
| 184 | 168 | ||
| 185 | // put the copy in the destination pool | 169 | // put the copy in the destination pool |
| 186 | int newIndex = editorDest.addItem( entryCopy.getItem() ); | 170 | int newIndex = editorDest.addItem(entryCopy.getItem()); |
| 187 | entryCopy.setIndex( newIndex ); | 171 | entryCopy.setIndex(newIndex); |
| 188 | assert( type.selfIndexIsValid( entryCopy, editorDest ) ) : type + ", self: " + entryCopy + " dest: " + editorDest.getItem( entryCopy.getIndex() ); | 172 | assert (type.selfIndexIsValid(entryCopy, editorDest)) : type + ", self: " + entryCopy + " dest: " + editorDest.getItem(entryCopy.getIndex()); |
| 189 | 173 | ||
| 190 | // make sure the source entry is unchanged | 174 | // make sure the source entry is unchanged |
| 191 | assert( type.subIndicesAreValid( entry, editorSrc ) ); | 175 | assert (type.subIndicesAreValid(entry, editorSrc)); |
| 192 | assert( type.selfIndexIsValid( entry, editorSrc ) ); | 176 | assert (type.selfIndexIsValid(entry, editorSrc)); |
| 193 | 177 | ||
| 194 | // add the index mapping so we can update the bytecode later | 178 | // add the index mapping so we can update the bytecode later |
| 195 | if( indexMap.containsKey( index ) ) | 179 | if (indexMap.containsKey(index)) { |
| 196 | { | 180 | throw new Error("Entry at index " + index + " already copied!"); |
| 197 | throw new Error( "Entry at index " + index + " already copied!" ); | ||
| 198 | } | 181 | } |
| 199 | indexMap.put( index, newIndex ); | 182 | indexMap.put(index, newIndex); |
| 200 | } | 183 | } |
| 201 | } | 184 | } |
| 202 | 185 | ||
| 203 | // make a new bytecode | 186 | // make a new bytecode |
| 204 | Bytecode newBytecode = new Bytecode( dest, bytecode.getMaxStack(), bytecode.getMaxLocals() ); | 187 | Bytecode newBytecode = new Bytecode(dest, bytecode.getMaxStack(), bytecode.getMaxLocals()); |
| 205 | bytecode.setStackDepth( bytecode.getStackDepth() ); | 188 | bytecode.setStackDepth(bytecode.getStackDepth()); |
| 206 | setBytecode( newBytecode, bytecode.get() ); | 189 | setBytecode(newBytecode, bytecode.get()); |
| 207 | setExceptionTable( newBytecode, bytecode.getExceptionTable() ); | 190 | setExceptionTable(newBytecode, bytecode.getExceptionTable()); |
| 208 | 191 | ||
| 209 | // apply the mappings to the bytecode | 192 | // apply the mappings to the bytecode |
| 210 | BytecodeIndexIterator iter = new BytecodeIndexIterator( newBytecode ); | 193 | BytecodeIndexIterator iter = new BytecodeIndexIterator(newBytecode); |
| 211 | for( Index index : iter.indices() ) | 194 | for (Index index : iter.indices()) { |
| 212 | { | ||
| 213 | int oldIndex = index.getIndex(); | 195 | int oldIndex = index.getIndex(); |
| 214 | Integer newIndex = indexMap.get( oldIndex ); | 196 | Integer newIndex = indexMap.get(oldIndex); |
| 215 | if( newIndex != null ) | 197 | if (newIndex != null) { |
| 216 | { | ||
| 217 | // make sure this mapping makes sense | 198 | // make sure this mapping makes sense |
| 218 | InfoType typeSrc = editorSrc.getItem( oldIndex ).getType(); | 199 | InfoType typeSrc = editorSrc.getItem(oldIndex).getType(); |
| 219 | InfoType typeDest = editorDest.getItem( newIndex ).getType(); | 200 | InfoType typeDest = editorDest.getItem(newIndex).getType(); |
| 220 | assert( typeSrc == typeDest ); | 201 | assert (typeSrc == typeDest); |
| 221 | 202 | ||
| 222 | // apply the mapping | 203 | // apply the mapping |
| 223 | index.setIndex( newIndex ); | 204 | index.setIndex(newIndex); |
| 224 | } | 205 | } |
| 225 | } | 206 | } |
| 226 | iter.saveChangesToBytecode(); | 207 | iter.saveChangesToBytecode(); |
| 227 | 208 | ||
| 228 | // make sure all the indices are valid | 209 | // make sure all the indices are valid |
| 229 | iter = new BytecodeIndexIterator( newBytecode ); | 210 | iter = new BytecodeIndexIterator(newBytecode); |
| 230 | for( Index index : iter.indices() ) | 211 | for (Index index : iter.indices()) { |
| 231 | { | 212 | assert (index.isValid(newBytecode)); |
| 232 | assert( index.isValid( newBytecode ) ); | ||
| 233 | } | 213 | } |
| 234 | 214 | ||
| 235 | return newBytecode; | 215 | return newBytecode; |
| 236 | } | 216 | } |
| 237 | 217 | ||
| 238 | public static void setBytecode( Bytecode dest, byte[] src ) | 218 | public static void setBytecode(Bytecode dest, byte[] src) { |
| 239 | { | 219 | if (src.length > dest.getSize()) { |
| 240 | if( src.length > dest.getSize() ) | 220 | dest.addGap(src.length - dest.getSize()); |
| 241 | { | ||
| 242 | dest.addGap( src.length - dest.getSize() ); | ||
| 243 | } | 221 | } |
| 244 | assert( dest.getSize() == src.length ); | 222 | assert (dest.getSize() == src.length); |
| 245 | for( int i=0; i<src.length; i++ ) | 223 | for (int i = 0; i < src.length; i++) { |
| 246 | { | 224 | dest.write(i, src[i]); |
| 247 | dest.write( i, src[i] ); | ||
| 248 | } | 225 | } |
| 249 | } | 226 | } |
| 250 | 227 | ||
| 251 | public static void setExceptionTable( Bytecode dest, ExceptionTable src ) | 228 | public static void setExceptionTable(Bytecode dest, ExceptionTable src) { |
| 252 | { | 229 | |
| 253 | // clear the dest exception table | 230 | // clear the dest exception table |
| 254 | int size = dest.getExceptionTable().size(); | 231 | int size = dest.getExceptionTable().size(); |
| 255 | for( int i=size-1; i>=0; i-- ) | 232 | for (int i = size - 1; i >= 0; i--) { |
| 256 | { | 233 | dest.getExceptionTable().remove(i); |
| 257 | dest.getExceptionTable().remove( i ); | ||
| 258 | } | 234 | } |
| 259 | 235 | ||
| 260 | // copy the exception table | 236 | // copy the exception table |
| 261 | for( int i=0; i<src.size(); i++ ) | 237 | for (int i = 0; i < src.size(); i++) { |
| 262 | { | 238 | dest.getExceptionTable().add(src.startPc(i), src.endPc(i), src.handlerPc(i), src.catchType(i)); |
| 263 | dest.getExceptionTable().add( | ||
| 264 | src.startPc( i ), | ||
| 265 | src.endPc( i ), | ||
| 266 | src.handlerPc( i ), | ||
| 267 | src.catchType( i ) | ||
| 268 | ); | ||
| 269 | } | 239 | } |
| 270 | } | 240 | } |
| 271 | 241 | ||
| 272 | public static List<String> getParameterTypes( String signature ) | 242 | public static List<String> getParameterTypes(String signature) { |
| 273 | { | ||
| 274 | List<String> types = Lists.newArrayList(); | 243 | List<String> types = Lists.newArrayList(); |
| 275 | for( int i=0; i<signature.length(); ) | 244 | for (int i = 0; i < signature.length();) { |
| 276 | { | 245 | char c = signature.charAt(i); |
| 277 | char c = signature.charAt( i ); | ||
| 278 | 246 | ||
| 279 | // handle parens | 247 | // handle parens |
| 280 | if( c == '(' ) | 248 | if (c == '(') { |
| 281 | { | ||
| 282 | i++; | 249 | i++; |
| 283 | c = signature.charAt( i ); | 250 | c = signature.charAt(i); |
| 284 | } | 251 | } |
| 285 | if( c == ')' ) | 252 | if (c == ')') { |
| 286 | { | ||
| 287 | break; | 253 | break; |
| 288 | } | 254 | } |
| 289 | 255 | ||
| @@ -291,35 +257,30 @@ public class BytecodeTools | |||
| 291 | String type = null; | 257 | String type = null; |
| 292 | 258 | ||
| 293 | int arrayDim = 0; | 259 | int arrayDim = 0; |
| 294 | while( c == '[' ) | 260 | while (c == '[') { |
| 295 | { | ||
| 296 | // advance to array type | 261 | // advance to array type |
| 297 | arrayDim++; | 262 | arrayDim++; |
| 298 | i++; | 263 | i++; |
| 299 | c = signature.charAt( i ); | 264 | c = signature.charAt(i); |
| 300 | } | 265 | } |
| 301 | 266 | ||
| 302 | if( c == 'L' ) | 267 | if (c == 'L') { |
| 303 | { | ||
| 304 | // read class type | 268 | // read class type |
| 305 | int pos = signature.indexOf( ';', i + 1 ); | 269 | int pos = signature.indexOf(';', i + 1); |
| 306 | String className = signature.substring( i + 1, pos ); | 270 | String className = signature.substring(i + 1, pos); |
| 307 | type = "L" + className + ";"; | 271 | type = "L" + className + ";"; |
| 308 | i = pos + 1; | 272 | i = pos + 1; |
| 309 | } | 273 | } else { |
| 310 | else | ||
| 311 | { | ||
| 312 | // read primitive type | 274 | // read primitive type |
| 313 | type = signature.substring( i, i + 1 ); | 275 | type = signature.substring(i, i + 1); |
| 314 | i++; | 276 | i++; |
| 315 | } | 277 | } |
| 316 | 278 | ||
| 317 | // was it an array? | 279 | // was it an array? |
| 318 | while( arrayDim-- > 0 ) | 280 | while (arrayDim-- > 0) { |
| 319 | { | ||
| 320 | type = "[" + type; | 281 | type = "[" + type; |
| 321 | } | 282 | } |
| 322 | types.add( type ); | 283 | types.add(type); |
| 323 | } | 284 | } |
| 324 | return types; | 285 | return types; |
| 325 | } | 286 | } |