Home | History | Annotate | Download | only in code
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.dx.rop.code;
     18 
     19 import com.android.dx.rop.type.TypeBearer;
     20 import com.android.dx.util.MutabilityControl;
     21 import java.util.HashMap;
     22 
     23 /**
     24  * Container for local variable information for a particular {@link
     25  * RopMethod}.
     26  */
     27 public final class LocalVariableInfo
     28         extends MutabilityControl {
     29     /** {@code >= 0;} the register count for the method */
     30     private final int regCount;
     31 
     32     /**
     33      * {@code non-null;} {@link RegisterSpecSet} to use when indicating a block
     34      * that has no locals; it is empty and immutable but has an appropriate
     35      * max size for the method
     36      */
     37     private final RegisterSpecSet emptySet;
     38 
     39     /**
     40      * {@code non-null;} array consisting of register sets representing the
     41      * sets of variables already assigned upon entry to each block,
     42      * where array indices correspond to block labels
     43      */
     44     private final RegisterSpecSet[] blockStarts;
     45 
     46     /** {@code non-null;} map from instructions to the variable each assigns */
     47     private final HashMap<Insn, RegisterSpec> insnAssignments;
     48 
     49     /**
     50      * Constructs an instance.
     51      *
     52      * @param method {@code non-null;} the method being represented by this instance
     53      */
     54     public LocalVariableInfo(RopMethod method) {
     55         if (method == null) {
     56             throw new NullPointerException("method == null");
     57         }
     58 
     59         BasicBlockList blocks = method.getBlocks();
     60         int maxLabel = blocks.getMaxLabel();
     61 
     62         this.regCount = blocks.getRegCount();
     63         this.emptySet = new RegisterSpecSet(regCount);
     64         this.blockStarts = new RegisterSpecSet[maxLabel];
     65         this.insnAssignments =
     66             new HashMap<Insn, RegisterSpec>(blocks.getInstructionCount());
     67 
     68         emptySet.setImmutable();
     69     }
     70 
     71     /**
     72      * Sets the register set associated with the start of the block with
     73      * the given label.
     74      *
     75      * @param label {@code >= 0;} the block label
     76      * @param specs {@code non-null;} the register set to associate with the block
     77      */
     78     public void setStarts(int label, RegisterSpecSet specs) {
     79         throwIfImmutable();
     80 
     81         if (specs == null) {
     82             throw new NullPointerException("specs == null");
     83         }
     84 
     85         try {
     86             blockStarts[label] = specs;
     87         } catch (ArrayIndexOutOfBoundsException ex) {
     88             // Translate the exception.
     89             throw new IllegalArgumentException("bogus label");
     90         }
     91     }
     92 
     93     /**
     94      * Merges the given register set into the set for the block with the
     95      * given label. If there was not already an associated set, then this
     96      * is the same as calling {@link #setStarts}. Otherwise, this will
     97      * merge the two sets and call {@link #setStarts} on the result of the
     98      * merge.
     99      *
    100      * @param label {@code >= 0;} the block label
    101      * @param specs {@code non-null;} the register set to merge into the start set
    102      * for the block
    103      * @return {@code true} if the merge resulted in an actual change
    104      * to the associated set (including storing one for the first time) or
    105      * {@code false} if there was no change
    106      */
    107     public boolean mergeStarts(int label, RegisterSpecSet specs) {
    108         RegisterSpecSet start = getStarts0(label);
    109         boolean changed = false;
    110 
    111         if (start == null) {
    112             setStarts(label, specs);
    113             return true;
    114         }
    115 
    116         RegisterSpecSet newStart = start.mutableCopy();
    117         if (start.size() != 0) {
    118             newStart.intersect(specs, true);
    119         } else {
    120             newStart = specs.mutableCopy();
    121         }
    122 
    123         if (start.equals(newStart)) {
    124             return false;
    125         }
    126 
    127         newStart.setImmutable();
    128         setStarts(label, newStart);
    129 
    130         return true;
    131     }
    132 
    133     /**
    134      * Gets the register set associated with the start of the block
    135      * with the given label. This returns an empty set with the appropriate
    136      * max size if no set was associated with the block in question.
    137      *
    138      * @param label {@code >= 0;} the block label
    139      * @return {@code non-null;} the associated register set
    140      */
    141     public RegisterSpecSet getStarts(int label) {
    142         RegisterSpecSet result = getStarts0(label);
    143 
    144         return (result != null) ? result : emptySet;
    145     }
    146 
    147     /**
    148      * Gets the register set associated with the start of the given
    149      * block. This is just convenient shorthand for
    150      * {@code getStarts(block.getLabel())}.
    151      *
    152      * @param block {@code non-null;} the block in question
    153      * @return {@code non-null;} the associated register set
    154      */
    155     public RegisterSpecSet getStarts(BasicBlock block) {
    156         return getStarts(block.getLabel());
    157     }
    158 
    159     /**
    160      * Gets a mutable copy of the register set associated with the
    161      * start of the block with the given label. This returns a
    162      * newly-allocated empty {@link RegisterSpecSet} of appropriate
    163      * max size if there is not yet any set associated with the block.
    164      *
    165      * @param label {@code >= 0;} the block label
    166      * @return {@code non-null;} the associated register set
    167      */
    168     public RegisterSpecSet mutableCopyOfStarts(int label) {
    169         RegisterSpecSet result = getStarts0(label);
    170 
    171         return (result != null) ?
    172             result.mutableCopy() : new RegisterSpecSet(regCount);
    173     }
    174 
    175     /**
    176      * Adds an assignment association for the given instruction and
    177      * register spec. This throws an exception if the instruction
    178      * doesn't actually perform a named variable assignment.
    179      *
    180      * <b>Note:</b> Although the instruction contains its own spec for
    181      * the result, it still needs to be passed in explicitly to this
    182      * method, since the spec that is stored here should always have a
    183      * simple type and the one in the instruction can be an arbitrary
    184      * {@link TypeBearer} (such as a constant value).
    185      *
    186      * @param insn {@code non-null;} the instruction in question
    187      * @param spec {@code non-null;} the associated register spec
    188      */
    189     public void addAssignment(Insn insn, RegisterSpec spec) {
    190         throwIfImmutable();
    191 
    192         if (insn == null) {
    193             throw new NullPointerException("insn == null");
    194         }
    195 
    196         if (spec == null) {
    197             throw new NullPointerException("spec == null");
    198         }
    199 
    200         insnAssignments.put(insn, spec);
    201     }
    202 
    203     /**
    204      * Gets the named register being assigned by the given instruction, if
    205      * previously stored in this instance.
    206      *
    207      * @param insn {@code non-null;} instruction in question
    208      * @return {@code null-ok;} the named register being assigned, if any
    209      */
    210     public RegisterSpec getAssignment(Insn insn) {
    211         return insnAssignments.get(insn);
    212     }
    213 
    214     /**
    215      * Gets the number of assignments recorded by this instance.
    216      *
    217      * @return {@code >= 0;} the number of assignments
    218      */
    219     public int getAssignmentCount() {
    220         return insnAssignments.size();
    221     }
    222 
    223     public void debugDump() {
    224         for (int label = 0 ; label < blockStarts.length; label++) {
    225             if (blockStarts[label] == null) {
    226                 continue;
    227             }
    228 
    229             if (blockStarts[label] == emptySet) {
    230                 System.out.printf("%04x: empty set\n", label);
    231             } else {
    232                 System.out.printf("%04x: %s\n", label, blockStarts[label]);
    233             }
    234         }
    235     }
    236 
    237     /**
    238      * Helper method, to get the starts for a label, throwing the
    239      * right exception for range problems.
    240      *
    241      * @param label {@code >= 0;} the block label
    242      * @return {@code null-ok;} associated register set or {@code null} if there
    243      * is none
    244      */
    245     private RegisterSpecSet getStarts0(int label) {
    246         try {
    247             return blockStarts[label];
    248         } catch (ArrayIndexOutOfBoundsException ex) {
    249             // Translate the exception.
    250             throw new IllegalArgumentException("bogus label");
    251         }
    252     }
    253 }
    254