Home | History | Annotate | Download | only in builder
      1 /*
      2  * Copyright 2012, 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.builder;
     33 
     34 import com.google.common.collect.Lists;
     35 import org.jf.dexlib2.Opcode;
     36 import org.jf.dexlib2.builder.instruction.*;
     37 import org.jf.dexlib2.iface.instruction.Instruction;
     38 import org.jf.dexlib2.iface.instruction.OffsetInstruction;
     39 import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
     40 import org.jf.dexlib2.iface.instruction.formats.PackedSwitchPayload;
     41 import org.jf.dexlib2.iface.instruction.formats.SparseSwitchPayload;
     42 import org.junit.Assert;
     43 import org.junit.Test;
     44 
     45 import java.util.List;
     46 
     47 public class PayloadAlignmentTest {
     48 
     49     @Test
     50     public void testPayloadAlignmentRemoveNop() {
     51         MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10);
     52 
     53         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
     54         implBuilder.addInstruction(new BuilderArrayPayload(4, null));
     55 
     56         List<? extends Instruction> instructions =
     57                 Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions());
     58 
     59         Assert.assertEquals(instructions.size(), 1);
     60 
     61         Instruction instruction = instructions.get(0);
     62 
     63         Assert.assertEquals(instruction.getOpcode(), Opcode.ARRAY_PAYLOAD);
     64     }
     65 
     66     @Test
     67     public void testPayloadAlignmentAddNop() {
     68         MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10);
     69 
     70         implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0));
     71         implBuilder.addInstruction(new BuilderArrayPayload(4, null));
     72 
     73         List<? extends Instruction> instructions =
     74                 Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions());
     75 
     76         Assert.assertEquals(instructions.size(), 3);
     77 
     78         Instruction instruction = instructions.get(0);
     79         Assert.assertEquals(instruction.getOpcode(), Opcode.MOVE);
     80 
     81         instruction = instructions.get(1);
     82         Assert.assertEquals(instruction.getOpcode(), Opcode.NOP);
     83 
     84         instruction = instructions.get(2);
     85         Assert.assertEquals(instruction.getOpcode(), Opcode.ARRAY_PAYLOAD);
     86     }
     87 
     88     @Test
     89     public void testPayloadAlignmentRemoveNopWithReferent() {
     90         MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10);
     91 
     92         Label label = implBuilder.getLabel("array_payload");
     93         implBuilder.addInstruction(new BuilderInstruction31t(Opcode.FILL_ARRAY_DATA, 0, label));
     94         implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0));
     95         implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0));
     96         implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0));
     97         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
     98         implBuilder.addLabel("array_payload");
     99         implBuilder.addInstruction(new BuilderArrayPayload(4, null));
    100 
    101         List<Instruction> instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions());
    102 
    103         checkInstructions(instructions,
    104                 new Opcode[]{Opcode.FILL_ARRAY_DATA,
    105                         Opcode.MOVE,
    106                         Opcode.MOVE,
    107                         Opcode.MOVE,
    108                         Opcode.ARRAY_PAYLOAD});
    109 
    110         Instruction31t referent = (Instruction31t)instructions.get(0);
    111         Assert.assertEquals(6, referent.getCodeOffset());
    112     }
    113 
    114     @Test
    115     public void testPayloadAlignmentAddNopWithReferent() {
    116         MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10);
    117 
    118         Label label = implBuilder.getLabel("array_payload");
    119         implBuilder.addInstruction(new BuilderInstruction31t(Opcode.FILL_ARRAY_DATA, 0, label));
    120         implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0));
    121         implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0));
    122         implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0));
    123         implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0));
    124         implBuilder.addLabel("array_payload");
    125         implBuilder.addInstruction(new BuilderArrayPayload(4, null));
    126 
    127         List<Instruction> instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions());
    128 
    129         checkInstructions(instructions,
    130                 new Opcode[]{Opcode.FILL_ARRAY_DATA,
    131                     Opcode.MOVE,
    132                     Opcode.MOVE,
    133                     Opcode.MOVE,
    134                     Opcode.MOVE,
    135                     Opcode.NOP,
    136                     Opcode.ARRAY_PAYLOAD});
    137 
    138         Instruction31t referent = (Instruction31t)instructions.get(0);
    139         Assert.assertEquals(8, referent.getCodeOffset());
    140     }
    141 
    142     private static void checkInstructions(List<Instruction> instructions, Opcode[] expectedOpcodes) {
    143         Assert.assertEquals(expectedOpcodes.length, instructions.size());
    144 
    145         for (int i=0; i<expectedOpcodes.length; i++) {
    146             Assert.assertEquals(instructions.get(i).getOpcode(), expectedOpcodes[i]);
    147         }
    148     }
    149 
    150     @Test
    151     public void testPackedSwitchAlignment() {
    152         MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10);
    153 
    154         implBuilder.addLabel("switch_target_1");
    155         implBuilder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, implBuilder.getLabel("goto_target")));
    156 
    157         implBuilder.addLabel("switch_payload");
    158         implBuilder.addInstruction(new BuilderPackedSwitchPayload(0, Lists.newArrayList(
    159                 implBuilder.getLabel("switch_target_1"),
    160                 implBuilder.getLabel("switch_target_2"),
    161                 implBuilder.getLabel("switch_target_3"))));
    162 
    163         implBuilder.addLabel("goto_target");
    164         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
    165         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
    166 
    167         implBuilder.addLabel("switch_target_2");
    168         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
    169 
    170         implBuilder.addLabel("switch_target_3");
    171         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
    172 
    173         implBuilder.addInstruction(new BuilderInstruction31t(Opcode.PACKED_SWITCH, 0,
    174                 implBuilder.getLabel("switch_payload")));
    175 
    176         List<Instruction> instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions());
    177 
    178         checkInstructions(instructions,
    179                 new Opcode[]{Opcode.GOTO,
    180                         Opcode.NOP,
    181                         Opcode.PACKED_SWITCH_PAYLOAD,
    182                         Opcode.NOP,
    183                         Opcode.NOP,
    184                         Opcode.NOP,
    185                         Opcode.NOP,
    186                         Opcode.PACKED_SWITCH});
    187 
    188         OffsetInstruction gotoInstruction = (OffsetInstruction)instructions.get(0);
    189         Assert.assertEquals(12, gotoInstruction.getCodeOffset());
    190 
    191         PackedSwitchPayload payload = (PackedSwitchPayload)instructions.get(2);
    192         Assert.assertEquals(3, payload.getSwitchElements().size());
    193         Assert.assertEquals(-16, payload.getSwitchElements().get(0).getOffset());
    194         Assert.assertEquals(-2, payload.getSwitchElements().get(1).getOffset());
    195         Assert.assertEquals(-1, payload.getSwitchElements().get(2).getOffset());
    196 
    197         OffsetInstruction referent = (OffsetInstruction)instructions.get(7);
    198         Assert.assertEquals(-14, referent.getCodeOffset());
    199     }
    200 
    201     @Test
    202     public void testSparseSwitchAlignment() {
    203         MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10);
    204 
    205         implBuilder.addLabel("switch_target_1");
    206         implBuilder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, implBuilder.getLabel("goto_target")));
    207 
    208         implBuilder.addLabel("switch_payload");
    209         implBuilder.addInstruction(new BuilderSparseSwitchPayload(Lists.newArrayList(
    210                 new SwitchLabelElement(0, implBuilder.getLabel("switch_target_1")),
    211                 new SwitchLabelElement(5, implBuilder.getLabel("switch_target_2")),
    212                 new SwitchLabelElement(10, implBuilder.getLabel("switch_target_3")))));
    213 
    214         implBuilder.addLabel("goto_target");
    215         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
    216         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
    217 
    218         implBuilder.addLabel("switch_target_2");
    219         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
    220 
    221         implBuilder.addLabel("switch_target_3");
    222         implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
    223 
    224         implBuilder.addInstruction(new BuilderInstruction31t(Opcode.SPARSE_SWITCH, 0,
    225                 implBuilder.getLabel("switch_payload")));
    226 
    227         List<Instruction> instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions());
    228 
    229         checkInstructions(instructions,
    230                 new Opcode[]{Opcode.GOTO,
    231                         Opcode.NOP,
    232                         Opcode.SPARSE_SWITCH_PAYLOAD,
    233                         Opcode.NOP,
    234                         Opcode.NOP,
    235                         Opcode.NOP,
    236                         Opcode.NOP,
    237                         Opcode.SPARSE_SWITCH});
    238 
    239         OffsetInstruction gotoInstruction = (OffsetInstruction)instructions.get(0);
    240         Assert.assertEquals(16, gotoInstruction.getCodeOffset());
    241 
    242         SparseSwitchPayload payload = (SparseSwitchPayload)instructions.get(2);
    243         Assert.assertEquals(3, payload.getSwitchElements().size());
    244         Assert.assertEquals(-20, payload.getSwitchElements().get(0).getOffset());
    245         Assert.assertEquals(-2, payload.getSwitchElements().get(1).getOffset());
    246         Assert.assertEquals(-1, payload.getSwitchElements().get(2).getOffset());
    247 
    248         OffsetInstruction referent = (OffsetInstruction)instructions.get(7);
    249         Assert.assertEquals(-18, referent.getCodeOffset());
    250     }
    251 }
    252