All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraphBuilder Maven / Gradle / Ivy

There is a newer version: 1.6.7
Show newest version
/*
 * 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 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.ir.ssa.AstPropertyRead;
import com.ibm.wala.cast.ir.ssa.AstPropertyWrite;
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.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.collections.Iterator2Iterable;
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 {@code Object} and {@code 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 < normalInstructions.length; ++i) if (normalInstructions[i] != null) { visitor.instructionIndex = i; normalInstructions[i].visit(visitor); } // now visit phis and catches visitor.instructionIndex = -1; for (SSAInstruction inst : Iterator2Iterable.make(ir.iteratePhis())) inst.visit(visitor); for (SSAInstruction inst : Iterator2Iterable.make(ir.iterateCatchInstructions())) inst.visit(visitor); } } // primitive functions that are treated specially private static final 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 < n; ++i) { VarVertex v = factory.makeVarVertex(func, phi.getUse(i)); flowgraph.addEdge(v, w); } } @Override public void visitPrototypeLookup(PrototypeLookup proto) { // treat it simply as an assignment flowgraph.addEdge( factory.makeVarVertex(func, proto.getUse(0)), factory.makeVarVertex(func, proto.getDef())); handleLexicalDef(proto.getDef()); } private void visitPut(int val, String propName) { Vertex v = factory.makeVarVertex(func, val), w = factory.makePropVertex(propName); flowgraph.addEdge(v, w); } @Override public void visitPut(SSAPutInstruction put) { visitPut(put.getVal(), put.getDeclaredField().getName().toString()); } @Override public void visitSetPrototype(SetPrototype instruction) { visitPut(instruction.getUse(1), "prototype"); } @Override public void visitAstGlobalWrite(AstGlobalWrite instruction) { String propName = instruction.getDeclaredField().getName().toString(); // hack to account for global variables assert propName.startsWith("global "); propName = propName.substring("global ".length()); visitPut(instruction.getVal(), propName); } @Override public void visitPropertyWrite(AstPropertyWrite pw) { int p = pw.getMemberRef(); if (symtab.isConstant(p)) { String pn = JSCallGraphUtil.simulateToStringForPropertyNames(symtab.getConstantValue(p)); Vertex v = factory.makeVarVertex(func, pw.getValue()), w = factory.makePropVertex(pn); flowgraph.addEdge(v, w); } } @Override public void visitAstLexicalWrite(AstLexicalWrite lw) { for (Access acc : lw.getAccesses()) { Vertex v = factory.makeVarVertex(func, acc.valueNumber), w = factory.makeLexicalAccessVertex(acc.variableDefiner, acc.variableName); flowgraph.addEdge(v, w); } } @Override public void visitGet(SSAGetInstruction get) { String propName = get.getDeclaredField().getName().toString(); if (propName.startsWith("global ")) propName = propName.substring("global ".length()); Vertex v = factory.makePropVertex(propName), w = factory.makeVarVertex(func, get.getDef()); flowgraph.addEdge(v, w); handleLexicalDef(get.getDef()); } @Override public void visitAstGlobalRead(AstGlobalRead instruction) { if (supportFullPointerAnalysis && instruction .getGlobalName() .endsWith(JSSSAPropagationCallGraphBuilder.GLOBAL_OBJ_VAR_NAME)) { Vertex lval = factory.makeVarVertex(func, instruction.getDef()); flowgraph.addEdge(factory.global(), lval); } else { visitGet(instruction); } } @Override public void visitPropertyRead(AstPropertyRead pr) { int p = pr.getMemberRef(); if (symtab.isConstant(p)) { String pn = JSCallGraphUtil.simulateToStringForPropertyNames(symtab.getConstantValue(p)); Vertex v = factory.makePropVertex(pn), w = factory.makeVarVertex(func, pr.getDef()); flowgraph.addEdge(v, w); } IntSet argVns = Util.getArgumentsArrayVns(ir, du); if (argVns.contains(pr.getObjectRef())) { Vertex v = factory.makeArgVertex(func), w = factory.makeVarVertex(func, pr.getDef()); flowgraph.addEdge(v, w); } handleLexicalDef(pr.getDef()); } @Override public void visitAstLexicalRead(AstLexicalRead lr) { for (Access acc : lr.getAccesses()) { Vertex v = factory.makeLexicalAccessVertex(acc.variableDefiner, acc.variableName), w = factory.makeVarVertex(func, acc.valueNumber); flowgraph.addEdge(v, w); handleLexicalDef(acc.valueNumber); } } @Override public void visitReturn(SSAReturnInstruction ret) { if (ret.getResult() != -1) { // non-void return Vertex v = factory.makeVarVertex(func, ret.getResult()), w = factory.makeRetVertex(func); flowgraph.addEdge(v, w); } } @Override public void visitThrow(SSAThrowInstruction thr) { Vertex v = factory.makeVarVertex(func, thr.getException()), w = factory.makeUnknownVertex(); flowgraph.addEdge(v, w); } @Override public void visitGetCaughtException(SSAGetCaughtExceptionInstruction katch) { Vertex v = factory.makeUnknownVertex(), w = factory.makeVarVertex(func, katch.getDef()); flowgraph.addEdge(v, w); } @Override public void visitJavaScriptInvoke(JavaScriptInvoke invk) { flowgraph.addEdge( factory.makeUnknownVertex(), factory.makeVarVertex(func, invk.getException())); // check whether this invoke corresponds to a function expression/declaration // flow callee variable into callee vertex if (invk.getDeclaredTarget().equals(JavaScriptMethods.ctorReference)) { flowgraph.addEdge( factory.makeVarVertex(func, invk.getFunction()), factory.makeCallVertex(func, invk)); if (isFunctionConstructorInvoke(invk)) { // second parameter is function name String fn_name = symtab.getStringValue(invk.getUse(1)); // find the function being defined here IClass klass = cha.lookupClass(TypeReference.findOrCreate(JavaScriptTypes.jsLoader, fn_name)); if (klass == null) { System.err.println( "cannot find " + fn_name + " at " + ((AstMethod) ir.getMethod()) .getSourcePosition( ir.getCallInstructionIndices(invk.getCallSite()).intIterator().next())); return; } IMethod fn = klass.getMethod(AstMethodReference.fnSelector); FuncVertex fnVertex = factory.makeFuncVertex(klass); // function flows into its own v1 variable flowgraph.addEdge(fnVertex, factory.makeVarVertex(fnVertex, 1)); // flow parameters into local variables for (int i = 1; i < fn.getNumberOfParameters(); ++i) flowgraph.addEdge( factory.makeParamVertex(fnVertex, i), factory.makeVarVertex(fnVertex, i + 1)); // flow function into result variable flowgraph.addEdge(fnVertex, factory.makeVarVertex(func, invk.getDef())); } else if (supportFullPointerAnalysis) { CreationSiteVertex cs = factory.makeCreationSiteVertex(method, invk.iIndex(), JavaScriptTypes.Object); // flow creation site into result of new call flowgraph.addEdge(cs, factory.makeVarVertex(func, invk.getDef())); // also passed as 'this' to constructor if (invk.getNumberOfPositionalParameters() > 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())); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy