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.dexgen.rop.code; 18 19 import com.android.dexgen.rop.type.TypeBearer; 20 import com.android.dexgen.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 */ LocalVariableInfo(RopMethod method)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 */ setStarts(int label, RegisterSpecSet specs)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 */ mergeStarts(int label, RegisterSpecSet specs)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 newStart.intersect(specs, true); 119 120 if (start.equals(newStart)) { 121 return false; 122 } 123 124 newStart.setImmutable(); 125 setStarts(label, newStart); 126 127 return true; 128 } 129 130 /** 131 * Gets the register set associated with the start of the block 132 * with the given label. This returns an empty set with the appropriate 133 * max size if no set was associated with the block in question. 134 * 135 * @param label {@code >= 0;} the block label 136 * @return {@code non-null;} the associated register set 137 */ getStarts(int label)138 public RegisterSpecSet getStarts(int label) { 139 RegisterSpecSet result = getStarts0(label); 140 141 return (result != null) ? result : emptySet; 142 } 143 144 /** 145 * Gets the register set associated with the start of the given 146 * block. This is just convenient shorthand for 147 * {@code getStarts(block.getLabel())}. 148 * 149 * @param block {@code non-null;} the block in question 150 * @return {@code non-null;} the associated register set 151 */ getStarts(BasicBlock block)152 public RegisterSpecSet getStarts(BasicBlock block) { 153 return getStarts(block.getLabel()); 154 } 155 156 /** 157 * Gets a mutable copy of the register set associated with the 158 * start of the block with the given label. This returns a 159 * newly-allocated empty {@link RegisterSpecSet} of appropriate 160 * max size if there is not yet any set associated with the block. 161 * 162 * @param label {@code >= 0;} the block label 163 * @return {@code non-null;} the associated register set 164 */ mutableCopyOfStarts(int label)165 public RegisterSpecSet mutableCopyOfStarts(int label) { 166 RegisterSpecSet result = getStarts0(label); 167 168 return (result != null) ? 169 result.mutableCopy() : new RegisterSpecSet(regCount); 170 } 171 172 /** 173 * Adds an assignment association for the given instruction and 174 * register spec. This throws an exception if the instruction 175 * doesn't actually perform a named variable assignment. 176 * 177 * <b>Note:</b> Although the instruction contains its own spec for 178 * the result, it still needs to be passed in explicitly to this 179 * method, since the spec that is stored here should always have a 180 * simple type and the one in the instruction can be an arbitrary 181 * {@link TypeBearer} (such as a constant value). 182 * 183 * @param insn {@code non-null;} the instruction in question 184 * @param spec {@code non-null;} the associated register spec 185 */ addAssignment(Insn insn, RegisterSpec spec)186 public void addAssignment(Insn insn, RegisterSpec spec) { 187 throwIfImmutable(); 188 189 if (insn == null) { 190 throw new NullPointerException("insn == null"); 191 } 192 193 if (spec == null) { 194 throw new NullPointerException("spec == null"); 195 } 196 197 insnAssignments.put(insn, spec); 198 } 199 200 /** 201 * Gets the named register being assigned by the given instruction, if 202 * previously stored in this instance. 203 * 204 * @param insn {@code non-null;} instruction in question 205 * @return {@code null-ok;} the named register being assigned, if any 206 */ getAssignment(Insn insn)207 public RegisterSpec getAssignment(Insn insn) { 208 return insnAssignments.get(insn); 209 } 210 211 /** 212 * Gets the number of assignments recorded by this instance. 213 * 214 * @return {@code >= 0;} the number of assignments 215 */ getAssignmentCount()216 public int getAssignmentCount() { 217 return insnAssignments.size(); 218 } 219 debugDump()220 public void debugDump() { 221 for (int label = 0 ; label < blockStarts.length; label++) { 222 if (blockStarts[label] == null) { 223 continue; 224 } 225 226 if (blockStarts[label] == emptySet) { 227 System.out.printf("%04x: empty set\n", label); 228 } else { 229 System.out.printf("%04x: %s\n", label, blockStarts[label]); 230 } 231 } 232 } 233 234 /** 235 * Helper method, to get the starts for a label, throwing the 236 * right exception for range problems. 237 * 238 * @param label {@code >= 0;} the block label 239 * @return {@code null-ok;} associated register set or {@code null} if there 240 * is none 241 */ getStarts0(int label)242 private RegisterSpecSet getStarts0(int label) { 243 try { 244 return blockStarts[label]; 245 } catch (ArrayIndexOutOfBoundsException ex) { 246 // Translate the exception. 247 throw new IllegalArgumentException("bogus label"); 248 } 249 } 250 } 251