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

com.ibm.wala.cast.js.ipa.callgraph.JSSSAPropagationCallGraphBuilder Maven / Gradle / Ivy

There is a newer version: 1.6.7
Show newest version
/*
 * Copyright (c) 2002 - 2006 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.ipa.callgraph;

import com.ibm.wala.analysis.typeInference.TypeInference;
import com.ibm.wala.cast.ipa.callgraph.AstSSAPropagationCallGraphBuilder;
import com.ibm.wala.cast.ipa.callgraph.GlobalObjectKey;
import com.ibm.wala.cast.ir.ssa.AstGlobalRead;
import com.ibm.wala.cast.ir.ssa.AstGlobalWrite;
import com.ibm.wala.cast.ir.ssa.AstIsDefinedInstruction;
import com.ibm.wala.cast.ir.ssa.CAstBinaryOp;
import com.ibm.wala.cast.ir.ssa.CAstUnaryOp;
import com.ibm.wala.cast.ir.ssa.EachElementHasNextInstruction;
import com.ibm.wala.cast.js.analysis.typeInference.JSTypeInference;
import com.ibm.wala.cast.js.ipa.callgraph.JSSSAPropagationCallGraphBuilder.JSPointerAnalysisImpl.JSImplicitPointsToSetVisitor;
import com.ibm.wala.cast.js.ssa.JSInstructionVisitor;
import com.ibm.wala.cast.js.ssa.JavaScriptCheckReference;
import com.ibm.wala.cast.js.ssa.JavaScriptInstanceOf;
import com.ibm.wala.cast.js.ssa.JavaScriptInvoke;
import com.ibm.wala.cast.js.ssa.JavaScriptTypeOfInstruction;
import com.ibm.wala.cast.js.ssa.JavaScriptWithRegion;
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.loader.AstMethod;
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.core.util.CancelRuntimeException;
import com.ibm.wala.core.util.strings.Atom;
import com.ibm.wala.fixpoint.AbstractOperator;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.IAnalysisCacheView;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.propagation.AbstractFieldPointerKey;
import com.ibm.wala.ipa.callgraph.propagation.ConcreteTypeKey;
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKeyFactory;
import com.ibm.wala.ipa.callgraph.propagation.LocalPointerKey;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
import com.ibm.wala.ipa.callgraph.propagation.PointerKeyFactory;
import com.ibm.wala.ipa.callgraph.propagation.PointsToMap;
import com.ibm.wala.ipa.callgraph.propagation.PointsToSetVariable;
import com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder;
import com.ibm.wala.ipa.callgraph.propagation.PropagationSystem;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrike.shrikeBT.BinaryOpInstruction;
import com.ibm.wala.shrike.shrikeBT.IBinaryOpInstruction.IOperator;
import com.ibm.wala.shrike.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.IRView;
import com.ibm.wala.ssa.SSAAbstractBinaryInstruction;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSABinaryOpInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAUnaryOpInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.MonitorUtil;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetAction;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.intset.MutableMapping;
import com.ibm.wala.util.intset.MutableSparseIntSet;
import com.ibm.wala.util.intset.OrdinalSet;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.Set;

/**
 * Specialized pointer analysis constraint generation for JavaScript.
 *
 * 

Global object handling

* * The global object is represented by a {@link GlobalObjectKey} stored in {@link #globalObject}. * {@link AstGlobalRead} and {@link AstGlobalWrite} instructions are treated as accessing properties * of the global object; see {@link JSConstraintVisitor#visitAstGlobalRead(AstGlobalRead)}, {@link * JSConstraintVisitor#visitAstGlobalWrite(AstGlobalWrite)}, and {@link * JSImplicitPointsToSetVisitor#visitAstGlobalRead(AstGlobalRead)}. Finally, we need to represent * direct flow of the global object to handle receiver argument semantics (see {@link * com.ibm.wala.cast.js.translator.RhinoToAstTranslator}). To do so, we create a reference to a * global named {@link #GLOBAL_OBJ_VAR_NAME}, which is handled specially in {@link * JSConstraintVisitor#visitAstGlobalRead(AstGlobalRead)}. */ @SuppressWarnings({"javadoc", "JavadocReference"}) public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraphBuilder { public static final boolean DEBUG_LEXICAL = false; public static final boolean DEBUG_TYPE_INFERENCE = false; /** name to be used internally to pass around the global object */ public static final String GLOBAL_OBJ_VAR_NAME = "__WALA__int3rnal__global"; private final GlobalObjectKey globalObject; @Override public GlobalObjectKey getGlobalObject(Atom language) { assert language.equals(JavaScriptTypes.jsName); return globalObject; } /** is field a direct (WALA-internal) reference to the global object? */ private static boolean directGlobalObjectRef(FieldReference field) { return field.getName().toString().endsWith(GLOBAL_OBJ_VAR_NAME); } private static FieldReference makeNonGlobalFieldReference(FieldReference field) { String nonGlobalFieldName = field.getName().toString().substring(7); field = FieldReference.findOrCreate( JavaScriptTypes.Root, Atom.findOrCreateUnicodeAtom(nonGlobalFieldName), JavaScriptTypes.Root); return field; } private URL scriptBaseURL; public URL getBaseURL() { return scriptBaseURL; } public void setBaseURL(URL url) { this.scriptBaseURL = url; } protected JSSSAPropagationCallGraphBuilder( AbstractRootMethod abstractRootMethod, AnalysisOptions options, IAnalysisCacheView cache, PointerKeyFactory pointerKeyFactory) { super(abstractRootMethod, options, cache, pointerKeyFactory); globalObject = new GlobalObjectKey(cha.lookupClass(JavaScriptTypes.Root)); } @Override protected boolean isConstantRef(SymbolTable symbolTable, int valueNumber) { if (valueNumber == -1) { return false; } return symbolTable.isConstant(valueNumber); } // /////////////////////////////////////////////////////////////////////////// // // language specialization interface // // /////////////////////////////////////////////////////////////////////////// @Override protected boolean useObjectCatalog() { return true; } @Override protected boolean isUncataloguedField(IClass type, String fieldName) { if (!type.getReference().equals(JavaScriptTypes.Object)) { return true; } return "prototype".equals(fieldName) || "constructor".equals(fieldName) || "arguments".equals(fieldName) || "class".equals(fieldName) || "$value".equals(fieldName) || "__proto__".equals(fieldName); } @Override protected AbstractFieldPointerKey fieldKeyForUnknownWrites(AbstractFieldPointerKey fieldKey) { // TODO: fix this. null is wrong. return null; // return ReflectedFieldPointerKey.mapped(new // ConcreteTypeKey(cha.lookupClass(JavaScriptTypes.String)), fieldKey.getInstanceKey()); } // /////////////////////////////////////////////////////////////////////////// // // top-level node constraint generation // // /////////////////////////////////////////////////////////////////////////// private static final FieldReference prototypeRef; static { FieldReference x = null; try { byte[] utf8 = "__proto__".getBytes("UTF-8"); x = FieldReference.findOrCreate( JavaScriptTypes.Root, Atom.findOrCreate(utf8, 0, utf8.length), JavaScriptTypes.Root); } catch (UnsupportedEncodingException e) { assert false; } prototypeRef = x; } public PointerKey getPointerKeyForGlobalVar(String varName) { FieldReference fieldRef = FieldReference.findOrCreate( JavaScriptTypes.Root, Atom.findOrCreateUnicodeAtom(varName), JavaScriptTypes.Root); IField f = cha.resolveField(fieldRef); assert f != null : "couldn't resolve " + varName; return getPointerKeyForInstanceField(getGlobalObject(JavaScriptTypes.jsName), f); } @Override protected ExplicitCallGraph createEmptyCallGraph(IMethod fakeRootClass, AnalysisOptions options) { return new JSCallGraph(fakeRootClass, options, getAnalysisCache()); } protected TypeInference makeTypeInference(IR ir, IClassHierarchy cha) { TypeInference ti = new JSTypeInference(ir, cha); if (DEBUG_TYPE_INFERENCE) { System.err.println(("IR of " + ir.getMethod())); System.err.println(ir); System.err.println(("TypeInference of " + ir.getMethod())); for (int i = 0; i <= ir.getSymbolTable().getMaxValueNumber(); i++) { if (ti.isUndefined(i)) { System.err.println((" value " + i + " is undefined")); } else { System.err.println((" value " + i + " has type " + ti.getType(i))); } } } return ti; } @Override protected void addAssignmentsForCatchPointerKey( PointerKey exceptionVar, Set catchClasses, PointerKey e) { system.newConstraint(exceptionVar, assignOperator, e); } public static class JSInterestingVisitor extends AstInterestingVisitor implements com.ibm.wala.cast.js.ssa.JSInstructionVisitor { public JSInterestingVisitor(int vn) { super(vn); } @Override public void visitBinaryOp(final SSABinaryOpInstruction instruction) { bingo = true; } @Override public void visitJavaScriptInvoke(JavaScriptInvoke instruction) { bingo = true; } @Override public void visitTypeOf(JavaScriptTypeOfInstruction inst) { bingo = true; } @Override public void visitJavaScriptInstanceOf(JavaScriptInstanceOf instruction) { bingo = true; } @Override public void visitCheckRef(JavaScriptCheckReference instruction) {} @Override public void visitWithRegion(JavaScriptWithRegion instruction) {} @Override public void visitSetPrototype(SetPrototype instruction) { bingo = true; } @Override public void visitPrototypeLookup(PrototypeLookup instruction) { bingo = true; } } @Override protected InterestingVisitor makeInterestingVisitor(CGNode node, int vn) { return new JSInterestingVisitor(vn); } // /////////////////////////////////////////////////////////////////////////// // // specialized pointer analysis // // /////////////////////////////////////////////////////////////////////////// public static class JSPointerAnalysisImpl extends AstSSAPropagationCallGraphBuilder.AstPointerAnalysisImpl { JSPointerAnalysisImpl( PropagationCallGraphBuilder builder, CallGraph cg, PointsToMap pointsToMap, MutableMapping instanceKeys, PointerKeyFactory pointerKeys, InstanceKeyFactory iKeyFactory) { super(builder, cg, pointsToMap, instanceKeys, pointerKeys, iKeyFactory); } public static class JSImplicitPointsToSetVisitor extends AstImplicitPointsToSetVisitor implements com.ibm.wala.cast.js.ssa.JSInstructionVisitor { public JSImplicitPointsToSetVisitor(AstPointerAnalysisImpl analysis, LocalPointerKey lpk) { super(analysis, lpk); } @Override public void visitJavaScriptInvoke(JavaScriptInvoke instruction) {} @Override public void visitTypeOf(JavaScriptTypeOfInstruction instruction) {} @Override public void visitJavaScriptInstanceOf(JavaScriptInstanceOf instruction) {} @Override public void visitCheckRef(JavaScriptCheckReference instruction) {} @Override public void visitWithRegion(JavaScriptWithRegion instruction) {} @Override public void visitAstGlobalRead(AstGlobalRead instruction) { JSPointerAnalysisImpl jsAnalysis = (JSPointerAnalysisImpl) analysis; FieldReference field = makeNonGlobalFieldReference(instruction.getDeclaredField()); assert !directGlobalObjectRef(field); IField f = jsAnalysis.builder.getCallGraph().getClassHierarchy().resolveField(field); assert f != null; MutableSparseIntSet S = MutableSparseIntSet.makeEmpty(); InstanceKey globalObj = ((AstSSAPropagationCallGraphBuilder) jsAnalysis.builder) .getGlobalObject(JavaScriptTypes.jsName); PointerKey fkey = analysis.getHeapModel().getPointerKeyForInstanceField(globalObj, f); if (fkey != null) { OrdinalSet pointees = analysis.getPointsToSet(fkey); IntSet set = pointees.getBackingSet(); if (set != null) { S.addAll(set); } } pointsToSet = new OrdinalSet<>(S, analysis.getInstanceKeyMapping()); } @Override public void visitSetPrototype(SetPrototype instruction) {} @Override public void visitPrototypeLookup(PrototypeLookup instruction) {} } @Override protected ImplicitPointsToSetVisitor makeImplicitPointsToVisitor(LocalPointerKey lpk) { return new JSImplicitPointsToSetVisitor(this, lpk); } } @Override protected PropagationSystem makeSystem(AnalysisOptions options) { return new PropagationSystem(callGraph, pointerKeyFactory, instanceKeyFactory) { @Override public PointerAnalysis makePointerAnalysis(PropagationCallGraphBuilder builder) { return new JSPointerAnalysisImpl( builder, cg, pointsToMap, instanceKeys, pointerKeyFactory, instanceKeyFactory); } }; } // /////////////////////////////////////////////////////////////////////////// // // IR visitor specialization for JavaScript // // /////////////////////////////////////////////////////////////////////////// @Override public JSConstraintVisitor makeVisitor(CGNode node) { if (AstSSAPropagationCallGraphBuilder.DEBUG_PROPERTIES) { final IMethod method = node.getMethod(); if (method instanceof AstMethod) { System.err.println("\n\nNode: " + node); final IR ir = node.getIR(); System.err.println("Position: " + getSomePositionForMethod(ir, (AstMethod) method)); // System.err.println(ir); } } return new JSConstraintVisitor(this, node); } public static Position getSomePositionForMethod(IR ir, AstMethod method) { SSAInstruction[] instructions = ir.getInstructions(); for (int i = 0; i < instructions.length; i++) { Position p = method.getSourcePosition(i); if (p != null) { return p; } } return null; } public static class JSConstraintVisitor extends AstConstraintVisitor implements JSInstructionVisitor { public JSConstraintVisitor(AstSSAPropagationCallGraphBuilder builder, CGNode node) { super(builder, node); } @Override public void visitUnaryOp(SSAUnaryOpInstruction inst) { if (inst.getOpcode() == IUnaryOpInstruction.Operator.NEG) { addLvalTypeKeyConstraint(inst, JavaScriptTypes.Boolean); } else if (inst.getOpcode() == CAstUnaryOp.MINUS) { addLvalTypeKeyConstraint(inst, JavaScriptTypes.Number); } } /** add a constraint indicating that the value def'd by inst can point to a value of type t */ private void addLvalTypeKeyConstraint(SSAInstruction inst, TypeReference t) { int lval = inst.getDef(0); PointerKey lk = getPointerKeyForLocal(lval); IClass bool = getClassHierarchy().lookupClass(t); InstanceKey key = new ConcreteTypeKey(bool); system.newConstraint(lk, key); } @Override public void visitIsDefined(AstIsDefinedInstruction inst) { addLvalTypeKeyConstraint(inst, JavaScriptTypes.Boolean); } @Override public void visitJavaScriptInstanceOf(JavaScriptInstanceOf inst) { addLvalTypeKeyConstraint(inst, JavaScriptTypes.Boolean); } @Override public void visitEachElementHasNext(EachElementHasNextInstruction inst) { addLvalTypeKeyConstraint(inst, JavaScriptTypes.Boolean); } @Override public void visitTypeOf(JavaScriptTypeOfInstruction instruction) { addLvalTypeKeyConstraint(instruction, JavaScriptTypes.String); } @Override public void visitAstGlobalRead(AstGlobalRead instruction) { int lval = instruction.getDef(); FieldReference field = makeNonGlobalFieldReference(instruction.getDeclaredField()); PointerKey def = getPointerKeyForLocal(lval); assert def != null; IField f = getClassHierarchy().resolveField(field); assert f != null : "could not resolve referenced global " + field; if (hasNoInterestingUses(lval)) { system.recordImplicitPointsToSet(def); } else { InstanceKey globalObj = getBuilder().getGlobalObject(JavaScriptTypes.jsName); if (directGlobalObjectRef(field)) { // points-to set is just the global object system.newConstraint(def, globalObj); } else { system.findOrCreateIndexForInstanceKey(globalObj); PointerKey p = getPointerKeyForInstanceField(globalObj, f); system.newConstraint(def, assignOperator, p); } } } @Override public void visitAstGlobalWrite(AstGlobalWrite instruction) { int rval = instruction.getVal(); FieldReference field = makeNonGlobalFieldReference(instruction.getDeclaredField()); IField f = getClassHierarchy().resolveField(field); assert f != null : "could not resolve referenced global " + field; assert !f.getFieldTypeReference().isPrimitiveType(); InstanceKey globalObj = getBuilder().getGlobalObject(JavaScriptTypes.jsName); system.findOrCreateIndexForInstanceKey(globalObj); PointerKey p = getPointerKeyForInstanceField(globalObj, f); PointerKey rvalKey = getPointerKeyForLocal(rval); if (contentsAreInvariant(symbolTable, du, rval)) { system.recordImplicitPointsToSet(rvalKey); InstanceKey[] ik = getInvariantContents(rval); for (InstanceKey element : ik) { system.newConstraint(p, element); } } else { system.newConstraint(p, assignOperator, rvalKey); } } @Override public void visitBinaryOp(final SSABinaryOpInstruction instruction) { handleBinaryOp(instruction, node, symbolTable, du); } @Override public void visitJavaScriptInvoke(JavaScriptInvoke instruction) { if (instruction.getDeclaredTarget().equals(JavaScriptMethods.dispatchReference)) { handleJavascriptDispatch(instruction); } else { visitInvokeInternal(instruction, new DefaultInvariantComputer()); } } private void handleJavascriptDispatch( final JavaScriptInvoke instruction, final InstanceKey receiverType) { int functionVn = instruction.getFunction(); ReflectedFieldAction fieldDispatchAction = new ReflectedFieldAction() { @Override public void action(final AbstractFieldPointerKey fieldKey) { class FieldValueDispatch extends UnaryOperator { private JavaScriptInvoke getInstruction() { return instruction; } private InstanceKey getReceiver() { return receiverType; } private AbstractFieldPointerKey getProperty() { return fieldKey; } private CGNode getNode() { return node; } private final MutableIntSet previous = IntSetUtil.make(); @Override public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable ptrs) { if (ptrs.getValue() != null) { ptrs.getValue() .foreachExcluding( previous, x -> { final InstanceKey functionObj = system.getInstanceKey(x); visitInvokeInternal( instruction, new DefaultInvariantComputer() { @Override public InstanceKey[][] computeInvariantParameters( SSAAbstractInvokeInstruction call) { InstanceKey[][] x = super.computeInvariantParameters(call); if (x == null) { x = new InstanceKey[call.getNumberOfUses()][]; } x[0] = new InstanceKey[] {functionObj}; x[1] = new InstanceKey[] {receiverType}; return x; } }); }); previous.addAll(ptrs.getValue()); } return NOT_CHANGED; } @Override public int hashCode() { return instruction.hashCode() * fieldKey.hashCode() * receiverType.hashCode(); } @Override public boolean equals(Object o) { return o instanceof FieldValueDispatch && ((FieldValueDispatch) o).getNode().equals(node) && ((FieldValueDispatch) o).getInstruction() == instruction && ((FieldValueDispatch) o).getProperty().equals(fieldKey) && ((FieldValueDispatch) o).getReceiver().equals(receiverType); } @Override public String toString() { return "sub-dispatch for " + instruction + ": " + receiverType + ", " + fieldKey; } } system.newSideEffect(new FieldValueDispatch(), fieldKey); } @Override public void dump( AbstractFieldPointerKey fieldKey, boolean constObj, boolean constProp) { System.err.println( "dispatch to " + receiverType + '.' + fieldKey + " for " + instruction); } }; TransitivePrototypeKey prototypeObjs = new TransitivePrototypeKey(receiverType); InstanceKey[] objKeys = new InstanceKey[] {receiverType}; if (contentsAreInvariant(symbolTable, du, functionVn)) { InstanceKey[] fieldsKeys = getInvariantContents(functionVn); newFieldOperationObjectAndFieldConstant(true, fieldDispatchAction, objKeys, fieldsKeys); newFieldOperationOnlyFieldConstant(true, fieldDispatchAction, prototypeObjs, fieldsKeys); } else { PointerKey fieldKey = getPointerKeyForLocal(functionVn); newFieldOperationOnlyObjectConstant(true, fieldDispatchAction, fieldKey, objKeys); newFieldFullOperation(true, fieldDispatchAction, prototypeObjs, fieldKey); } } private void handleJavascriptDispatch(final JavaScriptInvoke instruction) { int receiverVn = instruction.getUse(1); PointerKey receiverKey = getPointerKeyForLocal(receiverVn); if (contentsAreInvariant(symbolTable, du, receiverVn)) { system.recordImplicitPointsToSet(receiverKey); InstanceKey[] ik = getInvariantContents(receiverVn); for (InstanceKey element : ik) { handleJavascriptDispatch(instruction, element); } } else { class ReceiverForDispatchOp extends UnaryOperator { final MutableIntSet previous = IntSetUtil.make(); private CGNode getNode() { return node; } private JavaScriptInvoke getInstruction() { return instruction; } @Override public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) { if (rhs.getValue() != null) { rhs.getValue() .foreachExcluding( previous, x -> { try { MonitorUtil.throwExceptionIfCanceled(getBuilder().monitor); } catch (CancelException e) { throw new CancelRuntimeException(e); } InstanceKey ik = system.getInstanceKey(x); handleJavascriptDispatch(instruction, ik); }); previous.addAll(rhs.getValue()); } return NOT_CHANGED; } @Override public int hashCode() { return instruction.hashCode() * node.hashCode(); } @Override public boolean equals(Object o) { return o instanceof ReceiverForDispatchOp && ((ReceiverForDispatchOp) o).getInstruction() == getInstruction() && ((ReceiverForDispatchOp) o).getNode() == getNode(); } @Override public String toString() { return "receiver for dispatch: " + instruction; } } system.newSideEffect(new ReceiverForDispatchOp(), receiverKey); } } // /////////////////////////////////////////////////////////////////////////// // // string manipulation handling for binary operators // // /////////////////////////////////////////////////////////////////////////// private void handleBinaryOp( final SSABinaryOpInstruction instruction, final CGNode node, final SymbolTable symbolTable, final DefUse du) { int lval = instruction.getDef(0); PointerKey lk = getPointerKeyForLocal(lval); final PointsToSetVariable lv = system.findOrCreatePointsToSet(lk); final int arg1 = instruction.getUse(0); final int arg2 = instruction.getUse(1); class BinaryOperator extends AbstractOperator { private CGNode getNode() { return node; } private SSAAbstractBinaryInstruction getInstruction() { return instruction; } @Override public String toString() { return "BinOp: " + getInstruction(); } @Override public int hashCode() { return 17 * getInstruction().getUse(0) * getInstruction().getUse(1); } @Override public boolean equals(Object o) { if (o instanceof BinaryOperator) { BinaryOperator op = (BinaryOperator) o; return op.getNode().equals(getNode()) && op.getInstruction().getUse(0) == getInstruction().getUse(0) && op.getInstruction().getUse(1) == getInstruction().getUse(1) && op.getInstruction().getDef(0) == getInstruction().getDef(0); } else { return false; } } private InstanceKey[] getInstancesArray(int vn) { if (contentsAreInvariant(symbolTable, du, vn)) { return getInvariantContents(vn); } else { PointsToSetVariable v = system.findOrCreatePointsToSet(getPointerKeyForLocal(vn)); if (v.getValue() == null || v.size() == 0) { return new InstanceKey[0]; } else { final Set temp = HashSetFactory.make(); v.getValue().foreach(keyIndex -> temp.add(system.getInstanceKey(keyIndex))); return temp.toArray(new InstanceKey[0]); } } } private boolean isStringConstant(InstanceKey k) { return (k instanceof ConstantKey) && k.getConcreteType().getReference().equals(JavaScriptTypes.String); } private boolean addKey(InstanceKey k) { int n = system.findOrCreateIndexForInstanceKey(k); return lv.add(n); } @Override public byte evaluate(PointsToSetVariable lhs, final PointsToSetVariable[] rhs) { boolean doDefault = false; byte changed = NOT_CHANGED; InstanceKey[] iks1 = getInstancesArray(arg1); InstanceKey[] iks2 = getInstancesArray(arg2); IOperator op = instruction.getOperator(); if ((op == BinaryOpInstruction.Operator.ADD) && getOptions().getTraceStringConstants()) { for (InstanceKey element : iks1) { if (isStringConstant(element)) { for (InstanceKey element2 : iks2) { if (isStringConstant(element2)) { try { MonitorUtil.throwExceptionIfCanceled(builder.monitor); } catch (CancelException e) { throw new CancelRuntimeException(e); } String v1 = (String) ((ConstantKey) element).getValue(); String v2 = (String) ((ConstantKey) element2).getValue(); if (!v1.contains(v2) && !v2.contains(v1)) { InstanceKey lvalKey = getInstanceKeyForConstant(v1 + v2); if (addKey(lvalKey)) { changed = CHANGED; } } else { doDefault = true; } } else { doDefault = true; } } } else { doDefault = true; } } } else { doDefault = true; } if (doDefault) { for (InstanceKey element : iks1) { for (InstanceKey element2 : iks2) { try { MonitorUtil.throwExceptionIfCanceled(builder.monitor); } catch (CancelException e) { throw new CancelRuntimeException(e); } if (handleBinaryOperatorArgs(element, element2)) { changed = CHANGED; } } } } if (iks1 == null || iks1.length == 0 || iks2 == null || iks2.length == 0) { if (iks1 != null) { for (InstanceKey ik : iks1) { if (addKey(ik)) { changed = CHANGED; } } } if (iks2 != null) { for (InstanceKey ik : iks2) { if (addKey(ik)) { changed = CHANGED; } } } } if (op == CAstBinaryOp.STRICT_EQ || op == CAstBinaryOp.STRICT_NE) { IClass bc = getClassHierarchy().lookupClass(JavaScriptTypes.Boolean); InstanceKey bk = new ConcreteTypeKey(bc); if (addKey(bk)) { changed = CHANGED; } } return changed; } private boolean isNumberType(Language l, TypeReference t) { return l.isDoubleType(t) || l.isFloatType(t) || l.isIntType(t) || l.isLongType(t); } protected boolean handleBinaryOperatorArgs(InstanceKey left, InstanceKey right) { Language l = node.getMethod().getDeclaringClass().getClassLoader().getLanguage(); if (l.isStringType(left.getConcreteType().getReference()) || l.isStringType(right.getConcreteType().getReference())) { return addKey( new ConcreteTypeKey(node.getClassHierarchy().lookupClass(l.getStringType()))); } else if (isNumberType(l, left.getConcreteType().getReference()) && isNumberType(l, right.getConcreteType().getReference())) { if (left instanceof ConstantKey && right instanceof ConstantKey) { return addKey( new ConcreteTypeKey( node.getClassHierarchy().lookupClass(JavaScriptTypes.Number))); } else { return addKey(left) || addKey(right); } } else { return false; } } } BinaryOperator B = new BinaryOperator(); if (contentsAreInvariant(symbolTable, du, arg1)) { if (contentsAreInvariant(symbolTable, du, arg2)) { B.evaluate(null, null); } else { system.newConstraint(lk, B, getPointerKeyForLocal(arg2)); } } else { PointerKey k1 = getPointerKeyForLocal(arg1); if (contentsAreInvariant(symbolTable, du, arg2)) { system.newConstraint(lk, B, k1); } else { system.newConstraint(lk, B, k1, getPointerKeyForLocal(arg2)); } } } @Override public void visitCheckRef(JavaScriptCheckReference instruction) { // TODO Auto-generated method stub } @Override public void visitWithRegion(JavaScriptWithRegion instruction) { // TODO Auto-generated method stub } private final UnaryOperator transitivePrototypeOp = new UnaryOperator<>() { @Override public byte evaluate(final PointsToSetVariable lhs, PointsToSetVariable rhs) { class Op implements IntSetAction { private boolean changed = false; @Override public void act(int x) { InstanceKey protoObj = system.getInstanceKey(x); PointerKey protoObjKey = new TransitivePrototypeKey(protoObj); changed |= system.newStatement( lhs, assignOperator, system.findOrCreatePointsToSet(protoObjKey), true, true); } } if (rhs.getValue() != null) { Op op = new Op(); rhs.getValue().foreach(op); return (op.changed ? CHANGED : NOT_CHANGED); } return NOT_CHANGED; } @Override public int hashCode() { return -896435647; } @Override public boolean equals(Object o) { return o == this; } @Override public String toString() { return "transitivePrototypeOp"; } }; @Override public void visitSetPrototype(SetPrototype instruction) { visitPutInternal(instruction.getUse(1), instruction.getUse(0), false, prototypeRef); assert contentsAreInvariant(symbolTable, du, instruction.getUse(0)); if (contentsAreInvariant(symbolTable, du, instruction.getUse(1))) { for (InstanceKey newObj : getInvariantContents(instruction.getUse(0))) { PointerKey newObjKey = new TransitivePrototypeKey(newObj); for (InstanceKey protoObj : getInvariantContents(instruction.getUse(1))) { system.newConstraint(newObjKey, protoObj); system.newConstraint(newObjKey, assignOperator, new TransitivePrototypeKey(protoObj)); } } } else { for (InstanceKey newObj : getInvariantContents(instruction.getUse(0))) { PointerKey newObjKey = new TransitivePrototypeKey(newObj); system.newConstraint( newObjKey, assignOperator, getPointerKeyForLocal(instruction.getUse(1))); system.newConstraint( newObjKey, transitivePrototypeOp, getPointerKeyForLocal(instruction.getUse(1))); } } } @Override public void visitPrototypeLookup(PrototypeLookup instruction) { if (contentsAreInvariant(symbolTable, du, instruction.getUse(0))) { for (InstanceKey rhsObj : getInvariantContents(instruction.getUse(0))) { // property can come from object itself... system.newConstraint(getPointerKeyForLocal(instruction.getDef(0)), rhsObj); // ...or prototype objects system.newConstraint( getPointerKeyForLocal(instruction.getDef(0)), assignOperator, new TransitivePrototypeKey(rhsObj)); } } else { // property can come from object itself... system.newConstraint( getPointerKeyForLocal(instruction.getDef(0)), assignOperator, getPointerKeyForLocal(instruction.getUse(0))); // ...or prototype objects system.newConstraint( getPointerKeyForLocal(instruction.getDef(0)), transitivePrototypeOp, getPointerKeyForLocal(instruction.getUse(0))); } } } // /////////////////////////////////////////////////////////////////////////// // // function call handling // // ////////////////////////////////////////////////////////////////////////// @Override protected void processCallingConstraints( CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target, InstanceKey[][] constParams, PointerKey uniqueCatchKey) { processCallingConstraintsInternal( this, caller, instruction, target, constParams, uniqueCatchKey); } @SuppressWarnings("unused") public static void processCallingConstraintsInternal( AstSSAPropagationCallGraphBuilder builder, CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target, InstanceKey[][] constParams, PointerKey uniqueCatchKey) { IRView sourceIR = builder.getCFAContextInterpreter().getIRView(caller); SymbolTable sourceST = sourceIR.getSymbolTable(); IRView targetIR = builder.getCFAContextInterpreter().getIRView(target); SymbolTable targetST = targetIR.getSymbolTable(); JSConstraintVisitor targetVisitor = null; int av = -1; for (int v = 0; v <= targetST.getMaxValueNumber(); v++) { String[] vns = targetIR.getLocalNames(1, v); for (int n = 0; vns != null && n < vns.length; n++) { if ("arguments".equals(vns[n])) { av = v; targetVisitor = (JSConstraintVisitor) builder.makeVisitor(target); break; } } } int paramCount = targetST.getParameterValueNumbers().length; int argCount = instruction.getNumberOfPositionalParameters(); // the first two arguments are the function object and the receiver, neither of which // should become part of the arguments array int num_pseudoargs = 2; // pass actual arguments to formals in the normal way for (int i = 0; i < Math.min(paramCount, argCount); i++) { InstanceKey[] fn = new InstanceKey[] { builder.getInstanceKeyForConstant( JavaScriptTypes.String, String.valueOf(i - num_pseudoargs)) }; PointerKey F = builder.getTargetPointerKey(target, i); if (constParams != null && constParams[i] != null) { for (int j = 0; j < constParams[i].length; j++) { builder.getSystem().newConstraint(F, constParams[i][j]); } if (av != -1 && i >= num_pseudoargs) { targetVisitor.newFieldWrite(target, av, fn, constParams[i]); } } else { PointerKey A = builder.getPointerKeyForLocal(caller, instruction.getUse(i)); builder .getSystem() .newConstraint( F, (F instanceof FilteredPointerKey) ? builder.filterOperator : assignOperator, A); if (av != -1 && i >= num_pseudoargs) { targetVisitor.newFieldWrite(target, av, fn, F); } } } // extra actual arguments get assigned into the ``arguments'' object if (paramCount < argCount) { if (av != -1) { for (int i = paramCount; i < argCount; i++) { InstanceKey[] fn = new InstanceKey[] { builder.getInstanceKeyForConstant( JavaScriptTypes.String, String.valueOf(i - num_pseudoargs)) }; if (constParams != null && constParams[i] != null && i >= num_pseudoargs) { targetVisitor.newFieldWrite(target, av, fn, constParams[i]); } else if (i >= num_pseudoargs) { PointerKey A = builder.getPointerKeyForLocal(caller, instruction.getUse(i)); targetVisitor.newFieldWrite(target, av, fn, A); } } } } // extra formal parameters get null (extra args are ignored here) else if (argCount < paramCount) { int nullvn = sourceST.getNullConstant(); DefUse sourceDU = builder.getCFAContextInterpreter().getDU(caller); InstanceKey[] nullkeys = builder.getInvariantContents(sourceST, sourceDU, caller, nullvn, builder); for (int i = argCount; i < paramCount; i++) { PointerKey F = builder.getPointerKeyForLocal(target, targetST.getParameter(i)); for (InstanceKey nullkey : nullkeys) { builder.getSystem().newConstraint(F, nullkey); } } } // write `length' in argument objects if (av != -1) { InstanceKey[] svn = new InstanceKey[] { builder.getInstanceKeyForConstant(JavaScriptTypes.Number, argCount - 1) }; InstanceKey[] lnv = new InstanceKey[] {builder.getInstanceKeyForConstant(JavaScriptTypes.String, "length")}; targetVisitor.newFieldWrite(target, av, lnv, svn); } // return values if (instruction.getDef(0) != -1) { PointerKey RF = builder.getPointerKeyForReturnValue(target); PointerKey RA = builder.getPointerKeyForLocal(caller, instruction.getDef(0)); builder.getSystem().newConstraint(RA, assignOperator, RF); } PointerKey EF = builder.getPointerKeyForExceptionalReturnValue(target); if (SHORT_CIRCUIT_SINGLE_USES && uniqueCatchKey != null) { // e has exactly one use. so, represent e implicitly builder.getSystem().newConstraint(uniqueCatchKey, assignOperator, EF); } else { PointerKey EA = builder.getPointerKeyForLocal(caller, instruction.getDef(1)); builder.getSystem().newConstraint(EA, assignOperator, EF); } } @Override protected boolean sameMethod(CGNode opNode, String definingMethod) { return definingMethod.equals( opNode.getMethod().getReference().getDeclaringClass().getName().toString()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy