blob: 7146c222114d6789184504746d3876e13bc99e82 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.flex.abc.models;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.flex.abc.semantics.ExceptionInfo;
import org.apache.flex.abc.semantics.Instruction;
import org.apache.flex.abc.semantics.MethodBodyInfo;
import org.apache.flex.abc.visitors.IDiagnosticsVisitor;
import org.apache.flex.abc.visitors.NilDiagnosticsVisitor;
import org.apache.flex.abc.graph.IFlowgraph;
import org.apache.flex.abc.graph.IBasicBlock;
import org.apache.flex.abc.graph.algorithms.DominatorTree.Multimap;
/**
* The TreeModelEncoder translates the stack-oriented semantics
* of ABC bytecode into a tree-oriented model.
*/
public class TreeModelEncoder<T>
{
/**
* Construct a new TreeModelEncoder.
* @param mbi - the method body of interest.
* @param visitor - the TreeModelVisitor that's interested.
* @param diagnosticsVisitor - a handler for any diagnostics generated.
*/
public TreeModelEncoder( MethodBodyInfo mbi, TreeModelVisitor<T> visitor, IDiagnosticsVisitor diagnosticsVisitor)
{
this.mbi = mbi;
this.visitor = visitor;
this.diagnosticsVisitor = diagnosticsVisitor;
this.visitor.visit(this);
setUpFrames();
placePhiNodes();
visitFrames();
this.visitor.visitEnd();
}
/**
* The Method of interest.
*/
private final MethodBodyInfo mbi;
/**
* The model visitor we're driving.
*/
private final TreeModelVisitor<T> visitor;
/**
* Receiver of any diagnostic output.
*/
private final IDiagnosticsVisitor diagnosticsVisitor;
/**
* The Block currently under examination.
*/
IBasicBlock currentBlock;
/**
* The FrameModelEncoder that's driving this TreeModelEncoder.
*/
private FrameModelEncoder encoder;
/**
* The symbolic representations of this method's locals.
*/
ArrayList<Object> localSymbolicReferences = new ArrayList<Object>();
/**
* The symbolic representations of this method's value stack slots.
* Note: There is only one symbol for each element of the stack itself,
* not a symbol for each value the stack will hold over its lifetime.
*/
ArrayList<Object> valueSymbolicReferences = new ArrayList<Object>();
/**
* The symbolic representations of this method's scope stack slots.
* Note: There is only one symbol for each element of the stack itself,
* not a symbol for each scope the stack will hold over its lifetime.
*/
ArrayList<Object> scopeSymbolicReferences = new ArrayList<Object>();
/**
* Blocks modifying frame elements, keyed by
* the symbolic representation of the frame element.
*/
private Map<Object,Set<IBasicBlock>> a = new HashMap<Object,Set<IBasicBlock>>();
/**
* Get the MethodBodyInfo of the method being analyzed.
* @return the MethodBodyInfo being analyzed.
*/
public MethodBodyInfo getMethodBodyInfo()
{
return this.mbi;
}
/**
* Get the method's flowgraph.
* @return the IFlowGraph of the method being analyzed.
*/
public IFlowgraph getCfg()
{
return this.mbi.getCfg();
}
/**
* Get the block currently being analyzed.
* @return the block currently being analyzed.
*/
public IBasicBlock getCurrentBlock()
{
return this.currentBlock;
}
/**
* Get the index of the instruction currently being analyzed.
* @return the intra-Block index of the instruction currently being analyzed.
*/
public int getInstructionIndex()
{
return this.encoder.getInstructionIndex();
}
/**
* Do a preliminary pass over the method to set up
* the frames' extents and live-out sets.
*/
private void setUpFrames()
{
mbi.getCfg().traverseGraph(
new FrameModelEncoder(
this.mbi,
new FrameSetupVisitor(),
new NilDiagnosticsVisitor()
)
);
}
/**
* Place phi-nodes at blocks' dominance frontiers
* to model dataflow merges in frame state.
*/
private void placePhiNodes()
{
int iterCount = 0;
Multimap<IBasicBlock> df = getCfg().getDominatorTree().getDominanceFrontiers();
Map<IBasicBlock, Integer> hasAlready = new HashMap<IBasicBlock,Integer>();
Map<IBasicBlock, Integer> work = new HashMap<IBasicBlock, Integer>();
for ( Object local: localSymbolicReferences )
iterCount = placePhiNodes(iterCount, local, df, a, hasAlready, work);
for ( Object scope: scopeSymbolicReferences )
iterCount = placePhiNodes(iterCount, scope, df, a, hasAlready, work);
for ( Object value: valueSymbolicReferences )
iterCount = placePhiNodes(iterCount, value, df, a, hasAlready, work);
}
/**
* Place phi-nodes for one particular element of the frame.
* @param initialIteration - the initial value of the iteration counter.
* @param v - the frame element being analyzed.
* @param df - the method's dominance frontiers.
* @param a - the map of Blocks that assign to various frame elements.
* @param hasAlready - a map of Block-to-iteration-count values, used
* to note a Block that already has a phi node for the element.
* @param work - a map of Block-to-iteration-count values, used
* to find Blocks that need further analysis.
*/
private int placePhiNodes(
final int initialIteration,
final Object v,
Multimap<IBasicBlock> df,
Map<Object,Set<IBasicBlock>> a,
Map<IBasicBlock,Integer> hasAlready,
Map<IBasicBlock,Integer> work
)
{
if ( ! a.containsKey(v) )
return initialIteration;
int iterCount = initialIteration + 1;
HashSet<IBasicBlock> w = new HashSet<IBasicBlock>();
for ( IBasicBlock x: a.get(v) )
{
work.put(x, iterCount);
w.add(x);
}
while ( ! w.isEmpty() )
{
Iterator<IBasicBlock> it = w.iterator();
IBasicBlock x = it.next();
it.remove();
for ( IBasicBlock y: df.get(x) )
{
if ( !hasAlready.containsKey(y) || hasAlready.get(y).intValue() < iterCount )
{
placePhiNode(y,v);
hasAlready.put(y, iterCount);
if ( !work.containsKey(y) || work.get(y).intValue() < iterCount )
{
work.put(y, iterCount);
w.add(y);
}
}
}
}
return iterCount;
}
/**
* Place a phi-node for a particular (Block,frame element) tuple.
* @param target - the Block.
* @param frameKey - the variable.
*/
void placePhiNode(IBasicBlock target, Object frameKey)
{
Frame targetFrame = getFrame(target);
// Figure out which value this is.
int idx = this.localSymbolicReferences.indexOf(frameKey);
if ( idx != -1 )
{
if ( needsInitializer(targetFrame.locals, idx) )
setFrameElement(targetFrame.locals, idx, visitor.addMergePoint(currentBlock));
}
else
{
idx = this.valueSymbolicReferences.indexOf(frameKey);
if ( idx != -1 )
{
if ( needsInitializer(targetFrame.values, idx) )
setFrameElement(targetFrame.values, idx, visitor.addMergePoint(currentBlock));
}
else
{
idx = scopeSymbolicReferences.indexOf(frameKey);
assert idx != -1;
if ( needsInitializer(targetFrame.scopes, idx) )
setFrameElement(targetFrame.scopes, idx, visitor.addMergePoint(currentBlock));
}
}
}
/**
* Determine if a particular frame element needs an initializer.
* @param elements - the frame elements (locals, scope stack, or value stack).
* @param idx - the index of the frame element of interest.
* @return true if the given index has no initializer object.
*/
private boolean needsInitializer(ArrayList<? extends Object> elements, int idx)
{
return ( elements.size() <= idx || elements.get(idx) == null );
}
/**
* Initialize a frame element with an anonymous marker object if necessary.
* @param elements - the frame elements (locals, scope stack, or value stack).
* @param idx - the index of the frame element of interest.
*/
private void touchFrameElement(ArrayList<Object> elements, int idx)
{
if ( needsInitializer(elements, idx) )
setFrameElement(elements, idx, new Object());
}
/**
* Set a frame element.
* @param elements - the frame elements (locals, scope stack, or value stack).
* @param idx - the index of the frame element of interest.
* @param value - the value to set.
*/
private <E> void setFrameElement(ArrayList<E> elements, int idx, final E value)
{
while ( elements.size() <= idx )
elements.add(null);
elements.set(idx, value);
}
/**
* Modify a frame element; touch it and add the modifying Block to its
* set of assigning blocks.
* @param elements - the frame elements (locals, scope stack, or value stack).
* @param idx - the index of the frame element of interest.
* @param b - the Block that modifies the element.
*/
private void modifyFrameElement(ArrayList<Object> elements, int idx, IBasicBlock b)
{
touchFrameElement(elements,idx);
if ( ! a.containsKey(elements.get(idx)) )
a.put(elements.get(idx), new HashSet<IBasicBlock>());
a.get(elements.get(idx)).add(b);
}
/**
* Visit each block, its associated Frame, and the
* instructions in each block, showing each in turn
* to the TreeModelVisitor and recording its results
* in the Frame.
*/
private void visitFrames()
{
ArrayList<T> parameters = new ArrayList<T>();
// Load the initial frame's locals with parameter information.
for ( int i = 0; i < this.mbi.getMethodInfo().getParamCount(); i++ )
parameters.add(visitor.translateParameter(i));
Frame startFrame = getFrame(this.mbi.getCfg().getStartBlock());
startFrame.locals.addAll(parameters);
// Initialize exception-handling targets with the exception variable,
// and set up a dataflow merge node for each local that may be read
// in the block.
// TODO: This encoder makes pessimistic assumptions about dataflow
// into the locals, i.e., it assumes every exception handler is
// globally reachable.
for ( ExceptionInfo ex: this.mbi.getExceptions() )
{
IBasicBlock catchTarget = this.mbi.getCfg().getBlock(ex.getTarget());
Frame catchFrame = getFrame(catchTarget);
setFrameElement(catchFrame.values, 0, visitor.translateExceptionVariable(ex.getCatchVar(), ex.getExceptionType()));
for ( int i = 0; i < parameters.size(); i++ )
{
catchFrame.locals.add(visitor.addMergePoint(catchTarget));
@SuppressWarnings("unchecked")
TreeModelVisitor.IMergePoint<T> mergeNode = (TreeModelVisitor.IMergePoint<T>)catchFrame.locals.get(i);
mergeNode.addValue(parameters.get(i));
}
// Initialize the other locals' merge nodes, which are initially empty.
for ( int i = parameters.size(); i < localSymbolicReferences.size(); i++ )
{
catchFrame.locals.add(visitor.addMergePoint(catchTarget));
}
}
this.encoder = new FrameModelEncoder( this.mbi, new ModelDrivingVisitor(this.visitor), this.diagnosticsVisitor );
this.mbi.getCfg().traverseGraph( this.encoder );
}
/**
* A representation of an AVM "frame," the local variables, scope stack slots,
* and value stack slots used in a particular Block of the method's flowgraph.
*/
public class Frame
{
/**
* The local variables used in the Block.
*/
public final ArrayList<T> locals = new ArrayList<T>();
/**
* The scope stack slots used in the Block.
*/
public final ArrayList<T> scopes = new ArrayList<T>();
/**
* The value stack slots used in the Block.
*/
public final ArrayList<T> values = new ArrayList<T>();
/**
* @return the value on top of the value stack.
*/
public T tos()
{
return this.values.get(valueStackDepth());
}
/**
* Remove a value from the value stack.
* Visitors must modify the Frame, but should
* maintain their own modifiable view of it.
*/
private T popValue()
{
return popElement(this.values);
}
/**
* Push a value onto the value stack.
* Visitors must modify the Frame, but should
* maintain their own modifiable view of it.
*/
private T pushValue(T value)
{
this.values.add(value);
return value;
}
/**
* Push a value onto the scope stack.
* Visitors must modify the Frame, but should
* maintain their own modifiable view of it.
*/
private T pushScope(T scope)
{
this.scopes.add(scope);
return scope;
}
/**
* Pop a value off the scope stack.
* Visitors must modify the Frame, but should
* maintain their own modifiable view of it.
*/
private T popScope()
{
return popElement(this.scopes);
}
/**
* Get a local variable.
* Visitors must modify the Frame, but should
* maintain their own modifiable view of it.
*/
private T getlocal(int idx)
{
adjustSize(this.locals, idx+1);
return this.locals.get(idx);
}
/**
* Set a local variable.
* Visitors must modify the Frame, but should
* maintain their own modifiable view of it.
*/
private T setlocal(int idx, T value)
{
adjustSize(this.locals, idx+1);
this.locals.set(idx, value);
propagateLocalToCatchBlocks(idx,value);
return value;
}
/**
* Get a scope object.
* Visitors must modify the Frame, but should
* maintain their own modifiable view of it.
*/
private T getscopeobject(int idx)
{
adjustSize(this.scopes, idx+1);
return this.scopes.get(idx);
}
/**
* Verify that the value stack contains
* at least the required number of live values.
* @param required - the numbe of values required.
* @return true if the value stack has the required number of values.
*/
private boolean verifyStackDepth(int required)
{
return this.values.size() >= required;
}
/**
* @return the number of live values on the value stack.
*/
private int valueStackDepth()
{
return this.values.size() - 1;
}
/**
* Verify that the scope stack contains
* at least the required number of live values.
* @param required - the numbe of values required.
* @return true if the scope stack has the required number of values.
*/
private boolean verifyScopeDepth(int required)
{
return this.scopes.size() >= required;
}
/**
* @return the number of live values on the scope stack.
*/
@SuppressWarnings("unused")
private int scopeStackDepth()
{
return this.scopes.size() - 1;
}
}
/**
* Propagate a local variable's value to all catch blocks.
* TODO: this should only propagate to catch blocks reachable
* from the current block; this code errs on the side of pessimism.
* @param idx the index of the local.
* @param value the value to propagate.
*/
void propagateLocalToCatchBlocks(int idx, T value)
{
for ( ExceptionInfo ex: mbi.getExceptions() )
{
IBasicBlock catchTarget = mbi.getCfg().getBlock(ex.getTarget());
Frame catchFrame = getFrame(catchTarget);
if ( catchFrame.locals.get(idx) instanceof TreeModelVisitor.IMergePoint )
{
@SuppressWarnings("unchecked")
TreeModelVisitor.IMergePoint<T> mergeNode = (TreeModelVisitor.IMergePoint<T>)catchFrame.locals.get(idx);
mergeNode.addValue(value);
}
// else it's an exception variable.
}
}
/**
* Active Frames, keyed by their generating Block.
*/
private Map<IBasicBlock,Frame> framesByBlock = new HashMap<IBasicBlock,Frame>();
/**
* Get the Frame that corresponds to a Block.
* @param b - the Block of interest.
* @return the Frame mapped to the Block.
*/
public Frame getFrame(IBasicBlock b)
{
if ( ! this.framesByBlock.containsKey(b) )
this.framesByBlock.put(b, new Frame());
return this.framesByBlock.get(b);
}
/**
* The FrameSetupVisitor creates Frame objects for the Blocks,
* and drives the modifyFrameElement calls that initialize
* the map of Blocks that assign values to specific frame elements.
*/
private class FrameSetupVisitor implements FrameModelVisitor<T>
{
IBasicBlock currentBlock = null;
BlockState blockState = null;
public void visit(FrameModelEncoder encoder)
{
}
public void visitEnd()
{
}
public T noFrameEffect(Instruction i)
{
return null;
}
public T consumeValue(Instruction i, int count)
{
// The model driving visitor detects stack underflow.
blockState.stackDepth = Math.max(blockState.stackDepth - count, 0);
return null;
}
/**
* Handle an instruction that pushes a value onto the stack.
* @param i - the Instruction.
*/
public T produceValue(Instruction i)
{
modifyFrameElement(valueSymbolicReferences, blockState.stackDepth, currentBlock);
blockState.stackDepth++;
return null;
}
public T consumeAndProduceValue(Instruction i, int consumeCount)
{
consumeValue(null, consumeCount);
produceValue(null);
return null;
}
public T branch(Instruction i, IBasicBlock target)
{
return null;
}
public T multiwayBranch(Instruction i, Collection<IBasicBlock> targets)
{
return null;
}
public T getlocal(Instruction i, int idx)
{
touchFrameElement(localSymbolicReferences, idx);
return null;
}
public T setlocal(Instruction i, int idx)
{
modifyFrameElement(localSymbolicReferences, idx, currentBlock);
return null;
}
public void modifyLocal(Instruction i, int idx)
{
modifyFrameElement(localSymbolicReferences, idx, currentBlock);
}
public T moveValueToScopeStack(Instruction i)
{
consumeValue(null, 1);
modifyFrameElement(scopeSymbolicReferences, blockState.scopeDepth, currentBlock);
blockState.scopeDepth++;
return null;
}
public T popscope(Instruction i)
{
blockState.scopeDepth = Math.max(blockState.scopeDepth - 1, 0);
return null;
}
public T getScopeobject(Instruction i, int idx)
{
touchFrameElement(scopeSymbolicReferences, idx);
return null;
}
public T hasnext2(Instruction i)
{
modifyFrameElement(localSymbolicReferences, (Integer)i.getOperand(0), currentBlock);
modifyFrameElement(localSymbolicReferences, (Integer)i.getOperand(1), currentBlock);
return null;
}
public T dup(Instruction i)
{
produceValue(null);
return null;
}
public T swap(Instruction i)
{
// No effect on the frame setup.
return null;
}
public boolean visitBlock(IBasicBlock b)
{
if ( visited.add(b) )
{
assert this.currentBlock == null;
this.currentBlock = b;
this.blockState = getBlockState(b);
return true;
}
else
{
return false;
}
}
/**
* Blocks visisted so far.
*/
private Set<IBasicBlock> visited = new HashSet<IBasicBlock>();
/**
* End the visit to a block; ensure that all its
* frame elements are in place.
*/
public void visitEndBlock(IBasicBlock b)
{
for ( int i = 0; i < this.blockState.stackDepth; i++ )
touchFrameElement(valueSymbolicReferences, i);
for ( int i = 0; i < this.blockState.scopeDepth; i++ )
touchFrameElement(scopeSymbolicReferences, i);
this.currentBlock = null;
this.blockState = null;
}
/**
* Propagate value/scope stack depth information
* from one Block to its target block.
*/
public void visitEdge(IBasicBlock from, IBasicBlock target)
{
assert(from == this.currentBlock);
BlockState targetState = getBlockState(target);
targetState.stackDepth = blockState.stackDepth;
targetState.scopeDepth = blockState.scopeDepth;
}
/**
* Get the scope/value stack depth tracker for a Block.
* @param b - the Block of interest.
* @return the BlockState tracker mapped to it.
*/
private BlockState getBlockState(IBasicBlock b)
{
if ( ! this.statesByBlock.containsKey(b) )
this.statesByBlock.put(b, new BlockState());
return this.statesByBlock.get(b);
}
private Map<IBasicBlock, BlockState> statesByBlock = new HashMap<IBasicBlock,BlockState>();
}
private static class BlockState
{
int stackDepth = 0;
int scopeDepth = 0;
}
/**
* The ModelDrivingVisitor makes a second pass over the method's
* control flow graph, after the Frames have been initialized and
* dataflow merge points placed, and drives the visitor's traversal
* of the method.
*/
private class ModelDrivingVisitor implements FrameModelVisitor<T>
{
ModelDrivingVisitor(TreeModelVisitor<T> visitor)
{
this.visitor = visitor;
}
final TreeModelVisitor<T> visitor;
public void visit(FrameModelEncoder encoder)
{
assert(encoder == TreeModelEncoder.this.encoder);
}
public void visitEnd()
{
TreeModelEncoder.this.encoder = null;
}
/**
* The Frame that corresponds to the block being visited.
*/
Frame currentFrame = null;
@Override
public T noFrameEffect(Instruction i)
{
return visitor.translate(i, noOperands());
}
/**
* Handle an instruction that consumes value stack elements.
* @param i - the Instruction.
* @param count - the number of value stack elements consumed.
*/
public T consumeValue(Instruction i, int count)
{
if ( this.currentFrame.verifyStackDepth(count) )
{
ArrayList<T> operands = new ArrayList<T>(count);
for ( int j = 0; j < count; j++ )
operands.add(this.currentFrame.popValue());
return visitor.translate(i, operands);
}
else
{
return visitor.valueStackUnderflow(i, count);
}
}
/**
* Handle an instruction that pushes a value onto the stack.
* @param i - the Instruction.
*/
public T produceValue(Instruction i)
{
return this.currentFrame.pushValue(visitor.translate(i, noOperands()));
}
/**
* Handle an instruction that consumes value stack elements,
* and then pushes a new value onto the stack.
* @param i - the Instruction.
* @param consumeCount - the number of value stack elements consumed.
*/
public T consumeAndProduceValue(Instruction i, int consumeCount)
{
if ( this.currentFrame.verifyStackDepth(consumeCount) )
{
ArrayList<T> operands = new ArrayList<T>(consumeCount);
for ( int j = 0; j < consumeCount; j++ )
operands.add(this.currentFrame.popValue());
return this.currentFrame.pushValue(visitor.translate(i, operands));
}
else
{
return visitor.valueStackUnderflow(i, consumeCount);
}
}
/**
* Handle a branch instruction.
* @param i - the Instruction.
* @param target - the Instruction's target. Instructions with
* fall-through semantics also implicitly target the next Block.
*/
public T branch(Instruction i, IBasicBlock target)
{
return visitor.translateBranch(i, singleOperand(target));
}
/**
* Handle a multibranch instruction.
* @param i - the Instruction.
* @param targets - the Instruction's targets.
*/
public T multiwayBranch(Instruction i, Collection<IBasicBlock> targets)
{
return visitor.translateBranch(i, targets);
}
/**
* Get a local variable, leaving its value on the stack.
* @param i - the Instruction.
* @param idx - the variable's index.
*/
public T getlocal(Instruction i, int idx)
{
adjustSize(currentFrame.locals, idx + 1);
T result = visitor.translate(i, singleOperand(this.currentFrame.getlocal(idx)));
this.currentFrame.pushValue(result);
return result;
}
/**
* Set a local variable, comsuming a value from the stack.
* @param i - the Instruction.
* @param idx - the variable's index.
*/
public T setlocal(Instruction i, int idx)
{
adjustSize(currentFrame.locals, idx + 1);
if ( this.currentFrame.verifyStackDepth(1) )
{
T result = visitor.translate(i, singleOperand(this.currentFrame.popValue()));
return this.currentFrame.setlocal(idx, result);
}
else
{
return this.currentFrame.setlocal(idx, visitor.valueStackUnderflow(i, 1));
}
}
/**
* Modify a local variable.
* @param i - the Instruction.
* @param idx - the variable's index.
*/
public void modifyLocal(Instruction i, int idx)
{
visitor.translate(i, noOperands());
}
/**
* Pop a value off the value stack and push it on the scope stack.
* @param i - the Instruction (an OP_pushscope).
*/
public T moveValueToScopeStack(Instruction i)
{
if ( this.currentFrame.verifyStackDepth(1) )
return this.currentFrame.pushScope(visitor.translate(i, singleOperand(this.currentFrame.popValue())));
else
return visitor.valueStackUnderflow(i, 1);
}
/**
* Pop a value off the scope stack.
* @param i - the Instruction (an OP_popscope).
*/
public T popscope(Instruction i)
{
if ( this.currentFrame.verifyScopeDepth(1) )
return visitor.translate(i, singleOperand(this.currentFrame.popScope()));
else
return visitor.scopeStackUnderflow(i, 1);
}
/**
* Get a particular scope stack element.
* @param i - the Instruction.
* @param idx - the index of the scope element.
*/
public T getScopeobject(Instruction i, int idx)
{
if ( this.currentFrame.verifyScopeDepth(idx+1) )
return this.currentFrame.pushValue(visitor.translate(i, singleOperand(this.currentFrame.scopes.get(idx))));
else
return visitor.scopeStackUnderflow(i, 1);
}
/**
* Handle the special-case hasnext2 instruction.
* @param i - the Instruction.
*/
public T hasnext2(Instruction i)
{
return null;
}
/**
* Handle the stack-maintenance dup instruction.
* @param i - the Instruction.
*/
public T dup(Instruction i)
{
if ( this.currentFrame.verifyStackDepth(1) )
{
return this.currentFrame.pushValue(visitor.translate(i, singleOperand(this.currentFrame.tos())));
}
else
{
return visitor.valueStackUnderflow(i, 1);
}
}
/**
* Handle the stack-maintenance swap instruction.
* @param i - the Instruction.
*/
public T swap(Instruction i)
{
if ( this.currentFrame.verifyStackDepth(2) )
{
int stackDepth = this.currentFrame.valueStackDepth();
T temp = visitor.translate(i, singleOperand(this.currentFrame.tos()));
this.currentFrame.values.set( stackDepth, this.currentFrame.values.get(stackDepth-1) );
this.currentFrame.values.set( stackDepth - 1, temp);
return this.currentFrame.tos();
}
else
{
return visitor.valueStackUnderflow(i, 2);
}
}
@Override
public boolean visitBlock(IBasicBlock b)
{
Frame frame = getFrame(b);
currentBlock = b;
this.currentFrame = frame;
if ( visitor.visitBlock(b) )
{
return true;
}
else
{
// Tear down.
this.currentFrame = null;
currentBlock = null;
return false;
}
}
@Override
public void visitEndBlock(IBasicBlock b)
{
visitor.visitEnd(b);
this.currentFrame = null;
currentBlock = null;
}
@Override
public void visitEdge(IBasicBlock from, IBasicBlock target)
{
assert getFrame(from) == this.currentFrame;
Frame targetFrame = getFrame(target);
for ( int i = 0; i < this.currentFrame.locals.size(); i++ )
addInitializer(i, targetFrame.locals, this.currentFrame.getlocal(i));
for ( int i = 0; i < this.currentFrame.values.size(); i++ )
addInitializer(i, targetFrame.values, this.currentFrame.values.get(i));
for ( int i = 0; i < this.currentFrame.scopes.size(); i++ )
addInitializer(i, targetFrame.scopes, this.currentFrame.getscopeobject(i));
}
private void addInitializer(final int i, ArrayList<T> target, T value)
{
if ( target.size() <= i )
{
adjustSize(target, i);
target.add(value);
}
else if ( target.get(i) instanceof TreeModelVisitor.IMergePoint<?> )
{
@SuppressWarnings("unchecked")
TreeModelVisitor.IMergePoint<T> phi = (TreeModelVisitor.IMergePoint<T>) target.get(i);
phi.addValue(value);
}
else if ( target.get(i) == null )
{
target.set(i, value);
}
else
{
// TODO: Verify that the existing value and the current value are the same.
}
}
/**
* Build an operands Collection from a single operand.
* @param operand - the operand.
* @return the operand, wrapped in a Collection.
*/
private <X> Collection<X> singleOperand(X operand)
{
ArrayList<X> result = new ArrayList<X>(1);
result.add(operand);
return result;
}
/**
* @return an empty list, approprately cast.
*/
private Collection<T> noOperands()
{
return Collections.emptyList();
}
}
/**
* Adjust the size of a collection of frame elements.
* @param frameElements - the frame elements of interest.
* @param idx - the minimum size of the frame element.
*/
private static <X> void adjustSize(ArrayList<X> frameElements, int idx)
{
while(frameElements.size() < idx )
frameElements.add(null);
}
/**
* Pop a value off a collection of frame elements.
* @param frameElements - the frame elements of interest.
* @return the last element in the collection, which
* has been removed from the collection.
*/
static <X> X popElement(ArrayList<X> frameElements)
{
int lastIdx = frameElements.size() - 1;
return frameElements.remove(lastIdx);
}
}