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