com.ibm.wala.cast.js.ipa.callgraph.JSSSAPropagationCallGraphBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.ibm.wala.cast.js Show documentation
Show all versions of com.ibm.wala.cast.js Show documentation
T. J. Watson Libraries for Analysis
/*
* 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