| /* |
| * Copyright 2013, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| package org.jf.dexlib2.writer; |
| |
| import com.google.common.collect.Ordering; |
| import com.google.common.primitives.Ints; |
| import org.jf.dexlib2.Opcode; |
| import org.jf.dexlib2.Opcodes; |
| import org.jf.dexlib2.ReferenceType; |
| import org.jf.dexlib2.iface.instruction.DualReferenceInstruction; |
| import org.jf.dexlib2.iface.instruction.ReferenceInstruction; |
| import org.jf.dexlib2.iface.instruction.SwitchElement; |
| import org.jf.dexlib2.iface.instruction.formats.*; |
| import org.jf.dexlib2.iface.reference.FieldReference; |
| import org.jf.dexlib2.iface.reference.MethodProtoReference; |
| import org.jf.dexlib2.iface.reference.MethodReference; |
| import org.jf.dexlib2.iface.reference.Reference; |
| import org.jf.dexlib2.iface.reference.StringReference; |
| import org.jf.dexlib2.iface.reference.TypeReference; |
| import org.jf.util.ExceptionWithContext; |
| |
| import javax.annotation.Nonnull; |
| import java.io.IOException; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| public class InstructionWriter<StringRef extends StringReference, TypeRef extends TypeReference, |
| FieldRefKey extends FieldReference, MethodRefKey extends MethodReference, |
| ProtoRefKey extends MethodProtoReference> { |
| @Nonnull private final Opcodes opcodes; |
| @Nonnull private final DexDataWriter writer; |
| @Nonnull private final StringSection<?, StringRef> stringSection; |
| @Nonnull private final TypeSection<?, ?, TypeRef> typeSection; |
| @Nonnull private final FieldSection<?, ?, FieldRefKey, ?> fieldSection; |
| @Nonnull private final MethodSection<?, ?, ?, MethodRefKey, ?> methodSection; |
| @Nonnull private final ProtoSection<?, ?, ProtoRefKey, ?> protoSection; |
| |
| @Nonnull static <StringRef extends StringReference, TypeRef extends TypeReference, FieldRefKey extends FieldReference, |
| MethodRefKey extends MethodReference, ProtoRefKey extends MethodProtoReference> |
| InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey, ProtoRefKey> |
| makeInstructionWriter( |
| @Nonnull Opcodes opcodes, |
| @Nonnull DexDataWriter writer, |
| @Nonnull StringSection<?, StringRef> stringSection, |
| @Nonnull TypeSection<?, ?, TypeRef> typeSection, |
| @Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection, |
| @Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection, |
| @Nonnull ProtoSection<?, ?, ProtoRefKey, ?> protoSection) { |
| return new InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey, ProtoRefKey>( |
| opcodes, writer, stringSection, typeSection, fieldSection, methodSection, protoSection); |
| } |
| |
| InstructionWriter(@Nonnull Opcodes opcodes, |
| @Nonnull DexDataWriter writer, |
| @Nonnull StringSection<?, StringRef> stringSection, |
| @Nonnull TypeSection<?, ?, TypeRef> typeSection, |
| @Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection, |
| @Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection, |
| @Nonnull ProtoSection<?, ?, ProtoRefKey, ?> protoSection) { |
| this.opcodes = opcodes; |
| this.writer = writer; |
| this.stringSection = stringSection; |
| this.typeSection = typeSection; |
| this.fieldSection = fieldSection; |
| this.methodSection = methodSection; |
| this.protoSection = protoSection; |
| } |
| |
| private short getOpcodeValue(Opcode opcode) { |
| Short value = opcodes.getOpcodeValue(opcode); |
| if (value == null) { |
| throw new ExceptionWithContext("Instruction %s is invalid for api %d", opcode.name, opcodes.api); |
| } |
| return value; |
| } |
| |
| public void write(@Nonnull Instruction10t instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getCodeOffset()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction10x instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(0); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction11n instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(packNibbles(instruction.getRegisterA(), instruction.getNarrowLiteral())); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction11x instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction12x instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction20bc instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getVerificationError()); |
| writer.writeUshort(getReferenceIndex(instruction)); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction20t instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(0); |
| writer.writeShort(instruction.getCodeOffset()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction21c instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeUshort(getReferenceIndex(instruction)); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction21ih instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeShort(instruction.getHatLiteral()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction21lh instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeShort(instruction.getHatLiteral()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction21s instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeShort(instruction.getNarrowLiteral()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction21t instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeShort(instruction.getCodeOffset()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction22b instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.write(instruction.getRegisterB()); |
| writer.write(instruction.getNarrowLiteral()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction22c instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); |
| writer.writeUshort(getReferenceIndex(instruction)); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction22s instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); |
| writer.writeShort(instruction.getNarrowLiteral()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction22t instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); |
| writer.writeShort(instruction.getCodeOffset()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction22x instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeUshort(instruction.getRegisterB()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction23x instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.write(instruction.getRegisterB()); |
| writer.write(instruction.getRegisterC()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction30t instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(0); |
| writer.writeInt(instruction.getCodeOffset()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction31c instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeInt(getReferenceIndex(instruction)); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction31i instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeInt(instruction.getNarrowLiteral()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction31t instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeInt(instruction.getCodeOffset()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction32x instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(0); |
| writer.writeUshort(instruction.getRegisterA()); |
| writer.writeUshort(instruction.getRegisterB()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction35c instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount())); |
| writer.writeUshort(getReferenceIndex(instruction)); |
| writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD())); |
| writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF())); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction3rc instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterCount()); |
| writer.writeUshort(getReferenceIndex(instruction)); |
| writer.writeUshort(instruction.getStartRegister()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction45cc instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount())); |
| writer.writeUshort(getReferenceIndex(instruction)); |
| writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD())); |
| writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF())); |
| writer.writeUshort(getReference2Index(instruction)); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction4rcc instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterCount()); |
| writer.writeUshort(getReferenceIndex(instruction)); |
| writer.writeUshort(instruction.getStartRegister()); |
| writer.writeUshort(getReference2Index(instruction)); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull Instruction51l instruction) { |
| try { |
| writer.write(getOpcodeValue(instruction.getOpcode())); |
| writer.write(instruction.getRegisterA()); |
| writer.writeLong(instruction.getWideLiteral()); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull ArrayPayload instruction) { |
| try { |
| writer.writeUshort(getOpcodeValue(instruction.getOpcode())); |
| writer.writeUshort(instruction.getElementWidth()); |
| List<Number> elements = instruction.getArrayElements(); |
| writer.writeInt(elements.size()); |
| switch (instruction.getElementWidth()) { |
| case 1: |
| for (Number element: elements) { |
| writer.write(element.byteValue()); |
| } |
| break; |
| case 2: |
| for (Number element: elements) { |
| writer.writeShort(element.shortValue()); |
| } |
| break; |
| case 4: |
| for (Number element: elements) { |
| writer.writeInt(element.intValue()); |
| } |
| break; |
| case 8: |
| for (Number element: elements) { |
| writer.writeLong(element.longValue()); |
| } |
| break; |
| } |
| if ((writer.getPosition() & 1) != 0) { |
| writer.write(0); |
| } |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| public void write(@Nonnull SparseSwitchPayload instruction) { |
| try { |
| writer.writeUbyte(0); |
| writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8); |
| List<? extends SwitchElement> elements = Ordering.from(switchElementComparator).immutableSortedCopy( |
| instruction.getSwitchElements()); |
| writer.writeUshort(elements.size()); |
| for (SwitchElement element: elements) { |
| writer.writeInt(element.getKey()); |
| } |
| for (SwitchElement element: elements) { |
| writer.writeInt(element.getOffset()); |
| } |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| private final Comparator<SwitchElement> switchElementComparator = new Comparator<SwitchElement>() { |
| @Override public int compare(SwitchElement element1, SwitchElement element2) { |
| return Ints.compare(element1.getKey(), element2.getKey()); |
| } |
| }; |
| |
| public void write(@Nonnull PackedSwitchPayload instruction) { |
| try { |
| writer.writeUbyte(0); |
| writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8); |
| List<? extends SwitchElement> elements = instruction.getSwitchElements(); |
| writer.writeUshort(elements.size()); |
| if (elements.size() == 0) { |
| writer.writeInt(0); |
| } else { |
| writer.writeInt(elements.get(0).getKey()); |
| for (SwitchElement element: elements) { |
| writer.writeInt(element.getOffset()); |
| } |
| } |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| private static int packNibbles(int a, int b) { |
| return (b << 4) | a; |
| } |
| |
| private int getReferenceIndex(ReferenceInstruction referenceInstruction) { |
| return getReferenceIndex(referenceInstruction.getReferenceType(), |
| referenceInstruction.getReference()); |
| } |
| |
| private int getReference2Index(DualReferenceInstruction referenceInstruction) { |
| return getReferenceIndex(referenceInstruction.getReferenceType2(), |
| referenceInstruction.getReference2()); |
| } |
| |
| private int getReferenceIndex(int referenceType, Reference reference) { |
| switch (referenceType) { |
| case ReferenceType.FIELD: |
| return fieldSection.getItemIndex((FieldRefKey) reference); |
| case ReferenceType.METHOD: |
| return methodSection.getItemIndex((MethodRefKey) reference); |
| case ReferenceType.STRING: |
| return stringSection.getItemIndex((StringRef) reference); |
| case ReferenceType.TYPE: |
| return typeSection.getItemIndex((TypeRef) reference); |
| case ReferenceType.METHOD_PROTO: |
| return protoSection.getItemIndex((ProtoRefKey) reference); |
| default: |
| throw new ExceptionWithContext("Unknown reference type: %d", referenceType); |
| } |
| } |
| } |