2013-05-22 22:39:19 +00:00
|
|
|
/*******************************************************************************
|
|
|
|
* Copyright (c) 2013 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
|
|
|
|
*******************************************************************************/
|
2012-01-27 20:15:33 +00:00
|
|
|
package com.ibm.wala.cast.js.ipa.callgraph;
|
|
|
|
|
2012-03-01 02:45:51 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
2012-01-27 20:15:33 +00:00
|
|
|
import java.util.Map;
|
2012-02-23 17:44:09 +00:00
|
|
|
|
2012-01-27 20:15:33 +00:00
|
|
|
import com.ibm.wala.cast.ipa.callgraph.AstContextInsensitiveSSAContextInterpreter;
|
|
|
|
import com.ibm.wala.cast.ir.ssa.AstIRFactory;
|
|
|
|
import com.ibm.wala.cast.js.loader.JavaScriptLoader;
|
|
|
|
import com.ibm.wala.cast.js.translator.JSAstTranslator;
|
|
|
|
import com.ibm.wala.cast.js.translator.JSConstantFoldingRewriter;
|
|
|
|
import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation;
|
|
|
|
import com.ibm.wala.cast.loader.AstMethod.Retranslatable;
|
|
|
|
import com.ibm.wala.cast.tree.CAst;
|
|
|
|
import com.ibm.wala.cast.tree.CAstControlFlowMap;
|
|
|
|
import com.ibm.wala.cast.tree.CAstEntity;
|
|
|
|
import com.ibm.wala.cast.tree.CAstNode;
|
|
|
|
import com.ibm.wala.cast.tree.impl.CAstImpl;
|
2012-10-03 14:53:04 +00:00
|
|
|
import com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter;
|
2018-04-28 10:05:49 +00:00
|
|
|
import com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter.NonCopyingContext;
|
2012-03-01 02:45:51 +00:00
|
|
|
import com.ibm.wala.cast.util.CAstPattern;
|
|
|
|
import com.ibm.wala.cast.util.CAstPattern.Segments;
|
2012-01-27 20:15:33 +00:00
|
|
|
import com.ibm.wala.cfg.AbstractCFG;
|
|
|
|
import com.ibm.wala.cfg.ControlFlowGraph;
|
2013-04-11 01:09:10 +00:00
|
|
|
import com.ibm.wala.cfg.IBasicBlock;
|
2012-01-27 20:15:33 +00:00
|
|
|
import com.ibm.wala.classLoader.CallSiteReference;
|
|
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
|
|
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
|
|
|
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
|
|
|
import com.ibm.wala.ipa.callgraph.Context;
|
|
|
|
import com.ibm.wala.ipa.callgraph.ContextItem;
|
|
|
|
import com.ibm.wala.ipa.callgraph.ContextItem.Value;
|
|
|
|
import com.ibm.wala.ipa.callgraph.ContextKey;
|
|
|
|
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
2017-03-16 02:06:19 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.IAnalysisCacheView;
|
2012-01-27 20:15:33 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
|
|
|
import com.ibm.wala.ssa.DefUse;
|
|
|
|
import com.ibm.wala.ssa.IR;
|
|
|
|
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
2017-02-23 15:10:39 +00:00
|
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
2012-01-27 20:15:33 +00:00
|
|
|
import com.ibm.wala.ssa.SSAOptions;
|
|
|
|
import com.ibm.wala.ssa.SymbolTable;
|
|
|
|
import com.ibm.wala.types.TypeReference;
|
2012-03-01 02:45:51 +00:00
|
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
2012-01-27 20:15:33 +00:00
|
|
|
import com.ibm.wala.util.collections.Pair;
|
|
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
|
|
|
|
|
|
public class ArgumentSpecialization {
|
|
|
|
|
|
|
|
public static class ArgumentSpecializationContextIntepreter extends AstContextInsensitiveSSAContextInterpreter {
|
|
|
|
|
2017-03-16 02:06:19 +00:00
|
|
|
public ArgumentSpecializationContextIntepreter(AnalysisOptions options, IAnalysisCacheView cache) {
|
2012-01-27 20:15:33 +00:00
|
|
|
super(options, cache);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public IR getIR(CGNode node) {
|
|
|
|
if (node.getMethod() instanceof Retranslatable) {
|
2017-02-03 01:33:27 +00:00
|
|
|
return getAnalysisCache().getIR(node.getMethod(), node.getContext());
|
2012-01-27 20:15:33 +00:00
|
|
|
} else {
|
|
|
|
return super.getIR(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-25 15:57:37 +00:00
|
|
|
@Override
|
2012-01-27 20:15:33 +00:00
|
|
|
public DefUse getDU(CGNode node) {
|
|
|
|
if (node.getMethod() instanceof Retranslatable) {
|
2017-02-03 01:33:27 +00:00
|
|
|
return getAnalysisCache().getDefUse(getIR(node));
|
2012-01-27 20:15:33 +00:00
|
|
|
} else {
|
|
|
|
return super.getDU(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class ArgumentCountContext implements Context {
|
|
|
|
private final Context base;
|
|
|
|
private final int argumentCount;
|
|
|
|
|
|
|
|
public static ContextKey ARGUMENT_COUNT = new ContextKey() {
|
2013-06-25 15:57:37 +00:00
|
|
|
@Override
|
2012-01-27 20:15:33 +00:00
|
|
|
public String toString() {
|
|
|
|
return "argument count key";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-06-25 15:57:37 +00:00
|
|
|
@Override
|
2012-01-27 20:15:33 +00:00
|
|
|
public int hashCode() {
|
|
|
|
return base.hashCode() + (argumentCount * 4073);
|
|
|
|
}
|
|
|
|
|
2013-06-25 15:57:37 +00:00
|
|
|
@Override
|
2012-01-27 20:15:33 +00:00
|
|
|
public boolean equals(Object o) {
|
|
|
|
return
|
|
|
|
o.getClass() == this.getClass() &&
|
|
|
|
base.equals(((ArgumentCountContext)o).base) &&
|
|
|
|
argumentCount == ((ArgumentCountContext)o).argumentCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ArgumentCountContext(int argumentCount, Context base) {
|
|
|
|
this.argumentCount = argumentCount;
|
|
|
|
this.base = base;
|
|
|
|
}
|
|
|
|
|
2013-06-25 15:57:37 +00:00
|
|
|
@Override
|
2012-01-27 20:15:33 +00:00
|
|
|
public ContextItem get(ContextKey name) {
|
|
|
|
return (name == ARGUMENT_COUNT)? ContextItem.Value.make(argumentCount): base.get(name);
|
|
|
|
}
|
|
|
|
|
2013-06-25 15:57:37 +00:00
|
|
|
@Override
|
2012-01-27 20:15:33 +00:00
|
|
|
public String toString() {
|
|
|
|
return base.toString() + "(nargs:" + argumentCount + ")";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class ArgumentCountContextSelector implements ContextSelector, ContextKey {
|
|
|
|
private final ContextSelector base;
|
|
|
|
|
|
|
|
public ArgumentCountContextSelector(ContextSelector base) {
|
|
|
|
this.base = base;
|
|
|
|
}
|
|
|
|
|
2013-06-25 15:57:37 +00:00
|
|
|
@Override
|
2012-01-27 20:15:33 +00:00
|
|
|
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] actualParameters) {
|
|
|
|
Context baseContext = base.getCalleeTarget(caller, site, callee, actualParameters);
|
|
|
|
if (caller.getMethod() instanceof Retranslatable) {
|
|
|
|
int v = -1;
|
|
|
|
for (SSAAbstractInvokeInstruction x : caller.getIR().getCalls(site)) {
|
|
|
|
if (v == -1) {
|
2018-04-12 23:09:25 +00:00
|
|
|
v = x.getNumberOfPositionalParameters();
|
2012-01-27 20:15:33 +00:00
|
|
|
} else {
|
2018-04-12 23:09:25 +00:00
|
|
|
if (v != x.getNumberOfPositionalParameters()) {
|
2012-01-27 20:15:33 +00:00
|
|
|
return baseContext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new ArgumentCountContext(v, baseContext);
|
|
|
|
} else {
|
|
|
|
return baseContext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-25 15:57:37 +00:00
|
|
|
@Override
|
2012-01-27 20:15:33 +00:00
|
|
|
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
|
|
|
return base.getRelevantParameters(caller, site);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
Fix nearly all Eclipse warnings about using raw types
Along the way, I also converted many "for (;;)" loops into modern
"for (:)" loops. I didn't systematically look for all opportunities
to do this, though. I merely made this change where I was already
converting raw Iterator uses into modern Iterator<...> uses.
Better use of generics also allowed many casts to become statically
redundant. I have removed all such redundant casts.
Only three raw-types warnings remain after this batch of fixes. All
three involve raw uses of CallGraphBuilder. I've tried to fix these
too, but it quickly snowballs into a cascade of changes that may or
may not eventually reach a statically-type-save fixed point. I may
give these last few problem areas another go in the future. For now,
though, the hundreds of other fixes seem worth keeping even if there
are a few stragglers.
This commit may change some public APIs, but only by making weaker
type signatures stronger by replacing raw types with generic types.
For example, we may change something like "Set" into "Set<String>",
but we're not adding new arguments, changing any
underlying (post-generics-erasure) types, etc.
2017-07-09 18:38:35 +00:00
|
|
|
public static class ArgumentCountIRFactory extends AstIRFactory.AstDefaultIRFactory<IMethod> {
|
2012-03-01 02:45:51 +00:00
|
|
|
private static final CAstPattern directAccessPattern = CAstPattern.parse("|(ARRAY_REF(VAR(\"arguments\"),<value>*)||OBJECT_REF(VAR(\"arguments\"),<value>*))|");
|
|
|
|
|
|
|
|
private static final CAstPattern destructuredAccessPattern = CAstPattern.parse("BLOCK_EXPR(ASSIGN(VAR(/[$][$]destructure[$]rcvr[0-9]+/),VAR(\"arguments\")),ASSIGN(VAR(<name>/[$][$]destructure[$]elt[0-9]+/),<value>*))");
|
|
|
|
|
|
|
|
private static final CAstPattern destructuredCallPattern = CAstPattern.parse("CALL(VAR(<name>/[$][$]destructure[$]elt[0-9]+/),\"dispatch\",VAR(<thisptr>/[$][$]destructure[$]rcvr[0-9]+/),<args>**)");
|
|
|
|
|
2012-01-27 20:15:33 +00:00
|
|
|
private final SSAOptions defaultOptions;
|
|
|
|
|
|
|
|
public ArgumentCountIRFactory(SSAOptions defaultOptions) {
|
|
|
|
this.defaultOptions = defaultOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean contextIsIrrelevant(IMethod method) {
|
|
|
|
return method instanceof Retranslatable? false: super.contextIsIrrelevant(method);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public IR makeIR(final IMethod method, Context context, SSAOptions options) {
|
|
|
|
if (method instanceof Retranslatable) {
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
final Value<Integer> v = (Value<Integer>) context.get(ArgumentCountContext.ARGUMENT_COUNT);
|
|
|
|
final Retranslatable m = (Retranslatable)method;
|
|
|
|
if (v != null) {
|
|
|
|
final JavaScriptLoader myloader = (JavaScriptLoader) method.getDeclaringClass().getClassLoader();
|
2012-03-01 02:45:51 +00:00
|
|
|
|
2018-04-28 10:05:49 +00:00
|
|
|
class FixedArgumentsRewriter extends CAstBasicRewriter<NonCopyingContext> {
|
2012-03-01 02:45:51 +00:00
|
|
|
private final CAstEntity e;
|
2014-02-09 02:35:30 +00:00
|
|
|
private final Map<String, CAstNode> argRefs = HashMapFactory.make();
|
2012-03-01 02:45:51 +00:00
|
|
|
|
2012-01-27 20:15:33 +00:00
|
|
|
public FixedArgumentsRewriter(CAst Ast) {
|
2018-04-28 10:05:49 +00:00
|
|
|
super(Ast, new NonCopyingContext(), false);
|
2012-03-01 02:45:51 +00:00
|
|
|
this.e = m.getEntity();
|
|
|
|
for(Segments s : CAstPattern.findAll(destructuredAccessPattern, m.getEntity())) {
|
|
|
|
argRefs.put(s.getSingle("name").getValue().toString(), s.getSingle("value"));
|
2012-01-27 20:15:33 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-23 17:44:09 +00:00
|
|
|
|
2012-01-27 20:15:33 +00:00
|
|
|
private CAstNode handleArgumentRef(CAstNode n) {
|
2012-03-01 02:45:51 +00:00
|
|
|
Object x = n.getValue();
|
2012-01-27 20:15:33 +00:00
|
|
|
if (x != null) {
|
|
|
|
if (x instanceof Number && ((Number)x).intValue() < v.getValue()-2) {
|
2012-02-23 17:44:09 +00:00
|
|
|
int arg = ((Number)x).intValue() + 2;
|
2012-01-27 20:15:33 +00:00
|
|
|
if (arg < e.getArgumentCount()) {
|
|
|
|
return Ast.makeNode(CAstNode.VAR, Ast.makeConstant(e.getArgumentNames()[arg]));
|
|
|
|
} else {
|
|
|
|
return Ast.makeNode(CAstNode.VAR, Ast.makeConstant("$arg" + arg));
|
|
|
|
}
|
|
|
|
} else if (x instanceof String && "length".equals(x)) {
|
|
|
|
return Ast.makeConstant(v.getValue());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected CAstNode copyNodes(CAstNode root,
|
|
|
|
CAstControlFlowMap cfg,
|
|
|
|
NonCopyingContext context,
|
|
|
|
Map<Pair<CAstNode, NoKey>, CAstNode> nodeMap)
|
|
|
|
{
|
|
|
|
CAstNode result = null;
|
2012-03-01 02:45:51 +00:00
|
|
|
Segments s;
|
|
|
|
|
|
|
|
if ((s = CAstPattern.match(directAccessPattern, root)) != null) {
|
|
|
|
result = handleArgumentRef(s.getSingle("value"));
|
|
|
|
|
|
|
|
} else if ((s = CAstPattern.match(destructuredCallPattern, root)) != null) {
|
|
|
|
if (argRefs.containsKey(s.getSingle("name").getValue().toString())) {
|
2017-03-12 03:20:51 +00:00
|
|
|
List<CAstNode> x = new ArrayList<>();
|
2012-03-01 02:45:51 +00:00
|
|
|
CAstNode ref = handleArgumentRef(argRefs.get(s.getSingle("name").getValue().toString()));
|
|
|
|
if (ref != null) {
|
|
|
|
x.add(ref);
|
|
|
|
x.add(Ast.makeConstant("do"));
|
|
|
|
x.add(Ast.makeNode(CAstNode.VAR, Ast.makeConstant("arguments")));
|
|
|
|
for (CAstNode c : s.getMultiple("args")) {
|
|
|
|
x.add(copyNodes(c, cfg, context, nodeMap));
|
|
|
|
}
|
|
|
|
result = Ast.makeNode(CAstNode.CALL, x.toArray(new CAstNode[ x.size() ]));
|
|
|
|
}
|
|
|
|
}
|
2012-01-27 20:15:33 +00:00
|
|
|
|
|
|
|
} else if (root.getKind() == CAstNode.CONSTANT) {
|
|
|
|
result = Ast.makeConstant(root.getValue());
|
|
|
|
|
|
|
|
} else if (root.getKind() == CAstNode.OPERATOR) {
|
|
|
|
result = root;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == null) {
|
|
|
|
CAstNode children[] = new CAstNode[root.getChildCount()];
|
|
|
|
for (int i = 0; i < children.length; i++) {
|
|
|
|
children[i] = copyNodes(root.getChild(i), cfg, context, nodeMap);
|
|
|
|
}
|
|
|
|
for(Object label: cfg.getTargetLabels(root)) {
|
|
|
|
if (label instanceof CAstNode) {
|
|
|
|
copyNodes((CAstNode)label, cfg, context, nodeMap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CAstNode copy = Ast.makeNode(root.getKind(), children);
|
|
|
|
result = copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeMap.put(Pair.make(root, context.key()), result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
final FixedArgumentsRewriter args = new FixedArgumentsRewriter(new CAstImpl());
|
|
|
|
final JSConstantFoldingRewriter fold = new JSConstantFoldingRewriter(new CAstImpl());
|
|
|
|
|
|
|
|
class ArgumentativeTranslator extends JSAstTranslator {
|
|
|
|
|
|
|
|
public ArgumentativeTranslator(JavaScriptLoader loader) {
|
|
|
|
super(loader);
|
|
|
|
}
|
|
|
|
|
|
|
|
private CAstEntity codeBodyEntity;
|
|
|
|
private IMethod specializedCode;
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected int getArgumentCount(CAstEntity f) {
|
|
|
|
return Math.max(super.getArgumentCount(f), v.getValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected String[] getArgumentNames(CAstEntity f) {
|
|
|
|
if (super.getArgumentCount(f) >= v.getValue()) {
|
|
|
|
return super.getArgumentNames(f);
|
|
|
|
} else {
|
|
|
|
String[] argNames = new String[ v.getValue() ];
|
|
|
|
System.arraycopy(super.getArgumentNames(f), 0, argNames, 0, super.getArgumentCount(f));
|
|
|
|
for(int i = super.getArgumentCount(f); i < argNames.length; i++) {
|
2012-02-23 17:44:09 +00:00
|
|
|
argNames[i] = "$arg" + i;
|
2012-01-27 20:15:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return argNames;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected String composeEntityName(WalkContext parent, CAstEntity f) {
|
|
|
|
if (f == codeBodyEntity) {
|
|
|
|
return super.composeEntityName(parent, f) + "_" + v.getValue().intValue();
|
|
|
|
} else {
|
|
|
|
return super.composeEntityName(parent, f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-02-23 15:10:39 +00:00
|
|
|
protected void defineFunction(CAstEntity N, WalkContext definingContext, AbstractCFG<SSAInstruction, ? extends IBasicBlock<SSAInstruction>> cfg, SymbolTable symtab,
|
|
|
|
boolean hasCatchBlock, Map<IBasicBlock<SSAInstruction>,TypeReference[]> caughtTypes, boolean hasMonitorOp, AstLexicalInformation LI,
|
2012-01-27 20:15:33 +00:00
|
|
|
DebuggingInformation debugInfo) {
|
|
|
|
if (N == codeBodyEntity) {
|
|
|
|
specializedCode = myloader.makeCodeBodyCode(cfg, symtab, hasCatchBlock, caughtTypes, hasMonitorOp, LI, debugInfo, method.getDeclaringClass());
|
|
|
|
} else {
|
|
|
|
super.defineFunction(N, definingContext, cfg, symtab, hasCatchBlock, caughtTypes, hasMonitorOp, LI, debugInfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void translate(CAstEntity N, WalkContext context) {
|
|
|
|
if (N == m.getEntity()) {
|
|
|
|
codeBodyEntity = fold.rewrite(args.rewrite(N));
|
|
|
|
super.translate(codeBodyEntity, context);
|
|
|
|
} else {
|
|
|
|
super.translate(N, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
ArgumentativeTranslator a = new ArgumentativeTranslator(myloader);
|
|
|
|
m.retranslate(a);
|
|
|
|
return super.makeIR(a.specializedCode, context, options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return super.makeIR(method, context, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ControlFlowGraph makeCFG(IMethod method, Context context) {
|
|
|
|
return makeIR(method, context, defaultOptions).getControlFlowGraph();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|