Home | History | Annotate | Download | only in writer
      1 /*
      2  * Copyright 2013, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.dexlib2.writer;
     33 
     34 import com.google.common.collect.Ordering;
     35 import com.google.common.primitives.Ints;
     36 import org.jf.dexlib2.Opcode;
     37 import org.jf.dexlib2.Opcodes;
     38 import org.jf.dexlib2.ReferenceType;
     39 import org.jf.dexlib2.iface.instruction.DualReferenceInstruction;
     40 import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
     41 import org.jf.dexlib2.iface.instruction.SwitchElement;
     42 import org.jf.dexlib2.iface.instruction.formats.*;
     43 import org.jf.dexlib2.iface.reference.FieldReference;
     44 import org.jf.dexlib2.iface.reference.MethodProtoReference;
     45 import org.jf.dexlib2.iface.reference.MethodReference;
     46 import org.jf.dexlib2.iface.reference.Reference;
     47 import org.jf.dexlib2.iface.reference.StringReference;
     48 import org.jf.dexlib2.iface.reference.TypeReference;
     49 import org.jf.util.ExceptionWithContext;
     50 
     51 import javax.annotation.Nonnull;
     52 import java.io.IOException;
     53 import java.util.Comparator;
     54 import java.util.List;
     55 
     56 public class InstructionWriter<StringRef extends StringReference, TypeRef extends TypeReference,
     57         FieldRefKey extends FieldReference, MethodRefKey extends MethodReference,
     58         ProtoRefKey extends MethodProtoReference> {
     59     @Nonnull private final Opcodes opcodes;
     60     @Nonnull private final DexDataWriter writer;
     61     @Nonnull private final StringSection<?, StringRef> stringSection;
     62     @Nonnull private final TypeSection<?, ?, TypeRef> typeSection;
     63     @Nonnull private final FieldSection<?, ?, FieldRefKey, ?> fieldSection;
     64     @Nonnull private final MethodSection<?, ?, ?, MethodRefKey, ?> methodSection;
     65     @Nonnull private final ProtoSection<?, ?, ProtoRefKey, ?> protoSection;
     66 
     67     @Nonnull static <StringRef extends StringReference, TypeRef extends TypeReference, FieldRefKey extends FieldReference,
     68             MethodRefKey extends MethodReference, ProtoRefKey extends MethodProtoReference>
     69             InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey, ProtoRefKey>
     70             makeInstructionWriter(
     71                 @Nonnull Opcodes opcodes,
     72                 @Nonnull DexDataWriter writer,
     73                 @Nonnull StringSection<?, StringRef> stringSection,
     74                 @Nonnull TypeSection<?, ?, TypeRef> typeSection,
     75                 @Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection,
     76                 @Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection,
     77                 @Nonnull ProtoSection<?, ?, ProtoRefKey, ?> protoSection) {
     78         return new InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey, ProtoRefKey>(
     79                 opcodes, writer, stringSection, typeSection, fieldSection, methodSection, protoSection);
     80     }
     81 
     82     InstructionWriter(@Nonnull Opcodes opcodes,
     83                       @Nonnull DexDataWriter writer,
     84                       @Nonnull StringSection<?, StringRef> stringSection,
     85                       @Nonnull TypeSection<?, ?, TypeRef> typeSection,
     86                       @Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection,
     87                       @Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection,
     88                       @Nonnull ProtoSection<?, ?, ProtoRefKey, ?> protoSection) {
     89         this.opcodes = opcodes;
     90         this.writer = writer;
     91         this.stringSection = stringSection;
     92         this.typeSection = typeSection;
     93         this.fieldSection = fieldSection;
     94         this.methodSection = methodSection;
     95         this.protoSection = protoSection;
     96     }
     97 
     98     private short getOpcodeValue(Opcode opcode) {
     99         Short value = opcodes.getOpcodeValue(opcode);
    100         if (value == null) {
    101             throw new ExceptionWithContext("Instruction %s is invalid for api %d", opcode.name, opcodes.api);
    102         }
    103         return value;
    104     }
    105 
    106     public void write(@Nonnull Instruction10t instruction) {
    107         try {
    108             writer.write(getOpcodeValue(instruction.getOpcode()));
    109             writer.write(instruction.getCodeOffset());
    110         } catch (IOException ex) {
    111             throw new RuntimeException(ex);
    112         }
    113     }
    114 
    115     public void write(@Nonnull Instruction10x instruction) {
    116         try {
    117             writer.write(getOpcodeValue(instruction.getOpcode()));
    118             writer.write(0);
    119         } catch (IOException ex) {
    120             throw new RuntimeException(ex);
    121         }
    122     }
    123 
    124     public void write(@Nonnull Instruction11n instruction) {
    125         try {
    126             writer.write(getOpcodeValue(instruction.getOpcode()));
    127             writer.write(packNibbles(instruction.getRegisterA(), instruction.getNarrowLiteral()));
    128         } catch (IOException ex) {
    129             throw new RuntimeException(ex);
    130         }
    131     }
    132 
    133     public void write(@Nonnull Instruction11x instruction) {
    134         try {
    135             writer.write(getOpcodeValue(instruction.getOpcode()));
    136             writer.write(instruction.getRegisterA());
    137         } catch (IOException ex) {
    138             throw new RuntimeException(ex);
    139         }
    140     }
    141 
    142     public void write(@Nonnull Instruction12x instruction) {
    143         try {
    144             writer.write(getOpcodeValue(instruction.getOpcode()));
    145             writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
    146         } catch (IOException ex) {
    147             throw new RuntimeException(ex);
    148         }
    149     }
    150 
    151     public void write(@Nonnull Instruction20bc instruction) {
    152         try {
    153             writer.write(getOpcodeValue(instruction.getOpcode()));
    154             writer.write(instruction.getVerificationError());
    155             writer.writeUshort(getReferenceIndex(instruction));
    156         } catch (IOException ex) {
    157             throw new RuntimeException(ex);
    158         }
    159     }
    160 
    161     public void write(@Nonnull Instruction20t instruction) {
    162         try {
    163             writer.write(getOpcodeValue(instruction.getOpcode()));
    164             writer.write(0);
    165             writer.writeShort(instruction.getCodeOffset());
    166         } catch (IOException ex) {
    167             throw new RuntimeException(ex);
    168         }
    169     }
    170 
    171     public void write(@Nonnull Instruction21c instruction) {
    172         try {
    173             writer.write(getOpcodeValue(instruction.getOpcode()));
    174             writer.write(instruction.getRegisterA());
    175             writer.writeUshort(getReferenceIndex(instruction));
    176         } catch (IOException ex) {
    177             throw new RuntimeException(ex);
    178         }
    179     }
    180 
    181     public void write(@Nonnull Instruction21ih instruction) {
    182         try {
    183             writer.write(getOpcodeValue(instruction.getOpcode()));
    184             writer.write(instruction.getRegisterA());
    185             writer.writeShort(instruction.getHatLiteral());
    186         } catch (IOException ex) {
    187             throw new RuntimeException(ex);
    188         }
    189     }
    190 
    191     public void write(@Nonnull Instruction21lh instruction) {
    192         try {
    193             writer.write(getOpcodeValue(instruction.getOpcode()));
    194             writer.write(instruction.getRegisterA());
    195             writer.writeShort(instruction.getHatLiteral());
    196         } catch (IOException ex) {
    197             throw new RuntimeException(ex);
    198         }
    199     }
    200 
    201     public void write(@Nonnull Instruction21s instruction) {
    202         try {
    203             writer.write(getOpcodeValue(instruction.getOpcode()));
    204             writer.write(instruction.getRegisterA());
    205             writer.writeShort(instruction.getNarrowLiteral());
    206         } catch (IOException ex) {
    207             throw new RuntimeException(ex);
    208         }
    209     }
    210 
    211     public void write(@Nonnull Instruction21t instruction) {
    212         try {
    213             writer.write(getOpcodeValue(instruction.getOpcode()));
    214             writer.write(instruction.getRegisterA());
    215             writer.writeShort(instruction.getCodeOffset());
    216         } catch (IOException ex) {
    217             throw new RuntimeException(ex);
    218         }
    219     }
    220 
    221     public void write(@Nonnull Instruction22b instruction) {
    222         try {
    223             writer.write(getOpcodeValue(instruction.getOpcode()));
    224             writer.write(instruction.getRegisterA());
    225             writer.write(instruction.getRegisterB());
    226             writer.write(instruction.getNarrowLiteral());
    227         } catch (IOException ex) {
    228             throw new RuntimeException(ex);
    229         }
    230     }
    231 
    232     public void write(@Nonnull Instruction22c instruction) {
    233         try {
    234             writer.write(getOpcodeValue(instruction.getOpcode()));
    235             writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
    236             writer.writeUshort(getReferenceIndex(instruction));
    237         } catch (IOException ex) {
    238             throw new RuntimeException(ex);
    239         }
    240     }
    241 
    242     public void write(@Nonnull Instruction22cs instruction) {
    243         try {
    244             writer.write(getOpcodeValue(instruction.getOpcode()));
    245             writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
    246             writer.writeUshort(instruction.getFieldOffset());
    247         } catch (IOException ex) {
    248             throw new RuntimeException(ex);
    249         }
    250     }
    251 
    252     public void write(@Nonnull Instruction22s instruction) {
    253         try {
    254             writer.write(getOpcodeValue(instruction.getOpcode()));
    255             writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
    256             writer.writeShort(instruction.getNarrowLiteral());
    257         } catch (IOException ex) {
    258             throw new RuntimeException(ex);
    259         }
    260     }
    261 
    262     public void write(@Nonnull Instruction22t instruction) {
    263         try {
    264             writer.write(getOpcodeValue(instruction.getOpcode()));
    265             writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
    266             writer.writeShort(instruction.getCodeOffset());
    267         } catch (IOException ex) {
    268             throw new RuntimeException(ex);
    269         }
    270     }
    271 
    272     public void write(@Nonnull Instruction22x instruction) {
    273         try {
    274             writer.write(getOpcodeValue(instruction.getOpcode()));
    275             writer.write(instruction.getRegisterA());
    276             writer.writeUshort(instruction.getRegisterB());
    277         } catch (IOException ex) {
    278             throw new RuntimeException(ex);
    279         }
    280     }
    281 
    282     public void write(@Nonnull Instruction23x instruction) {
    283         try {
    284             writer.write(getOpcodeValue(instruction.getOpcode()));
    285             writer.write(instruction.getRegisterA());
    286             writer.write(instruction.getRegisterB());
    287             writer.write(instruction.getRegisterC());
    288         } catch (IOException ex) {
    289             throw new RuntimeException(ex);
    290         }
    291     }
    292 
    293     public void write(@Nonnull Instruction30t instruction) {
    294         try {
    295             writer.write(getOpcodeValue(instruction.getOpcode()));
    296             writer.write(0);
    297             writer.writeInt(instruction.getCodeOffset());
    298         } catch (IOException ex) {
    299             throw new RuntimeException(ex);
    300         }
    301     }
    302 
    303     public void write(@Nonnull Instruction31c instruction) {
    304         try {
    305             writer.write(getOpcodeValue(instruction.getOpcode()));
    306             writer.write(instruction.getRegisterA());
    307             writer.writeInt(getReferenceIndex(instruction));
    308         } catch (IOException ex) {
    309             throw new RuntimeException(ex);
    310         }
    311     }
    312 
    313     public void write(@Nonnull Instruction31i instruction) {
    314         try {
    315             writer.write(getOpcodeValue(instruction.getOpcode()));
    316             writer.write(instruction.getRegisterA());
    317             writer.writeInt(instruction.getNarrowLiteral());
    318         } catch (IOException ex) {
    319             throw new RuntimeException(ex);
    320         }
    321     }
    322 
    323     public void write(@Nonnull Instruction31t instruction) {
    324         try {
    325             writer.write(getOpcodeValue(instruction.getOpcode()));
    326             writer.write(instruction.getRegisterA());
    327             writer.writeInt(instruction.getCodeOffset());
    328         } catch (IOException ex) {
    329             throw new RuntimeException(ex);
    330         }
    331     }
    332 
    333     public void write(@Nonnull Instruction32x instruction) {
    334         try {
    335             writer.write(getOpcodeValue(instruction.getOpcode()));
    336             writer.write(0);
    337             writer.writeUshort(instruction.getRegisterA());
    338             writer.writeUshort(instruction.getRegisterB());
    339         } catch (IOException ex) {
    340             throw new RuntimeException(ex);
    341         }
    342     }
    343 
    344     public void write(@Nonnull Instruction35c instruction) {
    345         try {
    346             writer.write(getOpcodeValue(instruction.getOpcode()));
    347             writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount()));
    348             writer.writeUshort(getReferenceIndex(instruction));
    349             writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD()));
    350             writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF()));
    351         } catch (IOException ex) {
    352             throw new RuntimeException(ex);
    353         }
    354     }
    355 
    356     public void write(@Nonnull Instruction35mi instruction) {
    357         try {
    358             writer.write(getOpcodeValue(instruction.getOpcode()));
    359             writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount()));
    360             writer.writeUshort(instruction.getInlineIndex());
    361             writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD()));
    362             writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF()));
    363         } catch (IOException ex) {
    364             throw new RuntimeException(ex);
    365         }
    366     }
    367 
    368     public void write(@Nonnull Instruction35ms instruction) {
    369         try {
    370             writer.write(getOpcodeValue(instruction.getOpcode()));
    371             writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount()));
    372             writer.writeUshort(instruction.getVtableIndex());
    373             writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD()));
    374             writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF()));
    375         } catch (IOException ex) {
    376             throw new RuntimeException(ex);
    377         }
    378     }
    379 
    380     public void write(@Nonnull Instruction3rc instruction) {
    381         try {
    382             writer.write(getOpcodeValue(instruction.getOpcode()));
    383             writer.write(instruction.getRegisterCount());
    384             writer.writeUshort(getReferenceIndex(instruction));
    385             writer.writeUshort(instruction.getStartRegister());
    386         } catch (IOException ex) {
    387             throw new RuntimeException(ex);
    388         }
    389     }
    390 
    391     public void write(@Nonnull Instruction3rmi instruction) {
    392         try {
    393             writer.write(getOpcodeValue(instruction.getOpcode()));
    394             writer.write(instruction.getRegisterCount());
    395             writer.writeUshort(instruction.getInlineIndex());
    396             writer.writeUshort(instruction.getStartRegister());
    397         } catch (IOException ex) {
    398             throw new RuntimeException(ex);
    399         }
    400     }
    401 
    402 
    403     public void write(@Nonnull Instruction3rms instruction) {
    404         try {
    405             writer.write(getOpcodeValue(instruction.getOpcode()));
    406             writer.write(instruction.getRegisterCount());
    407             writer.writeUshort(instruction.getVtableIndex());
    408             writer.writeUshort(instruction.getStartRegister());
    409         } catch (IOException ex) {
    410             throw new RuntimeException(ex);
    411         }
    412     }
    413 
    414     public void write(@Nonnull Instruction45cc instruction) {
    415         try {
    416             writer.write(getOpcodeValue(instruction.getOpcode()));
    417             writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount()));
    418             writer.writeUshort(getReferenceIndex(instruction));
    419             writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD()));
    420             writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF()));
    421             writer.writeUshort(getReference2Index(instruction));
    422         } catch (IOException ex) {
    423             throw new RuntimeException(ex);
    424         }
    425     }
    426 
    427     public void write(@Nonnull Instruction4rcc instruction) {
    428         try {
    429             writer.write(getOpcodeValue(instruction.getOpcode()));
    430             writer.write(instruction.getRegisterCount());
    431             writer.writeUshort(getReferenceIndex(instruction));
    432             writer.writeUshort(instruction.getStartRegister());
    433             writer.writeUshort(getReference2Index(instruction));
    434         } catch (IOException ex) {
    435             throw new RuntimeException(ex);
    436         }
    437     }
    438 
    439     public void write(@Nonnull Instruction51l instruction) {
    440         try {
    441             writer.write(getOpcodeValue(instruction.getOpcode()));
    442             writer.write(instruction.getRegisterA());
    443             writer.writeLong(instruction.getWideLiteral());
    444         } catch (IOException ex) {
    445             throw new RuntimeException(ex);
    446         }
    447     }
    448 
    449     public void write(@Nonnull ArrayPayload instruction) {
    450         try {
    451             writer.writeUshort(getOpcodeValue(instruction.getOpcode()));
    452             writer.writeUshort(instruction.getElementWidth());
    453             List<Number> elements = instruction.getArrayElements();
    454             writer.writeInt(elements.size());
    455             switch (instruction.getElementWidth()) {
    456                 case 1:
    457                     for (Number element: elements) {
    458                         writer.write(element.byteValue());
    459                     }
    460                     break;
    461                 case 2:
    462                     for (Number element: elements) {
    463                         writer.writeShort(element.shortValue());
    464                     }
    465                     break;
    466                 case 4:
    467                     for (Number element: elements) {
    468                         writer.writeInt(element.intValue());
    469                     }
    470                     break;
    471                 case 8:
    472                     for (Number element: elements) {
    473                         writer.writeLong(element.longValue());
    474                     }
    475                     break;
    476             }
    477             if ((writer.getPosition() & 1) != 0) {
    478                 writer.write(0);
    479             }
    480         } catch (IOException ex) {
    481             throw new RuntimeException(ex);
    482         }
    483     }
    484 
    485     public void write(@Nonnull SparseSwitchPayload instruction) {
    486         try {
    487             writer.writeUbyte(0);
    488             writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8);
    489             List<? extends SwitchElement> elements = Ordering.from(switchElementComparator).immutableSortedCopy(
    490                     instruction.getSwitchElements());
    491             writer.writeUshort(elements.size());
    492             for (SwitchElement element: elements) {
    493                 writer.writeInt(element.getKey());
    494             }
    495             for (SwitchElement element: elements) {
    496                 writer.writeInt(element.getOffset());
    497             }
    498         } catch (IOException ex) {
    499             throw new RuntimeException(ex);
    500         }
    501     }
    502 
    503     private final Comparator<SwitchElement> switchElementComparator = new Comparator<SwitchElement>() {
    504         @Override public int compare(SwitchElement element1, SwitchElement element2) {
    505             return Ints.compare(element1.getKey(), element2.getKey());
    506         }
    507     };
    508 
    509     public void write(@Nonnull PackedSwitchPayload instruction) {
    510         try {
    511             writer.writeUbyte(0);
    512             writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8);
    513             List<? extends SwitchElement> elements = instruction.getSwitchElements();
    514             writer.writeUshort(elements.size());
    515             if (elements.size() == 0) {
    516                 writer.writeInt(0);
    517             } else {
    518                 writer.writeInt(elements.get(0).getKey());
    519                 for (SwitchElement element: elements) {
    520                     writer.writeInt(element.getOffset());
    521                 }
    522             }
    523         } catch (IOException ex) {
    524             throw new RuntimeException(ex);
    525         }
    526     }
    527 
    528     private static int packNibbles(int a, int b) {
    529         return (b << 4) | a;
    530     }
    531 
    532     private int getReferenceIndex(ReferenceInstruction referenceInstruction) {
    533         return getReferenceIndex(referenceInstruction.getReferenceType(),
    534                 referenceInstruction.getReference());
    535     }
    536 
    537     private int getReference2Index(DualReferenceInstruction referenceInstruction) {
    538         return getReferenceIndex(referenceInstruction.getReferenceType2(),
    539                 referenceInstruction.getReference2());
    540     }
    541 
    542     private int getReferenceIndex(int referenceType, Reference reference) {
    543         switch (referenceType) {
    544             case ReferenceType.FIELD:
    545                 return fieldSection.getItemIndex((FieldRefKey) reference);
    546             case ReferenceType.METHOD:
    547                 return methodSection.getItemIndex((MethodRefKey) reference);
    548             case ReferenceType.STRING:
    549                 return stringSection.getItemIndex((StringRef) reference);
    550             case ReferenceType.TYPE:
    551                 return typeSection.getItemIndex((TypeRef) reference);
    552             case ReferenceType.METHOD_PROTO:
    553                 return protoSection.getItemIndex((ProtoRefKey) reference);
    554             default:
    555                 throw new ExceptionWithContext("Unknown reference type: %d",  referenceType);
    556         }
    557     }
    558 }
    559