/* * * 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 static org.apache.flex.abc.ABCConstants.*; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import org.apache.flex.abc.graph.IBasicBlock; import org.apache.flex.abc.graph.IFlowgraph; import org.apache.flex.abc.semantics.Instruction; import org.apache.flex.abc.semantics.Label; import org.apache.flex.abc.semantics.MethodBodyInfo; import org.apache.flex.abc.semantics.Name; import org.apache.flex.abc.visitors.IDiagnosticsVisitor; import org.apache.flex.abc.visitors.IFlowGraphVisitor; /** * The FrameModelEncoder abstracts ABC instructions into * a sequence of operations on the method's "frame" * (i.e., its locals, scope stack, and value stack). */ public class FrameModelEncoder implements IFlowGraphVisitor { /** * Construct a new FrameModelEncoder. * @param mbi - the MethodBodyInfo to analyze. * @param visitor - the FrameModelVisitor analyzing the method. * @param diagnosticsVisitor - a sink for diagnostics. */ public FrameModelEncoder(MethodBodyInfo mbi, FrameModelVisitor<?> visitor, IDiagnosticsVisitor diagnosticsVisitor) { this.cfg = mbi.getCfg(); this.visitor = visitor; } private Set<IBasicBlock> visitedBlocks = new HashSet<IBasicBlock>(); /** * The visitor this encoder is driving. */ private final FrameModelVisitor<?> visitor; /** * The method's control flow graph, denormalized from its */ private final IFlowgraph cfg; /** * Visit a Block. * @param b - the IBasicBlock to visit. * @return the visitor's decision re: visiting the block. */ @Override public boolean visitBlock(IBasicBlock b) { this.instructionIndex = 0; return this.visitedBlocks.add(b) && this.visitor.visitBlock(b); } /** * The index of the current instruction in the current block. * This is maintained as a convenience for visitor clients. */ private int instructionIndex = -1; /** * Get the index of the current instruction in the current block. * @return the index of the current instruction in the current block. */ public int getInstructionIndex() { return this.instructionIndex; } /** * Visit an Instruction within the most recently-visited Block. * @param i - the Instruction. */ @Override public void visitInstruction(Instruction i) { switch (i.getOpcode()) { case OP_iflt: case OP_ifle: case OP_ifnlt: case OP_ifnle: case OP_ifgt: case OP_ifge: case OP_ifngt: case OP_ifnge: case OP_ifeq: case OP_ifstricteq: case OP_ifne: case OP_ifstrictne: visitor.consumeValue(i, 2); visitor.branch(i, cfg.getBlock(i.getTarget())); break; case OP_iftrue: case OP_iffalse: visitor.consumeValue(i, 1); visitor.branch(i, cfg.getBlock(i.getTarget())); break; case OP_jump: visitor.branch(i, cfg.getBlock(i.getTarget())); break; case OP_lookupswitch: { visitor.consumeValue(i,1); ArrayList<IBasicBlock> targets = new ArrayList<IBasicBlock>(); for ( int j = 0; j < i.getOperandCount(); j++ ) if( i.getOperand(j) instanceof Label ) { targets.add(cfg.getBlock((Label)i.getOperand(j))); } visitor.multiwayBranch(i, targets); } break; case OP_throw: visitor.consumeValue(i, 1); break; case OP_returnvalue: visitor.consumeValue(i, 1); break; case OP_returnvoid: visitor.noFrameEffect(i); break; case OP_pushnull: visitor.produceValue(i); break; case OP_pushundefined: visitor.produceValue(i); break; case OP_pushtrue: case OP_pushfalse: visitor.produceValue(i); break; case OP_pushnan: visitor.produceValue(i); break; case OP_pushbyte: case OP_pushshort: case OP_pushint: visitor.produceValue(i); break; case OP_debugfile: visitor.noFrameEffect(i); break; case OP_dxns: visitor.noFrameEffect(i); break; case OP_dxnslate: visitor.consumeValue(i, 1); break; case OP_pushstring: visitor.produceValue(i); break; case OP_pushuint: visitor.produceValue(i); break; case OP_pushdouble: visitor.produceValue(i); break; case OP_pushnamespace: visitor.produceValue(i); break; case OP_setlocal0: visitor.setlocal(i, 0); break; case OP_setlocal1: visitor.setlocal(i, 1); break; case OP_setlocal2: visitor.setlocal(i, 2); break; case OP_setlocal3: visitor.setlocal(i, 3); break; case OP_setlocal: visitor.setlocal(i, i.getImmediate()); break; case OP_getlocal0: visitor.getlocal(i, 0); break; case OP_getlocal1: visitor.getlocal(i, 1); break; case OP_getlocal2: visitor.getlocal(i, 2); break; case OP_getlocal3: visitor.getlocal(i, 3); break; case OP_getlocal: visitor.getlocal(i, i.getImmediate()); break; case OP_kill: visitor.modifyLocal(i, i.getImmediate()); break; case OP_inclocal: case OP_inclocal_i: case OP_declocal: case OP_declocal_i: visitor.modifyLocal(i, i.getImmediate()); break; case OP_newfunction: visitor.produceValue(i); break; case OP_getlex: visitor.produceValue(i); break; case OP_findpropstrict: case OP_findproperty: visitor.consumeAndProduceValue(i, runtimeNameAllowance(i.getOperand(0))); break; case OP_newclass: visitor.consumeAndProduceValue(i, 1); break; case OP_finddef: visitor.consumeAndProduceValue(i, runtimeNameAllowance(i.getOperand(0))); break; case OP_setproperty: case OP_initproperty: visitor.consumeValue(i, 2 + runtimeNameAllowance(i.getOperand(0))); break; case OP_getproperty: visitor.consumeAndProduceValue(i, 1 + runtimeNameAllowance(i.getOperand(0))); break; case OP_getdescendants: visitor.consumeAndProduceValue(i, 1 + runtimeNameAllowance(i.getOperand(0))); break; case OP_checkfilter: visitor.consumeAndProduceValue(i, 1); break; case OP_deleteproperty: visitor.consumeAndProduceValue(i, 1 + runtimeNameAllowance(i.getOperand(0))); break; case OP_astype: visitor.consumeAndProduceValue(i, 1); break; case OP_astypelate: visitor.consumeAndProduceValue(i, 2); break; case OP_coerce: visitor.consumeAndProduceValue(i, 1); break; case OP_convert_b: case OP_coerce_b: visitor.consumeAndProduceValue(i, 1); break; case OP_coerce_o: visitor.consumeAndProduceValue(i, 1); break; case OP_coerce_a: visitor.consumeAndProduceValue(i, 1); break; case OP_convert_i: case OP_coerce_i: visitor.consumeAndProduceValue(i, 1); break; case OP_convert_u: case OP_coerce_u: visitor.consumeAndProduceValue(i, 1); break; case OP_convert_d: case OP_coerce_d: visitor.consumeAndProduceValue(i, 1); break; case OP_unplus: visitor.consumeAndProduceValue(i, 1); break; case OP_coerce_s: visitor.consumeAndProduceValue(i, 1); break; case OP_istype: visitor.consumeAndProduceValue(i, 1 + runtimeNameAllowance(i.getOperand(0))); break; case OP_istypelate: visitor.consumeAndProduceValue(i, 2); break; case OP_convert_o: visitor.consumeAndProduceValue(i, 1); break; case OP_convert_s: case OP_esc_xelem: case OP_esc_xattr: visitor.consumeAndProduceValue(i, 1); break; case OP_callstatic: { int argc = (Integer)i.getOperand(1); visitor.consumeAndProduceValue(i, argc+1); } break; case OP_call: visitor.consumeAndProduceValue(i, 2 + i.getImmediate()); break; case OP_construct: visitor.consumeAndProduceValue(i, 1 + i.getImmediate()); break; case OP_callmethod: visitor.consumeAndProduceValue(i, 1 + i.getImmediate()); break; case OP_callproperty: case OP_callproplex: visitor.consumeAndProduceValue(i, 1 + runtimeNameAllowance(i.getOperand(0)) + (Integer)i.getOperand(1)); break; case OP_callpropvoid: visitor.consumeValue(i, 1 + runtimeNameAllowance(i.getOperand(0)) + (Integer)i.getOperand(1)); break; case OP_constructprop: visitor.consumeAndProduceValue(i, 1 + runtimeNameAllowance(i.getOperand(0)) + (Integer)i.getOperand(1)); break; case OP_applytype: visitor.consumeAndProduceValue(i, 1); break; case OP_callsuper: visitor.consumeAndProduceValue(i, 1 + runtimeNameAllowance(i.getOperand(0)) + (Integer)i.getOperand(1)); break; case OP_callsupervoid: visitor.consumeValue(i, 1 + runtimeNameAllowance(i.getOperand(0)) + (Integer)i.getOperand(1)); break; case OP_getsuper: visitor.consumeAndProduceValue(i, 1 + runtimeNameAllowance(i.getOperand(0))); break; case OP_setsuper: visitor.consumeValue(i, 2 + runtimeNameAllowance(i.getOperand(0))); break; case OP_constructsuper: visitor.consumeAndProduceValue(i, 1 + i.getImmediate()); break; case OP_newobject: visitor.consumeAndProduceValue(i, 2*i.getImmediate()); break; case OP_newarray: visitor.consumeAndProduceValue(i, i.getImmediate()); break; case OP_pushscope: case OP_pushwith: visitor.moveValueToScopeStack(i); break; case OP_newactivation: visitor.produceValue(i); break; case OP_newcatch: visitor.produceValue(i); break; case OP_popscope: visitor.popscope(i); break; case OP_getscopeobject: visitor.getScopeobject(i, i.getImmediate()); break; case OP_getouterscope: // TODO: Need to model this correctly. visitor.getScopeobject(i, i.getImmediate()); break; case OP_getglobalscope: visitor.getScopeobject(i, 0); break; case OP_getglobalslot: visitor.produceValue(i); break; case OP_setglobalslot: visitor.consumeValue(i, 1); break; case OP_getslot: visitor.consumeAndProduceValue(i, 1); break; case OP_setslot: visitor.consumeValue(i, 2); break; case OP_pop: visitor.consumeValue(i, 1); break; case OP_dup: visitor.dup(i); break; case OP_swap: visitor.swap(i); break; case OP_lessthan: case OP_greaterthan: case OP_lessequals: case OP_greaterequals: case OP_equals: case OP_strictequals: case OP_instanceof: case OP_in: visitor.consumeAndProduceValue(i, 2); break; case OP_not: visitor.consumeAndProduceValue(i, 1); break; case OP_add: visitor.consumeAndProduceValue(i, 2); break; case OP_modulo: case OP_subtract: case OP_divide: case OP_multiply: visitor.consumeAndProduceValue(i, 2); break; case OP_negate: case OP_increment: case OP_decrement: visitor.consumeAndProduceValue(i, 1); break; case OP_increment_i: case OP_decrement_i: visitor.consumeAndProduceValue(i, 1); break; case OP_add_i: case OP_subtract_i: case OP_multiply_i: visitor.consumeAndProduceValue(i, 2); break; case OP_negate_i: visitor.consumeAndProduceValue(i, 1); break; case OP_bitand: case OP_bitor: case OP_bitxor: visitor.consumeAndProduceValue(i, 2); break; case OP_lshift: case OP_rshift: visitor.consumeAndProduceValue(i, 2); break; case OP_urshift: visitor.consumeAndProduceValue(i, 2); break; case OP_bitnot: visitor.consumeAndProduceValue(i, 1); break; case OP_typeof: visitor.consumeAndProduceValue(i, 1); break; case OP_nop: case OP_bkpt: case OP_bkptline: case OP_timestamp: case OP_debug: case OP_label: case OP_debugline: visitor.noFrameEffect(i); break; case OP_nextvalue: case OP_nextname: visitor.consumeAndProduceValue(i, 2); break; case OP_hasnext: visitor.consumeAndProduceValue(i, 2); break; case OP_hasnext2: visitor.hasnext2(i); visitor.produceValue(i); break; case OP_sxi1: case OP_sxi8: case OP_sxi16: visitor.consumeAndProduceValue(i, 1); break; case OP_li8: case OP_li16: case OP_li32: visitor.consumeAndProduceValue(i, 1); break; case OP_lf32: case OP_lf64: visitor.consumeAndProduceValue(i, 1); break; /* case OP_lf32x4: visitor.consumeAndProduceValue(i, 1); break; */ case OP_si8: case OP_si16: case OP_si32: case OP_sf32: case OP_sf64: visitor.consumeValue(i, 2); break; /* case OP_sf32x4: visitor.consumeValue(i, 2); break; */ case OP_callinterface: case OP_callsuperid: case OP_deletepropertylate: case OP_setpropertylate: assert false : "internal only instruction:" + i; //$NON-NLS-1$ break; default: assert false : "unknown instruction:" + i; //$NON-NLS-1$ break; } this.instructionIndex++; } @Override public void visitEnd(IBasicBlock b) { for ( IBasicBlock succ: b.getSuccessors() ) visitor.visitEdge(b, succ); visitor.visitEndBlock(b); } /** * 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; } }