/****************************************************************************** * Copyright (c) 2002 - 2012 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *****************************************************************************/ package com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph; import java.util.Iterator; import com.ibm.wala.cast.ir.ssa.AstGlobalRead; import com.ibm.wala.cast.ir.ssa.AstGlobalWrite; import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access; import com.ibm.wala.cast.ir.ssa.AstLexicalRead; import com.ibm.wala.cast.ir.ssa.AstLexicalWrite; import com.ibm.wala.cast.js.callgraph.fieldbased.JSMethodInstructionVisitor; import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.CreationSiteVertex; import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.FuncVertex; import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.VarVertex; import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.Vertex; import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.VertexFactory; import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraphUtil; import com.ibm.wala.cast.js.ipa.callgraph.JSSSAPropagationCallGraphBuilder; import com.ibm.wala.cast.js.ssa.JavaScriptInvoke; import com.ibm.wala.cast.js.ssa.JavaScriptPropertyRead; import com.ibm.wala.cast.js.ssa.JavaScriptPropertyWrite; import com.ibm.wala.cast.js.ssa.PrototypeLookup; import com.ibm.wala.cast.js.ssa.SetPrototype; import com.ibm.wala.cast.js.types.JavaScriptMethods; import com.ibm.wala.cast.js.types.JavaScriptTypes; import com.ibm.wala.cast.js.util.Util; import com.ibm.wala.cast.loader.AstMethod; import com.ibm.wala.cast.loader.AstMethod.LexicalInformation; import com.ibm.wala.cast.types.AstMethodReference; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.ipa.callgraph.IAnalysisCacheView; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.ssa.IR; import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction; import com.ibm.wala.ssa.SSAGetInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSANewInstruction; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.ssa.SSAPutInstruction; import com.ibm.wala.ssa.SSAReturnInstruction; import com.ibm.wala.ssa.SSAThrowInstruction; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.intset.EmptyIntSet; import com.ibm.wala.util.intset.IntSet; /** * Class for building intra-procedural flow graphs for a given class hierarchy. * * @author mschaefer */ public class FlowGraphBuilder { private final IClassHierarchy cha; private final IAnalysisCacheView cache; private final boolean supportFullPointerAnalysis; public FlowGraphBuilder(IClassHierarchy cha, IAnalysisCacheView cache, boolean supportPointerAnalysis) { this.cha = cha; this.cache = cache; this.supportFullPointerAnalysis = supportPointerAnalysis; } /** * This is the main entry point of the flow graph builder. * *

* It creates a new, empty flow graph, adds nodes for a small number of special primitive * functions such as Object and Function and sets up flow * edges to make them flow into the corresponding global variables. Then it iterates over * all functions in the class hierarchy and all their IR instructions, and adds the * flow edges induced by these instructions. *

* * @return the completed flow graph */ public FlowGraph buildFlowGraph() { FlowGraph flowgraph = new FlowGraph(); addPrimitives(flowgraph); visitProgram(flowgraph); return flowgraph; } public void visitProgram(FlowGraph flowgraph) { for(IClass klass : cha) { for(IMethod method : klass.getDeclaredMethods()) { if(method.getDescriptor().equals(AstMethodReference.fnDesc)) { visitFunction(flowgraph, method); } } } } public void visitFunction(FlowGraph flowgraph, IMethod method) { { IR ir = cache.getIR(method); FlowGraphSSAVisitor visitor = new FlowGraphSSAVisitor(ir, flowgraph); // first visit normal instructions SSAInstruction[] normalInstructions = ir.getInstructions(); for(int i=0;i iter=ir.iteratePhis();iter.hasNext();) iter.next().visit(visitor); for(Iterator iter=ir.iterateCatchInstructions();iter.hasNext();) iter.next().visit(visitor); } } // primitive functions that are treated specially private static String[] primitiveFunctions = { "Object", "Function", "Array", "StringObject", "NumberObject", "BooleanObject", "RegExp" }; /** * Add flows from the special primitive functions to the corresponding global variables. * * @param flowgraph the flow graph under construction */ private void addPrimitives(FlowGraph flowgraph) { VertexFactory factory = flowgraph.getVertexFactory(); for(String pf : primitiveFunctions) { TypeReference typeref = TypeReference.findOrCreate(JavaScriptTypes.jsLoader, "L" + pf); IClass klass = cha.lookupClass(typeref); String prop = pf.endsWith("Object")? pf.substring(0, pf.length() - 6): pf; flowgraph.addEdge(factory.makeFuncVertex(klass), factory.makePropVertex(prop)); } } /** * Visitor class that does the heavy lifting (such as it is) of flow graph construction, adding flow graph * edges for every instruction in the method IR. * *

* The only slightly tricky thing are assignments to exposed variables inside their defining function. In * the IR, they initially appear as normal SSA variable assignments, without any indication of their lexical * nature. The normal call graph construction logic does something convoluted to fix this up later when * an actual lexical access is encountered. *

* *

* We use a much simpler approach. Whenever we see an assignment vi = e, we ask the * enclosing function whether vi is an exposed variable. If it is, we determine its * source-level names x1, x2, ..., xn, and then add edges * corresponding to lexical writes of vi into all the xj. *

* * @author mschaefer */ private class FlowGraphSSAVisitor extends JSMethodInstructionVisitor { // index of the instruction currently visited; -1 if the instruction isn't a normal instruction public int instructionIndex = -1; // flow graph being built private final FlowGraph flowgraph; // vertex factory to use for constructing new vertices private final VertexFactory factory; // lexical information about the current function private final LexicalInformation lexicalInfo; // the set of SSA variables in the current function that are accessed by nested functions private final IntSet exposedVars; // the IR of the current function private final IR ir; // the function vertex corresponding to the current function private final FuncVertex func; public FlowGraphSSAVisitor(IR ir, FlowGraph flowgraph) { super(ir.getMethod(), ir.getSymbolTable(), cache.getDefUse(ir)); this.ir = ir; this.flowgraph = flowgraph; this.factory = flowgraph.getVertexFactory(); this.func = factory.makeFuncVertex(ir.getMethod().getDeclaringClass()); if(method instanceof AstMethod) { this.lexicalInfo = ((AstMethod)method).lexicalInfo(); this.exposedVars = lexicalInfo.getAllExposedUses(); } else { this.lexicalInfo = null; this.exposedVars = EmptyIntSet.instance; } } // add extra flow from v_def to every lexical variable it may correspond to at source-level private void handleLexicalDef(int def) { assert def != -1; if(instructionIndex != -1 && exposedVars.contains(def)) { VarVertex v = factory.makeVarVertex(func, def); for(String localName : ir.getLocalNames(instructionIndex, def)) flowgraph.addEdge(v, factory.makeLexicalAccessVertex(lexicalInfo.getScopingName(), localName)); } } @Override public void visitPhi(SSAPhiInstruction phi) { int n = phi.getNumberOfUses(); VarVertex w = factory.makeVarVertex(func, phi.getDef()); for(int i=0;i 1) { flowgraph.addEdge(cs, factory.makeVarVertex(func, invk.getUse(0))); } } } else { // check whether it is a method call if(invk.getDeclaredTarget().equals(JavaScriptMethods.dispatchReference)) { // we only handle method calls with constant names if(symtab.isConstant(invk.getFunction())) { String pn = JSCallGraphUtil.simulateToStringForPropertyNames(symtab.getConstantValue(invk.getFunction())); // flow callee property into callee vertex flowgraph.addEdge(factory.makePropVertex(pn), factory.makeCallVertex(func, invk)); } } else { // this case is simpler: just flow callee variable into callee vertex flowgraph.addEdge(factory.makeVarVertex(func, invk.getFunction()), factory.makeCallVertex(func, invk)); } } handleLexicalDef(invk.getDef()); } @Override public void visitNew(SSANewInstruction invk) { if (supportFullPointerAnalysis) { // special case for supporting full pointer analysis // some core objects in the prologue (and the arguments array objects) get created with 'new' CreationSiteVertex cs = factory.makeCreationSiteVertex(method, invk.iindex, invk.getConcreteType()); // flow creation site into result of new call flowgraph.addEdge(cs, factory.makeVarVertex(func, invk.getDef())); } } } }