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 17package com.android.dx.rop.code; 18 19import com.android.dx.rop.type.TypeBearer; 20import com.android.dx.util.MutabilityControl; 21import java.util.HashMap; 22 23/** 24 * Container for local variable information for a particular {@link 25 * RopMethod}. 26 */ 27public 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