Instruction.java revision 281b510a9c2b4ae914ab28b9a4f4d622e5861da6
1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2009 Ben Gruver 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29package org.jf.dexlib.Code; 30 31import org.jf.dexlib.*; 32import org.jf.dexlib.Util.NumberUtils; 33import org.jf.dexlib.Code.Format.Format; 34 35public abstract class Instruction { 36 private DexFile dexFile; 37 private Opcode opcode; 38 private IndexedItem referencedItem; 39 protected byte[] encodedInstruction; 40 41 public int getSize() { 42 return encodedInstruction.length; 43 } 44 45 public Opcode getOpcode() { 46 return opcode; 47 } 48 49 public IndexedItem getReferencedItem() { 50 return referencedItem; 51 } 52 53 protected void setReferencedItem(IndexedItem referencedItem) { 54 checkReferenceType(referencedItem, this.opcode); 55 this.referencedItem = referencedItem; 56 } 57 58 protected Instruction(DexFile dexFile, Opcode opcode, IndexedItem referencedItem) { 59 this.dexFile = dexFile; 60 this.opcode = opcode; 61 this.referencedItem = referencedItem; 62 63 checkFormat(opcode.format); 64 checkReferenceType(referencedItem, this.opcode); 65 } 66 67 protected void checkFormat(Format format) { 68 if (format != getFormat()) { 69 throw new RuntimeException(opcode.name + " does not use " + getFormat().toString()); 70 } 71 } 72 73 protected Instruction(DexFile dexFile, Opcode opcode, byte[] rest) { 74 this.dexFile = dexFile; 75 this.opcode = opcode; 76 77 if ((rest.length + 1) != opcode.format.size) { 78 throw new RuntimeException("Invalid instruction size. This opcode is " + 79 Integer.toString(rest.length + 1) + " bytes, but the opcode should be " + 80 Integer.toString(opcode.format.size) + " bytes."); 81 } 82 83 this.encodedInstruction = new byte[rest.length + 1]; 84 encodedInstruction[0] = opcode.value; 85 System.arraycopy(rest, 0, encodedInstruction, 1, rest.length); 86 87 if (opcode.referenceType != ReferenceType.none) { 88 int itemIndex = NumberUtils.decodeUnsignedShort(encodedInstruction[2], encodedInstruction[3]); 89 getReferencedItem(dexFile, opcode, itemIndex); 90 } 91 } 92 93 protected Instruction() { 94 //this should only be used to make a blank clone within cloneTo() 95 } 96 97 private void checkReferenceType(IndexedItem referencedItem, Opcode opcode) { 98 switch (opcode.referenceType) { 99 case field: 100 if (!(referencedItem instanceof FieldIdItem)) { 101 throw new RuntimeException(referencedItem.getClass().getSimpleName() + 102 " is the wrong item type for opcode " + opcode.name + ". Expecting FieldIdItem."); 103 } 104 return; 105 case method: 106 if (!(referencedItem instanceof MethodIdItem)) { 107 throw new RuntimeException(referencedItem.getClass().getSimpleName() + 108 " is the wrong item type for opcode " + opcode.name + ". Expecting MethodIdItem."); 109 } 110 return; 111 case type: 112 if (!(referencedItem instanceof TypeIdItem)) { 113 throw new RuntimeException(referencedItem.getClass().getSimpleName() + 114 " is the wrong item type for opcode " + opcode.name + ". Expecting TypeIdItem."); 115 } 116 return; 117 case string: 118 if (!(referencedItem instanceof StringIdItem)) { 119 throw new RuntimeException(referencedItem.getClass().getSimpleName() + 120 " is the wrong item type for opcode " + opcode.name + ". Expecting StringIdItem."); 121 } 122 return; 123 default: 124 if (referencedItem != null) { 125 throw new RuntimeException(referencedItem.getClass().getSimpleName() + 126 " is invalid for opcode " + opcode.name + ". This opcode does not reference an item"); 127 } 128 return; 129 } 130 } 131 132 private void getReferencedItem(DexFile dexFile, Opcode opcode, int itemIndex) { 133 switch (opcode.referenceType) { 134 case field: 135 referencedItem = dexFile.FieldIdsSection.getByIndex(itemIndex); 136 return; 137 case method: 138 referencedItem = dexFile.MethodIdsSection.getByIndex(itemIndex); 139 return; 140 case type: 141 referencedItem = dexFile.TypeIdsSection.getByIndex(itemIndex); 142 return; 143 case string: 144 referencedItem = dexFile.StringIdsSection.getByIndex(itemIndex); 145 return; 146 } 147 return; 148 } 149 150 public abstract Format getFormat(); 151 152 public static interface InstructionFactory { 153 public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] rest); 154 } 155 156 public Instruction cloneTo(DexFile dexFile) { 157 Instruction clone = makeClone(); 158 clone.encodedInstruction = encodedInstruction.clone(); 159 clone.dexFile = dexFile; 160 clone.opcode = opcode; 161 if (referencedItem != null) { 162 switch (opcode.referenceType) { 163 case string: 164 clone.referencedItem = dexFile.StringIdsSection.intern((StringIdItem)referencedItem); 165 break; 166 case type: 167 clone.referencedItem = dexFile.TypeIdsSection.intern((TypeIdItem)referencedItem); 168 break; 169 case field: 170 clone.referencedItem = dexFile.FieldIdsSection.intern((FieldIdItem)referencedItem); 171 break; 172 case method: 173 clone.referencedItem = dexFile.MethodIdsSection.intern((MethodIdItem)referencedItem); 174 break; 175 case none: 176 break; 177 } 178 } 179 180 return clone; 181 } 182 183 protected abstract Instruction makeClone(); 184 185// public void readFrom(Input in) { 186// int startPos = in.getCursor(); 187// 188// byte opByte = in.readByte(); 189// 190// if (opByte == 0x00) { 191// reference = null; 192// byte secondByte = in.readByte(); 193// 194// int count; 195// 196// 197// switch (secondByte) { 198// case 0x00: 199// /** nop */ 200// bytes = new byte[] { 0x00, 0x00 }; 201// return; 202// case 0x01: 203// /** packed switch */ 204// count = in.readShort(); 205// in.setCursor(startPos); 206// bytes = in.readBytes((count * 4) + 8); 207// return; 208// case 0x02: 209// /** sparse switch */ 210// count = in.readShort(); 211// in.setCursor(startPos); 212// bytes = in.readBytes((count * 8) + 4); 213// return; 214// case 0x03: 215// /** fill array data */ 216// int elementWidth = in.readShort(); 217// count = in.readInt(); 218// in.setCursor(startPos); 219// bytes = in.readBytes(((elementWidth * count + 1)/2 + 4) * 2); 220// return; 221// default: 222// throw new RuntimeException("Invalid 2nd byte for opcode 0x00"); 223// } 224// } 225// 226// this.opcode = Opcode.getOpcodeByValue(opByte); 227// 228// if (opcode.referenceType != ReferenceType.none) { 229// in.skipBytes(1); 230// int referenceIndex = in.readShort(); 231// 232// //handle const-string/jumbo as a special case 233// if (opByte == 0x1b) { 234// int hiWord = in.readShort(); 235// if (hiWord != 0) { 236// referenceIndex += (hiWord<<16); 237// } 238// } 239// 240// switch (opcode.referenceType) { 241// case string: 242// reference = dexFile.StringIdsSection.getByIndex(referenceIndex); 243// break; 244// case type: 245// reference = dexFile.TypeIdsSection.getByIndex(referenceIndex); 246// break; 247// case field: 248// reference = dexFile.FieldIdsSection.getByIndex(referenceIndex); 249// break; 250// case method: 251// reference = dexFile.MethodIdsSection.getByIndex(referenceIndex); 252// break; 253// } 254// } else { 255// reference = null; 256// } 257// 258// in.setCursor(startPos); 259// bytes = in.readBytes(opcode.numBytes); 260// } 261// 262// public void writeTo(AnnotatedOutput out) { 263// out.annotate(bytes.length, "instruction"); 264// if (needsAlign()) { 265// //the "special instructions" must be 4 byte aligned 266// out.alignTo(4); 267// out.write(bytes); 268// } else if (reference == null) { 269// out.write(bytes); 270// } else { 271// out.write(bytes,0,2); 272// //handle const-string/jumbo as a special case 273// if (bytes[0] == 0x1b) { 274// out.writeInt(reference.getIndex()); 275// } else { 276// int index = reference.getIndex(); 277// if (index > 0xFFFF) { 278// throw new RuntimeException("String index doesn't fit."); 279// } 280// out.writeShort(reference.getIndex()); 281// out.write(bytes, 4, bytes.length - 4); 282// } 283// } 284// } 285// 286// public void copyTo(DexFile dexFile, Instruction copy) { 287// copy.bytes = bytes; 288// copy.opcode = opcode; 289// 290// switch (opcode.referenceType) { 291// case string: 292// copy.reference = dexFile.StringIdsSection.intern(dexFile, (StringIdItem)reference); 293// break; 294// case type: 295// copy.reference = dexFile.TypeIdsSection.intern(dexFile, (TypeIdItem)reference); 296// break; 297// case field: 298// copy.reference = dexFile.FieldIdsSection.intern(dexFile, (FieldIdItem)reference); 299// break; 300// case method: 301// copy.reference = dexFile.MethodIdsSection.intern(dexFile, (MethodIdItem)reference); 302// break; 303// case none: 304// break; 305// } 306// } 307// 308// public int place(int offset) { 309// return offset + getSize(offset); 310// } 311// 312// public int getSize(int offset) { 313// if (this.needsAlign() && (offset % 4) != 0) { 314// return bytes.length + 2; 315// } else { 316// return bytes.length; 317// } 318// } 319// 320// private boolean needsAlign() { 321// //true if the opcode is one of the "special format" opcodes 322// return bytes[0] == 0 && bytes[1] > 0; 323// } 324} 325