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.util.Bits; 20import com.android.dx.util.IntList; 21 22/** 23 * Code to figure out which local variables are active at which points in 24 * a method. 25 */ 26public final class LocalVariableExtractor { 27 /** {@code non-null;} method being extracted from */ 28 private final RopMethod method; 29 30 /** {@code non-null;} block list for the method */ 31 private final BasicBlockList blocks; 32 33 /** {@code non-null;} result in-progress */ 34 private final LocalVariableInfo resultInfo; 35 36 /** {@code non-null;} work set indicating blocks needing to be processed */ 37 private final int[] workSet; 38 39 /** 40 * Extracts out all the local variable information from the given method. 41 * 42 * @param method {@code non-null;} the method to extract from 43 * @return {@code non-null;} the extracted information 44 */ 45 public static LocalVariableInfo extract(RopMethod method) { 46 LocalVariableExtractor lve = new LocalVariableExtractor(method); 47 return lve.doit(); 48 } 49 50 /** 51 * Constructs an instance. This method is private. Use {@link #extract}. 52 * 53 * @param method {@code non-null;} the method to extract from 54 */ 55 private LocalVariableExtractor(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.method = method; 64 this.blocks = blocks; 65 this.resultInfo = new LocalVariableInfo(method); 66 this.workSet = Bits.makeBitSet(maxLabel); 67 } 68 69 /** 70 * Does the extraction. 71 * 72 * @return {@code non-null;} the extracted information 73 */ 74 private LocalVariableInfo doit() { 75 for (int label = method.getFirstLabel(); 76 label >= 0; 77 label = Bits.findFirst(workSet, 0)) { 78 Bits.clear(workSet, label); 79 processBlock(label); 80 } 81 82 resultInfo.setImmutable(); 83 return resultInfo; 84 } 85 86 /** 87 * Processes a single block. 88 * 89 * @param label {@code >= 0;} label of the block to process 90 */ 91 private void processBlock(int label) { 92 RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(label); 93 BasicBlock block = blocks.labelToBlock(label); 94 InsnList insns = block.getInsns(); 95 int insnSz = insns.size(); 96 97 /* 98 * We may have to treat the last instruction specially: If it 99 * can (but doesn't always) throw, and the exception can be 100 * caught within the same method, then we need to use the 101 * state *before* executing it to be what is merged into 102 * exception targets. 103 */ 104 boolean canThrowDuringLastInsn = block.hasExceptionHandlers() && 105 (insns.getLast().getResult() != null); 106 int freezeSecondaryStateAt = insnSz - 1; 107 RegisterSpecSet secondaryState = primaryState; 108 109 /* 110 * Iterate over the instructions, adding information for each place 111 * that the active variable set changes. 112 */ 113 114 for (int i = 0; i < insnSz; i++) { 115 if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) { 116 // Until this point, primaryState == secondaryState. 117 primaryState.setImmutable(); 118 primaryState = primaryState.mutableCopy(); 119 } 120 121 Insn insn = insns.get(i); 122 RegisterSpec result; 123 124 result = insn.getLocalAssignment(); 125 126 if (result == null) { 127 /* 128 * If an assignment assigns over an existing local, make 129 * sure to mark the local as going out of scope. 130 */ 131 132 result = insn.getResult(); 133 134 if (result != null 135 && primaryState.get(result.getReg()) != null) { 136 primaryState.remove(primaryState.get(result.getReg())); 137 } 138 continue; 139 } 140 141 result = result.withSimpleType(); 142 143 RegisterSpec already = primaryState.get(result); 144 /* 145 * The equals() check ensures we only add new info if 146 * the instruction causes a change to the set of 147 * active variables. 148 */ 149 if (!result.equals(already)) { 150 /* 151 * If this insn represents a local moving from one register 152 * to another, remove the association between the old register 153 * and the local. 154 */ 155 RegisterSpec previous 156 = primaryState.localItemToSpec(result.getLocalItem()); 157 158 if (previous != null 159 && (previous.getReg() != result.getReg())) { 160 161 primaryState.remove(previous); 162 } 163 164 resultInfo.addAssignment(insn, result); 165 primaryState.put(result); 166 } 167 } 168 169 primaryState.setImmutable(); 170 171 /* 172 * Merge this state into the start state for each successor, 173 * and update the work set where required (that is, in cases 174 * where the start state for a block changes). 175 */ 176 177 IntList successors = block.getSuccessors(); 178 int succSz = successors.size(); 179 int primarySuccessor = block.getPrimarySuccessor(); 180 181 for (int i = 0; i < succSz; i++) { 182 int succ = successors.get(i); 183 RegisterSpecSet state = (succ == primarySuccessor) ? 184 primaryState : secondaryState; 185 186 if (resultInfo.mergeStarts(succ, state)) { 187 Bits.set(workSet, succ); 188 } 189 } 190 } 191} 192