blob: f8c63bda94ceff4e2fb10affd9f8e9d1b1c9713c [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.semantics;
import java.util.HashMap;
import java.util.Map;
import static org.apache.flex.abc.ABCConstants.*;
import org.apache.flex.abc.graph.IBasicBlock;
import org.apache.flex.abc.graph.IFlowgraph;
import org.apache.flex.abc.visitors.IDiagnosticsVisitor;
import org.apache.flex.abc.visitors.IFlowGraphVisitor;
/**
* A FrameCountVisitor tracks the stack, scope, local, and slot numbers
* encountered in a method body, so that the MethodBodyInfo can set its
* max_stack, max_scope, max_local, and max_slot values.
*/
public class FrameCountVisitor implements IFlowGraphVisitor
{
/**
* Construct a new FrameCountVisitor.
*
* @param mbi - the MethodBodyInfo to analyze.
* @param diagnosticsVisitor - a sink for diagnostics.
* @param initial_scope - caller's a priori initial scope depth.
*/
FrameCountVisitor(MethodBodyInfo mbi, IDiagnosticsVisitor diagnosticsVisitor, int initial_scope)
{
this.diagnosticsVisitor = diagnosticsVisitor;
this.cfg = mbi.getCfg();
this.mbi = mbi;
this.initial_scope = initial_scope;
this.exceptions = mbi.getExceptions();
this.instructionIndex = 0;
}
/**
* The MethodBodyInfo that generated the IFlowgraph.
*/
final MethodBodyInfo mbi;
/**
* Receiver of any diagnostic output.
*/
final IDiagnosticsVisitor diagnosticsVisitor;
/**
* The control flow graph, denormalized from the MethodBodyInfo.
*/
final IFlowgraph cfg;
/**
* Caller's a priori initial scope depth.
*/
final int initial_scope;
/**
* Exception information from the method body.
*/
final Iterable<ExceptionInfo> exceptions;
int max_stack;
int max_local;
int max_scope;
int max_slot;
/**
* Set if we encounter a newclass instruction.
*/
boolean hasNewclass = false;
/**
* Stack depths on entry to blocks
*/
Map<IBasicBlock, Integer> stkin = new HashMap<IBasicBlock, Integer>();
/**
* Scope depths on entry to blocks
*/
Map<IBasicBlock, Integer> scpin = new HashMap<IBasicBlock, Integer>();
int stkdepth = 0;
int scpdepth = 0;
int instructionIndex;
IBasicBlock currentBlock;
/**
* Visit a new Block.
*
* @param b - the Block to visit.
* @return true if the walker should continue visiting the block.
*/
public boolean visitBlock(IBasicBlock b)
{
this.currentBlock = b;
if (this.cfg.isCatchTarget(b))
{
stkdepth = 1;
scpdepth = 0;
}
else if (stkin.containsKey(b))
{
stkdepth = stkin.get(b);
scpdepth = scpin.get(b);
}
// else use the current values.
return true;
}
/**
* Visit an Instruction within the most recently-visited Block.
*
* @param i - the Instruction.
*/
public void visitInstruction(Instruction i)
{
switch (i.opcode)
{
case OP_add:
case OP_add_i:
case OP_astypelate:
case OP_bitand:
case OP_bitor:
case OP_bitxor:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_applytype:
case OP_astype:
case OP_bitnot:
case OP_bkpt:
case OP_bkptline:
case OP_checkfilter:
case OP_coerce:
case OP_coerce_a:
case OP_coerce_i:
case OP_coerce_d:
case OP_coerce_s:
case OP_coerce_u:
case OP_convert_b:
case OP_convert_d:
case OP_convert_i:
case OP_convert_o:
case OP_convert_s:
case OP_convert_u:
case OP_debug:
case OP_debugfile:
case OP_debugline:
case OP_declocal:
case OP_declocal_i:
case OP_decrement:
case OP_decrement_i:
case OP_dxns:
case OP_esc_xattr:
case OP_esc_xelem:
case OP_inclocal:
case OP_inclocal_i:
case OP_increment:
case OP_increment_i:
case OP_jump:
case OP_kill:
case OP_label:
case OP_negate:
case OP_negate_i:
case OP_returnvoid:
case OP_nop:
case OP_not:
case OP_swap:
case OP_timestamp:
case OP_typeof:
case OP_unplus:
// Net effect zero.
break;
case OP_call:
stkdepth = adjustValueStack(stkdepth, -(i.getImmediate() + 1));
break;
case OP_callmethod:
assert false : "internal only instruction!";
stkdepth = adjustValueStack(stkdepth, -i.getImmediate());
break;
case OP_callstatic:
stkdepth = adjustValueStack(stkdepth, -((Integer)i.getOperand(1)));
break;
case OP_callproperty:
case OP_callproplex:
case OP_callsuper:
case OP_constructprop:
stkdepth = adjustValueStack(stkdepth, -((Integer)i.getOperand(1)) + runtimeNameAllowance((Name)i.getOperand(0)));
break;
case OP_callpropvoid:
case OP_callsupervoid:
// void calls do not push a result
stkdepth = adjustValueStack(stkdepth, -((Integer)i.getOperand(1)) + runtimeNameAllowance((Name)i.getOperand(0)) - 1);
break;
case OP_construct:
stkdepth = adjustValueStack(stkdepth, -i.getImmediate());
break;
case OP_constructsuper:
stkdepth = adjustValueStack(stkdepth, -i.getImmediate() - 1);
break;
case OP_deleteproperty:
stkdepth = adjustValueStack(stkdepth, runtimeNameAllowance((Name)i.getOperand(0)));
break;
case OP_divide:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_dup:
stkdepth = adjustValueStack(stkdepth, 1);
break;
case OP_dxnslate:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_equals:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_finddef:
case OP_findproperty:
case OP_findpropstrict:
stkdepth = adjustValueStack(stkdepth, 1 + runtimeNameAllowance((Name)i.getOperand(0)));
break;
case OP_getdescendants:
case OP_getproperty:
case OP_getsuper:
stkdepth = adjustValueStack(stkdepth, runtimeNameAllowance((Name)i.getOperand(0)));
break;
case OP_getglobalscope:
case OP_getglobalslot:
case OP_getlex:
case OP_getouterscope:
stkdepth = adjustValueStack(stkdepth, 1);
break;
case OP_getlocal:
stkdepth = adjustValueStack(stkdepth, 1);
adjustMaxLocal(i.getImmediate());
if (i.getImmediate() < 4)
i.opcode = OP_getlocal0 + i.getImmediate();
break;
case OP_getlocal0:
case OP_getlocal1:
case OP_getlocal2:
case OP_getlocal3:
stkdepth = adjustValueStack(stkdepth, 1);
adjustMaxLocal(i.opcode - OP_getlocal0);
break;
case OP_getslot:
if (i.getImmediate() > max_slot)
max_slot = i.getImmediate();
break;
case OP_getscopeobject:
stkdepth = adjustValueStack(stkdepth, 1);
break;
case OP_greaterequals:
case OP_greaterthan:
case OP_hasnext:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_hasnext2:
{
// Both hasnext2 operands are locals.
adjustMaxLocal((Integer)i.getOperand(0));
adjustMaxLocal((Integer)i.getOperand(1));
stkdepth = adjustValueStack(stkdepth, 1);
break;
}
case OP_ifeq:
case OP_ifge:
case OP_ifgt:
case OP_ifle:
case OP_iflt:
case OP_ifnge:
case OP_ifngt:
case OP_ifnle:
case OP_ifnlt:
case OP_ifne:
case OP_ifstricteq:
case OP_ifstrictne:
stkdepth = adjustValueStack(stkdepth, -2);
break;
case OP_iffalse:
case OP_iftrue:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_in:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_initproperty:
stkdepth = adjustValueStack(stkdepth, -2 + runtimeNameAllowance((Name)i.getOperand(0)));
break;
case OP_instanceof:
case OP_istypelate:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_istype:
stkdepth = adjustValueStack(stkdepth, runtimeNameAllowance((Name)i.getOperand(0)));
break;
case OP_lessequals:
case OP_lessthan:
case OP_lookupswitch:
case OP_lshift:
case OP_modulo:
case OP_multiply:
case OP_multiply_i:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_newactivation:
case OP_newcatch:
case OP_newfunction:
stkdepth = adjustValueStack(stkdepth, 1);
break;
case OP_newarray:
stkdepth = adjustValueStack(stkdepth, 1 - i.getImmediate());
break;
case OP_newobject:
// The operands are name-value pairs.
stkdepth = adjustValueStack(stkdepth, 1 - (i.getImmediate() * 2));
break;
case OP_nextname:
case OP_nextvalue:
case OP_pop:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_popscope:
scpdepth = adjustScopeStack(scpdepth, -1);
break;
case OP_pushbyte:
case OP_pushdouble:
case OP_pushfalse:
case OP_pushint:
case OP_pushnamespace:
case OP_pushnan:
case OP_pushnull:
case OP_pushshort:
case OP_pushstring:
case OP_pushtrue:
case OP_pushuint:
case OP_pushundefined:
stkdepth = adjustValueStack(stkdepth, 1);
break;
case OP_pushscope:
case OP_pushwith:
stkdepth = adjustValueStack(stkdepth, -1);
scpdepth = adjustScopeStack(scpdepth, 1);
break;
case OP_returnvalue:
case OP_rshift:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_setlocal:
stkdepth = adjustValueStack(stkdepth, -1);
adjustMaxLocal(i.getImmediate());
if (i.getImmediate() < 4)
i.opcode = OP_setlocal0 + i.getImmediate();
break;
case OP_setlocal0:
case OP_setlocal1:
case OP_setlocal2:
case OP_setlocal3:
stkdepth = adjustValueStack(stkdepth, -1);
adjustMaxLocal(i.opcode - OP_setlocal0);
break;
case OP_setglobalslot:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_setproperty:
case OP_setsuper:
stkdepth = adjustValueStack(stkdepth, -2 + runtimeNameAllowance((Name)i.getOperand(0)));
break;
case OP_setslot:
stkdepth = adjustValueStack(stkdepth, -2);
if (max_slot < i.getImmediate())
max_slot = i.getImmediate();
break;
case OP_strictequals:
case OP_subtract:
case OP_subtract_i:
case OP_throw:
case OP_urshift:
stkdepth = adjustValueStack(stkdepth, -1);
break;
case OP_newclass:
this.hasNewclass = true;
break;
case OP_li8:
case OP_li16:
case OP_li32:
case OP_lf32:
case OP_lf64:
case OP_sxi1:
case OP_sxi8:
case OP_sxi16:
// consume one stack entry, and produce 1 stack entry.
break;
case OP_si8:
case OP_si16:
case OP_si32:
case OP_sf32:
case OP_sf64:
// consume two stack entries
stkdepth = adjustValueStack(stkdepth, -2);
break;
case OP_callinterface:
case OP_callsuperid:
case OP_deletepropertylate:
case OP_setpropertylate:
assert false : "internal only instruction!";
break;
default:
assert false : "unknown instruction!";
break;
}
this.instructionIndex++;
}
public void visitEnd(IBasicBlock b)
{
for (IBasicBlock s : b.getSuccessors())
{
if (!stkin.containsKey(s))
{
stkin.put(s, stkdepth);
scpin.put(s, scpdepth);
}
}
}
private int adjustValueStack(int stkdepth, int incr)
{
stkdepth += incr;
if (stkdepth < 0)
this.diagnosticsVisitor.operandStackUnderflow(this.mbi, this.cfg, this.currentBlock, instructionIndex);
if (stkdepth > this.max_stack)
this.max_stack = stkdepth;
return stkdepth;
}
private int adjustScopeStack(int scpdepth, int incr)
{
scpdepth += incr;
if (scpdepth < 0)
this.diagnosticsVisitor.scopeStackUnderflow(this.mbi, this.cfg, this.currentBlock, instructionIndex);
if (scpdepth > (this.max_scope - this.initial_scope))
{
this.max_scope = scpdepth + this.initial_scope;
}
return scpdepth;
}
private void adjustMaxLocal(int idx)
{
if (max_local <= idx)
max_local = idx + 1;
}
/**
* Examine a Name and compute the number of value stack elements it will
* need in its evaluation.
*
* @param operand - the runtime name. May be null if the operation can
* function without a name operand.
* @return the number of value stack elements evaluating this Name requires.
*/
private int runtimeNameAllowance(Object operand)
{
return operand instanceof Name ?
-((Name)operand).runtimeNameAllowance() :
0;
}
public boolean hasNewclassInstruction()
{
return this.hasNewclass;
}
}