| /* |
| * |
| * 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); |
| } |
| } |