/* * 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.rop.type.TypeBearer; import com.android.dx.util.MutabilityControl; import java.util.HashMap; /** * Container for local variable information for a particular {@link * RopMethod}. */ public final class LocalVariableInfo extends MutabilityControl { /** {@code >= 0;} the register count for the method */ private final int regCount; /** * {@code non-null;} {@link RegisterSpecSet} to use when indicating a block * that has no locals; it is empty and immutable but has an appropriate * max size for the method */ private final RegisterSpecSet emptySet; /** * {@code non-null;} array consisting of register sets representing the * sets of variables already assigned upon entry to each block, * where array indices correspond to block labels */ private final RegisterSpecSet[] blockStarts; /** {@code non-null;} map from instructions to the variable each assigns */ private final HashMap<Insn, RegisterSpec> insnAssignments; /** * Constructs an instance. * * @param method {@code non-null;} the method being represented by this instance */ public LocalVariableInfo(RopMethod method) { if (method == null) { throw new NullPointerException("method == null"); } BasicBlockList blocks = method.getBlocks(); int maxLabel = blocks.getMaxLabel(); this.regCount = blocks.getRegCount(); this.emptySet = new RegisterSpecSet(regCount); this.blockStarts = new RegisterSpecSet[maxLabel]; this.insnAssignments = new HashMap<Insn, RegisterSpec>(blocks.getInstructionCount()); emptySet.setImmutable(); } /** * Sets the register set associated with the start of the block with * the given label. * * @param label {@code >= 0;} the block label * @param specs {@code non-null;} the register set to associate with the block */ public void setStarts(int label, RegisterSpecSet specs) { throwIfImmutable(); if (specs == null) { throw new NullPointerException("specs == null"); } try { blockStarts[label] = specs; } catch (ArrayIndexOutOfBoundsException ex) { // Translate the exception. throw new IllegalArgumentException("bogus label"); } } /** * Merges the given register set into the set for the block with the * given label. If there was not already an associated set, then this * is the same as calling {@link #setStarts}. Otherwise, this will * merge the two sets and call {@link #setStarts} on the result of the * merge. * * @param label {@code >= 0;} the block label * @param specs {@code non-null;} the register set to merge into the start set * for the block * @return {@code true} if the merge resulted in an actual change * to the associated set (including storing one for the first time) or * {@code false} if there was no change */ public boolean mergeStarts(int label, RegisterSpecSet specs) { RegisterSpecSet start = getStarts0(label); boolean changed = false; if (start == null) { setStarts(label, specs); return true; } RegisterSpecSet newStart = start.mutableCopy(); newStart.intersect(specs, true); if (start.equals(newStart)) { return false; } newStart.setImmutable(); setStarts(label, newStart); return true; } /** * Gets the register set associated with the start of the block * with the given label. This returns an empty set with the appropriate * max size if no set was associated with the block in question. * * @param label {@code >= 0;} the block label * @return {@code non-null;} the associated register set */ public RegisterSpecSet getStarts(int label) { RegisterSpecSet result = getStarts0(label); return (result != null) ? result : emptySet; } /** * Gets the register set associated with the start of the given * block. This is just convenient shorthand for * {@code getStarts(block.getLabel())}. * * @param block {@code non-null;} the block in question * @return {@code non-null;} the associated register set */ public RegisterSpecSet getStarts(BasicBlock block) { return getStarts(block.getLabel()); } /** * Gets a mutable copy of the register set associated with the * start of the block with the given label. This returns a * newly-allocated empty {@link RegisterSpecSet} of appropriate * max size if there is not yet any set associated with the block. * * @param label {@code >= 0;} the block label * @return {@code non-null;} the associated register set */ public RegisterSpecSet mutableCopyOfStarts(int label) { RegisterSpecSet result = getStarts0(label); return (result != null) ? result.mutableCopy() : new RegisterSpecSet(regCount); } /** * Adds an assignment association for the given instruction and * register spec. This throws an exception if the instruction * doesn't actually perform a named variable assignment. * * <b>Note:</b> Although the instruction contains its own spec for * the result, it still needs to be passed in explicitly to this * method, since the spec that is stored here should always have a * simple type and the one in the instruction can be an arbitrary * {@link TypeBearer} (such as a constant value). * * @param insn {@code non-null;} the instruction in question * @param spec {@code non-null;} the associated register spec */ public void addAssignment(Insn insn, RegisterSpec spec) { throwIfImmutable(); if (insn == null) { throw new NullPointerException("insn == null"); } if (spec == null) { throw new NullPointerException("spec == null"); } insnAssignments.put(insn, spec); } /** * Gets the named register being assigned by the given instruction, if * previously stored in this instance. * * @param insn {@code non-null;} instruction in question * @return {@code null-ok;} the named register being assigned, if any */ public RegisterSpec getAssignment(Insn insn) { return insnAssignments.get(insn); } /** * Gets the number of assignments recorded by this instance. * * @return {@code >= 0;} the number of assignments */ public int getAssignmentCount() { return insnAssignments.size(); } public void debugDump() { for (int label = 0 ; label < blockStarts.length; label++) { if (blockStarts[label] == null) { continue; } if (blockStarts[label] == emptySet) { System.out.printf("%04x: empty set\n", label); } else { System.out.printf("%04x: %s\n", label, blockStarts[label]); } } } /** * Helper method, to get the starts for a label, throwing the * right exception for range problems. * * @param label {@code >= 0;} the block label * @return {@code null-ok;} associated register set or {@code null} if there * is none */ private RegisterSpecSet getStarts0(int label) { try { return blockStarts[label]; } catch (ArrayIndexOutOfBoundsException ex) { // Translate the exception. throw new IllegalArgumentException("bogus label"); } } }