| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.dx.io.instructions; |
| |
| import com.android.dex.DexException; |
| import com.android.dx.io.IndexType; |
| import com.android.dx.io.OpcodeInfo; |
| import com.android.dx.io.Opcodes; |
| import com.android.dx.util.Hex; |
| import java.io.EOFException; |
| |
| /** |
| * Representation of an instruction format, which knows how to decode into |
| * and encode from instances of {@link DecodedInstruction}. |
| */ |
| public enum InstructionCodec { |
| FORMAT_00X() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| return new ZeroRegisterDecodedInstruction( |
| this, opcodeUnit, 0, null, |
| 0, 0L); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write(insn.getOpcodeUnit()); |
| } |
| }, |
| |
| FORMAT_10X() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int literal = byte1(opcodeUnit); // should be zero |
| return new ZeroRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, literal); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write(insn.getOpcodeUnit()); |
| } |
| }, |
| |
| FORMAT_12X() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = nibble2(opcodeUnit); |
| int b = nibble3(opcodeUnit); |
| return new TwoRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, 0L, |
| a, b); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcodeUnit(), |
| makeByte(insn.getA(), insn.getB()))); |
| } |
| }, |
| |
| FORMAT_11N() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = nibble2(opcodeUnit); |
| int literal = (nibble3(opcodeUnit) << 28) >> 28; // sign-extend |
| return new OneRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, literal, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcodeUnit(), |
| makeByte(insn.getA(), insn.getLiteralNibble()))); |
| } |
| }, |
| |
| FORMAT_11X() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| return new OneRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, 0L, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write(codeUnit(insn.getOpcode(), insn.getA())); |
| } |
| }, |
| |
| FORMAT_10T() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int baseAddress = in.cursor() - 1; |
| int opcode = byte0(opcodeUnit); |
| int target = (byte) byte1(opcodeUnit); // sign-extend |
| return new ZeroRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| baseAddress + target, 0L); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| int relativeTarget = insn.getTargetByte(out.cursor()); |
| out.write(codeUnit(insn.getOpcode(), relativeTarget)); |
| } |
| }, |
| |
| FORMAT_20T() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int baseAddress = in.cursor() - 1; |
| int opcode = byte0(opcodeUnit); |
| int literal = byte1(opcodeUnit); // should be zero |
| int target = (short) in.read(); // sign-extend |
| return new ZeroRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| baseAddress + target, literal); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| short relativeTarget = insn.getTargetUnit(out.cursor()); |
| out.write(insn.getOpcodeUnit(), relativeTarget); |
| } |
| }, |
| |
| FORMAT_20BC() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| // Note: We use the literal field to hold the decoded AA value. |
| int opcode = byte0(opcodeUnit); |
| int literal = byte1(opcodeUnit); |
| int index = in.read(); |
| return new ZeroRegisterDecodedInstruction( |
| this, opcode, index, IndexType.VARIES, |
| 0, literal); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getLiteralByte()), |
| insn.getIndexUnit()); |
| } |
| }, |
| |
| FORMAT_22X() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| int b = in.read(); |
| return new TwoRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, 0L, |
| a, b); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getA()), |
| insn.getBUnit()); |
| } |
| }, |
| |
| FORMAT_21T() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int baseAddress = in.cursor() - 1; |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| int target = (short) in.read(); // sign-extend |
| return new OneRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| baseAddress + target, 0L, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| short relativeTarget = insn.getTargetUnit(out.cursor()); |
| out.write(codeUnit(insn.getOpcode(), insn.getA()), relativeTarget); |
| } |
| }, |
| |
| FORMAT_21S() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| int literal = (short) in.read(); // sign-extend |
| return new OneRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, literal, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getA()), |
| insn.getLiteralUnit()); |
| } |
| }, |
| |
| FORMAT_21H() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| long literal = (short) in.read(); // sign-extend |
| |
| /* |
| * Format 21h decodes differently depending on the opcode, |
| * because the "signed hat" might represent either a 32- |
| * or 64- bit value. |
| */ |
| literal <<= (opcode == Opcodes.CONST_HIGH16) ? 16 : 48; |
| |
| return new OneRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, literal, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| // See above. |
| int opcode = insn.getOpcode(); |
| int shift = (opcode == Opcodes.CONST_HIGH16) ? 16 : 48; |
| short literal = (short) (insn.getLiteral() >> shift); |
| |
| out.write(codeUnit(opcode, insn.getA()), literal); |
| } |
| }, |
| |
| FORMAT_21C() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| int index = in.read(); |
| IndexType indexType = OpcodeInfo.getIndexType(opcode); |
| return new OneRegisterDecodedInstruction( |
| this, opcode, index, indexType, |
| 0, 0L, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getA()), |
| insn.getIndexUnit()); |
| } |
| }, |
| |
| FORMAT_23X() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| int bc = in.read(); |
| int b = byte0(bc); |
| int c = byte1(bc); |
| return new ThreeRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, 0L, |
| a, b, c); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getA()), |
| codeUnit(insn.getB(), insn.getC())); |
| } |
| }, |
| |
| FORMAT_22B() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| int bc = in.read(); |
| int b = byte0(bc); |
| int literal = (byte) byte1(bc); // sign-extend |
| return new TwoRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, literal, |
| a, b); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getA()), |
| codeUnit(insn.getB(), |
| insn.getLiteralByte())); |
| } |
| }, |
| |
| FORMAT_22T() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int baseAddress = in.cursor() - 1; |
| int opcode = byte0(opcodeUnit); |
| int a = nibble2(opcodeUnit); |
| int b = nibble3(opcodeUnit); |
| int target = (short) in.read(); // sign-extend |
| return new TwoRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| baseAddress + target, 0L, |
| a, b); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| short relativeTarget = insn.getTargetUnit(out.cursor()); |
| out.write( |
| codeUnit(insn.getOpcode(), |
| makeByte(insn.getA(), insn.getB())), |
| relativeTarget); |
| } |
| }, |
| |
| FORMAT_22S() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = nibble2(opcodeUnit); |
| int b = nibble3(opcodeUnit); |
| int literal = (short) in.read(); // sign-extend |
| return new TwoRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, literal, |
| a, b); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcode(), |
| makeByte(insn.getA(), insn.getB())), |
| insn.getLiteralUnit()); |
| } |
| }, |
| |
| FORMAT_22C() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = nibble2(opcodeUnit); |
| int b = nibble3(opcodeUnit); |
| int index = in.read(); |
| IndexType indexType = OpcodeInfo.getIndexType(opcode); |
| return new TwoRegisterDecodedInstruction( |
| this, opcode, index, indexType, |
| 0, 0L, |
| a, b); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcode(), |
| makeByte(insn.getA(), insn.getB())), |
| insn.getIndexUnit()); |
| } |
| }, |
| |
| FORMAT_22CS() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = nibble2(opcodeUnit); |
| int b = nibble3(opcodeUnit); |
| int index = in.read(); |
| return new TwoRegisterDecodedInstruction( |
| this, opcode, index, IndexType.FIELD_OFFSET, |
| 0, 0L, |
| a, b); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write( |
| codeUnit(insn.getOpcode(), |
| makeByte(insn.getA(), insn.getB())), |
| insn.getIndexUnit()); |
| } |
| }, |
| |
| FORMAT_30T() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int baseAddress = in.cursor() - 1; |
| int opcode = byte0(opcodeUnit); |
| int literal = byte1(opcodeUnit); // should be zero |
| int target = in.readInt(); |
| return new ZeroRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| baseAddress + target, literal); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| int relativeTarget = insn.getTarget(out.cursor()); |
| out.write(insn.getOpcodeUnit(), |
| unit0(relativeTarget), unit1(relativeTarget)); |
| } |
| }, |
| |
| FORMAT_32X() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int literal = byte1(opcodeUnit); // should be zero |
| int a = in.read(); |
| int b = in.read(); |
| return new TwoRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, literal, |
| a, b); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| out.write(insn.getOpcodeUnit(), insn.getAUnit(), insn.getBUnit()); |
| } |
| }, |
| |
| FORMAT_31I() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| int literal = in.readInt(); |
| return new OneRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, literal, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| int literal = insn.getLiteralInt(); |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getA()), |
| unit0(literal), |
| unit1(literal)); |
| } |
| }, |
| |
| FORMAT_31T() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int baseAddress = in.cursor() - 1; |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| int target = baseAddress + in.readInt(); |
| |
| /* |
| * Switch instructions need to "forward" their addresses to their |
| * payload target instructions. |
| */ |
| switch (opcode) { |
| case Opcodes.PACKED_SWITCH: |
| case Opcodes.SPARSE_SWITCH: { |
| in.setBaseAddress(target, baseAddress); |
| break; |
| } |
| } |
| |
| return new OneRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| target, 0L, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| int relativeTarget = insn.getTarget(out.cursor()); |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getA()), |
| unit0(relativeTarget), unit1(relativeTarget)); |
| } |
| }, |
| |
| FORMAT_31C() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| int index = in.readInt(); |
| IndexType indexType = OpcodeInfo.getIndexType(opcode); |
| return new OneRegisterDecodedInstruction( |
| this, opcode, index, indexType, |
| 0, 0L, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| int index = insn.getIndex(); |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getA()), |
| unit0(index), |
| unit1(index)); |
| } |
| }, |
| |
| FORMAT_35C() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| return decodeRegisterList(this, opcodeUnit, in); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| encodeRegisterList(insn, out); |
| } |
| }, |
| |
| FORMAT_35MS() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| return decodeRegisterList(this, opcodeUnit, in); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| encodeRegisterList(insn, out); |
| } |
| }, |
| |
| FORMAT_35MI() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| return decodeRegisterList(this, opcodeUnit, in); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| encodeRegisterList(insn, out); |
| } |
| }, |
| |
| FORMAT_3RC() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| return decodeRegisterRange(this, opcodeUnit, in); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| encodeRegisterRange(insn, out); |
| } |
| }, |
| |
| FORMAT_3RMS() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| return decodeRegisterRange(this, opcodeUnit, in); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| encodeRegisterRange(insn, out); |
| } |
| }, |
| |
| FORMAT_3RMI() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| return decodeRegisterRange(this, opcodeUnit, in); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| encodeRegisterRange(insn, out); |
| } |
| }, |
| |
| FORMAT_51L() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int a = byte1(opcodeUnit); |
| long literal = in.readLong(); |
| return new OneRegisterDecodedInstruction( |
| this, opcode, 0, null, |
| 0, literal, |
| a); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| long literal = insn.getLiteral(); |
| out.write( |
| codeUnit(insn.getOpcode(), insn.getA()), |
| unit0(literal), |
| unit1(literal), |
| unit2(literal), |
| unit3(literal)); |
| } |
| }, |
| |
| FORMAT_PACKED_SWITCH_PAYLOAD() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int baseAddress = in.baseAddressForCursor() - 1; // already read opcode |
| int size = in.read(); |
| int firstKey = in.readInt(); |
| int[] targets = new int[size]; |
| |
| for (int i = 0; i < size; i++) { |
| targets[i] = baseAddress + in.readInt(); |
| } |
| |
| return new PackedSwitchPayloadDecodedInstruction( |
| this, opcodeUnit, firstKey, targets); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| PackedSwitchPayloadDecodedInstruction payload = |
| (PackedSwitchPayloadDecodedInstruction) insn; |
| int[] targets = payload.getTargets(); |
| int baseAddress = out.baseAddressForCursor(); |
| |
| out.write(payload.getOpcodeUnit()); |
| out.write(asUnsignedUnit(targets.length)); |
| out.writeInt(payload.getFirstKey()); |
| |
| for (int target : targets) { |
| out.writeInt(target - baseAddress); |
| } |
| } |
| }, |
| |
| FORMAT_SPARSE_SWITCH_PAYLOAD() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int baseAddress = in.baseAddressForCursor() - 1; // already read opcode |
| int size = in.read(); |
| int[] keys = new int[size]; |
| int[] targets = new int[size]; |
| |
| for (int i = 0; i < size; i++) { |
| keys[i] = in.readInt(); |
| } |
| |
| for (int i = 0; i < size; i++) { |
| targets[i] = baseAddress + in.readInt(); |
| } |
| |
| return new SparseSwitchPayloadDecodedInstruction( |
| this, opcodeUnit, keys, targets); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| SparseSwitchPayloadDecodedInstruction payload = |
| (SparseSwitchPayloadDecodedInstruction) insn; |
| int[] keys = payload.getKeys(); |
| int[] targets = payload.getTargets(); |
| int baseAddress = out.baseAddressForCursor(); |
| |
| out.write(payload.getOpcodeUnit()); |
| out.write(asUnsignedUnit(targets.length)); |
| |
| for (int key : keys) { |
| out.writeInt(key); |
| } |
| |
| for (int target : targets) { |
| out.writeInt(target - baseAddress); |
| } |
| } |
| }, |
| |
| FORMAT_FILL_ARRAY_DATA_PAYLOAD() { |
| @Override public DecodedInstruction decode(int opcodeUnit, |
| CodeInput in) throws EOFException { |
| int elementWidth = in.read(); |
| int size = in.readInt(); |
| |
| switch (elementWidth) { |
| case 1: { |
| byte[] array = new byte[size]; |
| boolean even = true; |
| for (int i = 0, value = 0; i < size; i++, even = !even) { |
| if (even) { |
| value = in.read(); |
| } |
| array[i] = (byte) (value & 0xff); |
| value >>= 8; |
| } |
| return new FillArrayDataPayloadDecodedInstruction( |
| this, opcodeUnit, array); |
| } |
| case 2: { |
| short[] array = new short[size]; |
| for (int i = 0; i < size; i++) { |
| array[i] = (short) in.read(); |
| } |
| return new FillArrayDataPayloadDecodedInstruction( |
| this, opcodeUnit, array); |
| } |
| case 4: { |
| int[] array = new int[size]; |
| for (int i = 0; i < size; i++) { |
| array[i] = in.readInt(); |
| } |
| return new FillArrayDataPayloadDecodedInstruction( |
| this, opcodeUnit, array); |
| } |
| case 8: { |
| long[] array = new long[size]; |
| for (int i = 0; i < size; i++) { |
| array[i] = in.readLong(); |
| } |
| return new FillArrayDataPayloadDecodedInstruction( |
| this, opcodeUnit, array); |
| } |
| } |
| |
| throw new DexException("bogus element_width: " |
| + Hex.u2(elementWidth)); |
| } |
| |
| @Override public void encode(DecodedInstruction insn, CodeOutput out) { |
| FillArrayDataPayloadDecodedInstruction payload = |
| (FillArrayDataPayloadDecodedInstruction) insn; |
| short elementWidth = payload.getElementWidthUnit(); |
| Object data = payload.getData(); |
| |
| out.write(payload.getOpcodeUnit()); |
| out.write(elementWidth); |
| out.writeInt(payload.getSize()); |
| |
| switch (elementWidth) { |
| case 1: out.write((byte[]) data); break; |
| case 2: out.write((short[]) data); break; |
| case 4: out.write((int[]) data); break; |
| case 8: out.write((long[]) data); break; |
| default: { |
| throw new DexException("bogus element_width: " |
| + Hex.u2(elementWidth)); |
| } |
| } |
| } |
| }; |
| |
| /** |
| * Decodes an instruction specified by the given opcode unit, reading |
| * any required additional code units from the given input source. |
| */ |
| public abstract DecodedInstruction decode(int opcodeUnit, CodeInput in) |
| throws EOFException; |
| |
| /** |
| * Encodes the given instruction. |
| */ |
| public abstract void encode(DecodedInstruction insn, CodeOutput out); |
| |
| /** |
| * Helper method that decodes any of the register-list formats. |
| */ |
| private static DecodedInstruction decodeRegisterList( |
| InstructionCodec format, int opcodeUnit, CodeInput in) |
| throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int e = nibble2(opcodeUnit); |
| int registerCount = nibble3(opcodeUnit); |
| int index = in.read(); |
| int abcd = in.read(); |
| int a = nibble0(abcd); |
| int b = nibble1(abcd); |
| int c = nibble2(abcd); |
| int d = nibble3(abcd); |
| IndexType indexType = OpcodeInfo.getIndexType(opcode); |
| |
| // TODO: Having to switch like this is less than ideal. |
| switch (registerCount) { |
| case 0: |
| return new ZeroRegisterDecodedInstruction( |
| format, opcode, index, indexType, |
| 0, 0L); |
| case 1: |
| return new OneRegisterDecodedInstruction( |
| format, opcode, index, indexType, |
| 0, 0L, |
| a); |
| case 2: |
| return new TwoRegisterDecodedInstruction( |
| format, opcode, index, indexType, |
| 0, 0L, |
| a, b); |
| case 3: |
| return new ThreeRegisterDecodedInstruction( |
| format, opcode, index, indexType, |
| 0, 0L, |
| a, b, c); |
| case 4: |
| return new FourRegisterDecodedInstruction( |
| format, opcode, index, indexType, |
| 0, 0L, |
| a, b, c, d); |
| case 5: |
| return new FiveRegisterDecodedInstruction( |
| format, opcode, index, indexType, |
| 0, 0L, |
| a, b, c, d, e); |
| } |
| |
| throw new DexException("bogus registerCount: " |
| + Hex.uNibble(registerCount)); |
| } |
| |
| /** |
| * Helper method that encodes any of the register-list formats. |
| */ |
| private static void encodeRegisterList(DecodedInstruction insn, |
| CodeOutput out) { |
| out.write(codeUnit(insn.getOpcode(), |
| makeByte(insn.getE(), insn.getRegisterCount())), |
| insn.getIndexUnit(), |
| codeUnit(insn.getA(), insn.getB(), insn.getC(), insn.getD())); |
| } |
| |
| /** |
| * Helper method that decodes any of the three-unit register-range formats. |
| */ |
| private static DecodedInstruction decodeRegisterRange( |
| InstructionCodec format, int opcodeUnit, CodeInput in) |
| throws EOFException { |
| int opcode = byte0(opcodeUnit); |
| int registerCount = byte1(opcodeUnit); |
| int index = in.read(); |
| int a = in.read(); |
| IndexType indexType = OpcodeInfo.getIndexType(opcode); |
| return new RegisterRangeDecodedInstruction( |
| format, opcode, index, indexType, |
| 0, 0L, |
| a, registerCount); |
| } |
| |
| /** |
| * Helper method that encodes any of the three-unit register-range formats. |
| */ |
| private static void encodeRegisterRange(DecodedInstruction insn, |
| CodeOutput out) { |
| out.write(codeUnit(insn.getOpcode(), insn.getRegisterCount()), |
| insn.getIndexUnit(), |
| insn.getAUnit()); |
| } |
| |
| private static short codeUnit(int lowByte, int highByte) { |
| if ((lowByte & ~0xff) != 0) { |
| throw new IllegalArgumentException("bogus lowByte"); |
| } |
| |
| if ((highByte & ~0xff) != 0) { |
| throw new IllegalArgumentException("bogus highByte"); |
| } |
| |
| return (short) (lowByte | (highByte << 8)); |
| } |
| |
| private static short codeUnit(int nibble0, int nibble1, int nibble2, |
| int nibble3) { |
| if ((nibble0 & ~0xf) != 0) { |
| throw new IllegalArgumentException("bogus nibble0"); |
| } |
| |
| if ((nibble1 & ~0xf) != 0) { |
| throw new IllegalArgumentException("bogus nibble1"); |
| } |
| |
| if ((nibble2 & ~0xf) != 0) { |
| throw new IllegalArgumentException("bogus nibble2"); |
| } |
| |
| if ((nibble3 & ~0xf) != 0) { |
| throw new IllegalArgumentException("bogus nibble3"); |
| } |
| |
| return (short) (nibble0 | (nibble1 << 4) |
| | (nibble2 << 8) | (nibble3 << 12)); |
| } |
| |
| private static int makeByte(int lowNibble, int highNibble) { |
| if ((lowNibble & ~0xf) != 0) { |
| throw new IllegalArgumentException("bogus lowNibble"); |
| } |
| |
| if ((highNibble & ~0xf) != 0) { |
| throw new IllegalArgumentException("bogus highNibble"); |
| } |
| |
| return lowNibble | (highNibble << 4); |
| } |
| |
| private static short asUnsignedUnit(int value) { |
| if ((value & ~0xffff) != 0) { |
| throw new IllegalArgumentException("bogus unsigned code unit"); |
| } |
| |
| return (short) value; |
| } |
| |
| private static short unit0(int value) { |
| return (short) value; |
| } |
| |
| private static short unit1(int value) { |
| return (short) (value >> 16); |
| } |
| |
| private static short unit0(long value) { |
| return (short) value; |
| } |
| |
| private static short unit1(long value) { |
| return (short) (value >> 16); |
| } |
| |
| private static short unit2(long value) { |
| return (short) (value >> 32); |
| } |
| |
| private static short unit3(long value) { |
| return (short) (value >> 48); |
| } |
| |
| private static int byte0(int value) { |
| return value & 0xff; |
| } |
| |
| private static int byte1(int value) { |
| return (value >> 8) & 0xff; |
| } |
| |
| private static int byte2(int value) { |
| return (value >> 16) & 0xff; |
| } |
| |
| private static int byte3(int value) { |
| return value >>> 24; |
| } |
| |
| private static int nibble0(int value) { |
| return value & 0xf; |
| } |
| |
| private static int nibble1(int value) { |
| return (value >> 4) & 0xf; |
| } |
| |
| private static int nibble2(int value) { |
| return (value >> 8) & 0xf; |
| } |
| |
| private static int nibble3(int value) { |
| return (value >> 12) & 0xf; |
| } |
| } |