/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dx.rop.code; import com.android.dx.util.Bits; import com.android.dx.util.IntList; /** * Code to figure out which local variables are active at which points in * a method. */ public final class LocalVariableExtractor { /** {@code non-null;} method being extracted from */ private final RopMethod method; /** {@code non-null;} block list for the method */ private final BasicBlockList blocks; /** {@code non-null;} result in-progress */ private final LocalVariableInfo resultInfo; /** {@code non-null;} work set indicating blocks needing to be processed */ private final int[] workSet; /** * Extracts out all the local variable information from the given method. * * @param method {@code non-null;} the method to extract from * @return {@code non-null;} the extracted information */ public static LocalVariableInfo extract(RopMethod method) { LocalVariableExtractor lve = new LocalVariableExtractor(method); return lve.doit(); } /** * Constructs an instance. This method is private. Use {@link #extract}. * * @param method {@code non-null;} the method to extract from */ private LocalVariableExtractor(RopMethod method) { if (method == null) { throw new NullPointerException("method == null"); } BasicBlockList blocks = method.getBlocks(); int maxLabel = blocks.getMaxLabel(); this.method = method; this.blocks = blocks; this.resultInfo = new LocalVariableInfo(method); this.workSet = Bits.makeBitSet(maxLabel); } /** * Does the extraction. * * @return {@code non-null;} the extracted information */ private LocalVariableInfo doit() { for (int label = method.getFirstLabel(); label >= 0; label = Bits.findFirst(workSet, 0)) { Bits.clear(workSet, label); processBlock(label); } resultInfo.setImmutable(); return resultInfo; } /** * Processes a single block. * * @param label {@code >= 0;} label of the block to process */ private void processBlock(int label) { RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(label); BasicBlock block = blocks.labelToBlock(label); InsnList insns = block.getInsns(); int insnSz = insns.size(); /* * We may have to treat the last instruction specially: If it * can (but doesn't always) throw, and the exception can be * caught within the same method, then we need to use the * state *before* executing it to be what is merged into * exception targets. */ boolean canThrowDuringLastInsn = block.hasExceptionHandlers() && (insns.getLast().getResult() != null); int freezeSecondaryStateAt = insnSz - 1; RegisterSpecSet secondaryState = primaryState; /* * Iterate over the instructions, adding information for each place * that the active variable set changes. */ for (int i = 0; i < insnSz; i++) { if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) { // Until this point, primaryState == secondaryState. primaryState.setImmutable(); primaryState = primaryState.mutableCopy(); } Insn insn = insns.get(i); RegisterSpec result; result = insn.getLocalAssignment(); if (result == null) { /* * If an assignment assigns over an existing local, make * sure to mark the local as going out of scope. */ result = insn.getResult(); if (result != null && primaryState.get(result.getReg()) != null) { primaryState.remove(primaryState.get(result.getReg())); } continue; } result = result.withSimpleType(); RegisterSpec already = primaryState.get(result); /* * The equals() check ensures we only add new info if * the instruction causes a change to the set of * active variables. */ if (!result.equals(already)) { /* * If this insn represents a local moving from one register * to another, remove the association between the old register * and the local. */ RegisterSpec previous = primaryState.localItemToSpec(result.getLocalItem()); if (previous != null && (previous.getReg() != result.getReg())) { primaryState.remove(previous); } resultInfo.addAssignment(insn, result); primaryState.put(result); } } primaryState.setImmutable(); /* * Merge this state into the start state for each successor, * and update the work set where required (that is, in cases * where the start state for a block changes). */ IntList successors = block.getSuccessors(); int succSz = successors.size(); int primarySuccessor = block.getPrimarySuccessor(); for (int i = 0; i < succSz; i++) { int succ = successors.get(i); RegisterSpecSet state = (succ == primarySuccessor) ? primaryState : secondaryState; if (resultInfo.mergeStarts(succ, state)) { Bits.set(workSet, succ); } } } }