Home | History | Annotate | Download | only in builder
      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.builder;
     33 
     34 import com.google.common.collect.ImmutableList;
     35 import org.jf.dexlib2.builder.debug.*;
     36 import org.jf.dexlib2.iface.instruction.Instruction;
     37 import org.jf.dexlib2.iface.reference.StringReference;
     38 import org.jf.dexlib2.iface.reference.TypeReference;
     39 
     40 import javax.annotation.Nonnull;
     41 import javax.annotation.Nullable;
     42 import java.util.*;
     43 
     44 public class MethodLocation {
     45     @Nullable BuilderInstruction instruction;
     46     int codeAddress;
     47     int index;
     48 
     49     // We end up creating and keeping around a *lot* of MethodLocation objects
     50     // when building a new dex file, so it's worth the trouble of lazily creating
     51     // the labels and debugItems lists only when they are needed
     52 
     53     @Nullable
     54     private List<Label> labels = null;
     55     @Nullable
     56     private List<BuilderDebugItem> debugItems = null;
     57 
     58     MethodLocation(@Nullable BuilderInstruction instruction, int codeAddress, int index) {
     59         this.instruction = instruction;
     60         this.codeAddress = codeAddress;
     61         this.index = index;
     62     }
     63 
     64     @Nullable
     65     public Instruction getInstruction() {
     66         return instruction;
     67     }
     68 
     69     public int getCodeAddress() {
     70         return codeAddress;
     71     }
     72 
     73     public int getIndex() {
     74         return index;
     75     }
     76 
     77     @Nonnull
     78     private List<Label> getLabels(boolean mutable) {
     79         if (labels == null) {
     80             if (mutable) {
     81                 labels = new ArrayList<Label>(1);
     82                 return labels;
     83             }
     84             return ImmutableList.of();
     85         }
     86         return labels;
     87     }
     88 
     89     @Nonnull
     90     private List<BuilderDebugItem> getDebugItems(boolean mutable) {
     91         if (debugItems == null) {
     92             if (mutable) {
     93                 debugItems = new ArrayList<BuilderDebugItem>(1);
     94                 return debugItems;
     95             }
     96             return ImmutableList.of();
     97         }
     98         return debugItems;
     99     }
    100 
    101     void mergeInto(@Nonnull MethodLocation other) {
    102         if (this.labels != null || other.labels != null) {
    103             List<Label> otherLabels = other.getLabels(true);
    104             for (Label label: this.getLabels(false)) {
    105                 label.location = other;
    106                 otherLabels.add(label);
    107             }
    108             this.labels = null;
    109         }
    110 
    111         if (this.debugItems != null || other.labels != null) {
    112             // We need to keep the debug items in the same order. We add the other debug items to this list, then reassign
    113             // the list.
    114             List<BuilderDebugItem> debugItems = getDebugItems(true);
    115             for (BuilderDebugItem debugItem: debugItems) {
    116                 debugItem.location = other;
    117             }
    118             debugItems.addAll(other.getDebugItems(false));
    119             other.debugItems = debugItems;
    120             this.debugItems = null;
    121         }
    122     }
    123 
    124     @Nonnull
    125     public Set<Label> getLabels() {
    126         return new AbstractSet<Label>() {
    127             @Nonnull
    128             @Override public Iterator<Label> iterator() {
    129                 final Iterator<Label> it = getLabels(false).iterator();
    130 
    131                 return new Iterator<Label>() {
    132                     private @Nullable Label currentLabel = null;
    133 
    134                     @Override public boolean hasNext() {
    135                         return it.hasNext();
    136                     }
    137 
    138                     @Override public Label next() {
    139                         currentLabel = it.next();
    140                         return currentLabel;
    141                     }
    142 
    143                     @Override public void remove() {
    144                         if (currentLabel != null) {
    145                             currentLabel.location = null;
    146                         }
    147                         it.remove();
    148                     }
    149                 };
    150             }
    151 
    152             @Override public int size() {
    153                 return getLabels(false).size();
    154             }
    155 
    156             @Override public boolean add(@Nonnull Label label) {
    157                 if (label.isPlaced()) {
    158                     throw new IllegalArgumentException("Cannot add a label that is already placed. You must remove " +
    159                             "it from its current location first.");
    160                 }
    161                 label.location = MethodLocation.this;
    162                 getLabels(true).add(label);
    163                 return true;
    164             }
    165         };
    166     }
    167 
    168     @Nonnull
    169     public Label addNewLabel() {
    170         Label label = new Label(this);
    171         getLabels(true).add(label);
    172         return label;
    173     }
    174 
    175     @Nonnull
    176     public Set<BuilderDebugItem> getDebugItems() {
    177         return new AbstractSet<BuilderDebugItem>() {
    178             @Nonnull
    179             @Override public Iterator<BuilderDebugItem> iterator() {
    180                 final Iterator<BuilderDebugItem> it = getDebugItems(false).iterator();
    181 
    182                 return new Iterator<BuilderDebugItem>() {
    183                     private @Nullable BuilderDebugItem currentDebugItem = null;
    184 
    185                     @Override public boolean hasNext() {
    186                         return it.hasNext();
    187                     }
    188 
    189                     @Override public BuilderDebugItem next() {
    190                         currentDebugItem = it.next();
    191                         return currentDebugItem;
    192                     }
    193 
    194                     @Override public void remove() {
    195                         if (currentDebugItem != null) {
    196                             currentDebugItem.location = null;
    197                         }
    198                         it.remove();
    199                     }
    200                 };
    201             }
    202 
    203             @Override public int size() {
    204                 return getDebugItems(false).size();
    205             }
    206 
    207             @Override public boolean add(@Nonnull BuilderDebugItem debugItem) {
    208                 if (debugItem.location != null) {
    209                     throw new IllegalArgumentException("Cannot add a debug item that has already been added to a " +
    210                             "method. You must remove it from its current location first.");
    211                 }
    212                 debugItem.location = MethodLocation.this;
    213                 getDebugItems(true).add(debugItem);
    214                 return true;
    215             }
    216         };
    217     }
    218 
    219     public void addLineNumber(int lineNumber) {
    220         getDebugItems().add(new BuilderLineNumber(lineNumber));
    221     }
    222 
    223     public void addStartLocal(int registerNumber, @Nullable StringReference name, @Nullable TypeReference type,
    224                               @Nullable StringReference signature) {
    225         getDebugItems().add(new BuilderStartLocal(registerNumber, name, type, signature));
    226     }
    227 
    228     public void addEndLocal(int registerNumber) {
    229         getDebugItems().add(new BuilderEndLocal(registerNumber));
    230     }
    231 
    232     public void addRestartLocal(int registerNumber) {
    233         getDebugItems().add(new BuilderRestartLocal(registerNumber));
    234     }
    235 
    236     public void addPrologue() {
    237         getDebugItems().add(new BuilderPrologueEnd());
    238     }
    239 
    240     public void addEpilogue() {
    241         getDebugItems().add(new BuilderEpilogueBegin());
    242     }
    243 
    244     public void addSetSourceFile(@Nullable StringReference sourceFile) {
    245         getDebugItems().add(new BuilderSetSourceFile(sourceFile));
    246     }
    247 }
    248