com.android.dx.ssa.LocalVariableInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of builder Show documentation
Show all versions of builder Show documentation
Library to build Android applications.
/*
* 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.ssa;
import com.android.dx.rop.code.RegisterSpec;
import com.android.dx.rop.code.RegisterSpecSet;
import com.android.dx.util.MutabilityControl;
import java.util.HashMap;
import java.util.List;
/**
* Container for local variable information for a particular {@link
* com.android.dx.ssa.SsaMethod}.
* Stolen from {@link com.android.dx.rop.code.LocalVariableInfo}.
*/
public class LocalVariableInfo extends MutabilityControl {
/** {@code >= 0;} the register count for the method */
private final int regCount;
/**
* {@code non-null;} {@link com.android.dx.rop.code.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 indices
*/
private final RegisterSpecSet[] blockStarts;
/** {@code non-null;} map from instructions to the variable each assigns */
private final HashMap insnAssignments;
/**
* Constructs an instance.
*
* @param method {@code non-null;} the method being represented by this instance
*/
public LocalVariableInfo(SsaMethod method) {
if (method == null) {
throw new NullPointerException("method == null");
}
List blocks = method.getBlocks();
this.regCount = method.getRegCount();
this.emptySet = new RegisterSpecSet(regCount);
this.blockStarts = new RegisterSpecSet[blocks.size()];
this.insnAssignments =
new HashMap(/*hint here*/);
emptySet.setImmutable();
}
/**
* Sets the register set associated with the start of the block with
* the given index.
*
* @param index {@code >= 0;} the block index
* @param specs {@code non-null;} the register set to associate with the block
*/
public void setStarts(int index, RegisterSpecSet specs) {
throwIfImmutable();
if (specs == null) {
throw new NullPointerException("specs == null");
}
try {
blockStarts[index] = specs;
} catch (ArrayIndexOutOfBoundsException ex) {
// Translate the exception.
throw new IllegalArgumentException("bogus index");
}
}
/**
* Merges the given register set into the set for the block with the
* given index. 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 index {@code >= 0;} the block index
* @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 index, RegisterSpecSet specs) {
RegisterSpecSet start = getStarts0(index);
boolean changed = false;
if (start == null) {
setStarts(index, specs);
return true;
}
RegisterSpecSet newStart = start.mutableCopy();
newStart.intersect(specs, true);
if (start.equals(newStart)) {
return false;
}
newStart.setImmutable();
setStarts(index, newStart);
return true;
}
/**
* Gets the register set associated with the start of the block
* with the given index. This returns an empty set with the appropriate
* max size if no set was associated with the block in question.
*
* @param index {@code >= 0;} the block index
* @return {@code non-null;} the associated register set
*/
public RegisterSpecSet getStarts(int index) {
RegisterSpecSet result = getStarts0(index);
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(SsaBasicBlock block) {
return getStarts(block.getIndex());
}
/**
* Gets a mutable copy of the register set associated with the
* start of the block with the given index. This returns a
* newly-allocated empty {@link RegisterSpecSet} of appropriate
* max size if there is not yet any set associated with the block.
*
* @param index {@code >= 0;} the block index
* @return {@code non-null;} the associated register set
*/
public RegisterSpecSet mutableCopyOfStarts(int index) {
RegisterSpecSet result = getStarts0(index);
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.
*
* Note: 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 com.android.dx.rop.type.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(SsaInsn 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(SsaInsn 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 index = 0 ; index < blockStarts.length; index++) {
if (blockStarts[index] == null) {
continue;
}
if (blockStarts[index] == emptySet) {
System.out.printf("%04x: empty set\n", index);
} else {
System.out.printf("%04x: %s\n", index, blockStarts[index]);
}
}
}
/**
* Helper method, to get the starts for a index, throwing the
* right exception for range problems.
*
* @param index {@code >= 0;} the block index
* @return {@code null-ok;} associated register set or {@code null} if there
* is none
*/
private RegisterSpecSet getStarts0(int index) {
try {
return blockStarts[index];
} catch (ArrayIndexOutOfBoundsException ex) {
// Translate the exception.
throw new IllegalArgumentException("bogus index");
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy