ast-based constant folding

This commit is contained in:
Julian Dolby 2018-04-28 12:05:49 +02:00
parent bc4939db97
commit 27a8fff714
13 changed files with 370 additions and 35 deletions

View File

@ -27,6 +27,7 @@ import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.tree.impl.CAstImpl;
import com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter;
import com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter.NonCopyingContext;
import com.ibm.wala.cast.util.CAstPattern;
import com.ibm.wala.cast.util.CAstPattern.Segments;
import com.ibm.wala.cfg.AbstractCFG;
@ -183,12 +184,12 @@ public class ArgumentSpecialization {
if (v != null) {
final JavaScriptLoader myloader = (JavaScriptLoader) method.getDeclaringClass().getClassLoader();
class FixedArgumentsRewriter extends CAstBasicRewriter {
class FixedArgumentsRewriter extends CAstBasicRewriter<NonCopyingContext> {
private final CAstEntity e;
private final Map<String, CAstNode> argRefs = HashMapFactory.make();
public FixedArgumentsRewriter(CAst Ast) {
super(Ast, false);
super(Ast, new NonCopyingContext(), false);
this.e = m.getEntity();
for(Segments s : CAstPattern.findAll(destructuredAccessPattern, m.getEntity())) {
argRefs.put(s.getSingle("name").getValue().toString(), s.getSingle("value"));

View File

@ -0,0 +1,192 @@
package com.ibm.wala.cast.test;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import org.junit.Test;
import com.ibm.wala.cast.tree.CAst;
import com.ibm.wala.cast.tree.CAstAnnotation;
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.CAstNodeTypeMap;
import com.ibm.wala.cast.tree.CAstQualifier;
import com.ibm.wala.cast.tree.CAstSourcePositionMap;
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
import com.ibm.wala.cast.tree.CAstType;
import com.ibm.wala.cast.tree.impl.CAstImpl;
import com.ibm.wala.cast.tree.impl.CAstOperator;
import com.ibm.wala.cast.tree.rewrite.AstConstantFolder;
import com.ibm.wala.cast.util.AstConstantCollector;
import com.ibm.wala.cast.util.CAstPattern;
import com.ibm.wala.cast.util.CAstPattern.Segments;
import com.ibm.wala.util.collections.EmptyIterator;
public class TestConstantCollector {
private CAst ast = new CAstImpl();
private static CAstEntity fakeEntity(CAstNode root) {
return new CAstEntity() {
@Override
public int getKind() {
// TODO Auto-generated method stub
return 0;
}
@Override
public String getName() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getSignature() {
// TODO Auto-generated method stub
return null;
}
@Override
public String[] getArgumentNames() {
// TODO Auto-generated method stub
return null;
}
@Override
public CAstNode[] getArgumentDefaults() {
// TODO Auto-generated method stub
return null;
}
@Override
public int getArgumentCount() {
// TODO Auto-generated method stub
return 0;
}
@Override
public Map<CAstNode, Collection<CAstEntity>> getAllScopedEntities() {
return Collections.emptyMap();
}
@Override
public Iterator<CAstEntity> getScopedEntities(CAstNode construct) {
return EmptyIterator.instance();
}
@Override
public CAstNode getAST() {
return root;
}
@Override
public CAstControlFlowMap getControlFlow() {
// TODO Auto-generated method stub
return null;
}
@Override
public CAstSourcePositionMap getSourceMap() {
// TODO Auto-generated method stub
return null;
}
@Override
public Position getPosition() {
// TODO Auto-generated method stub
return null;
}
@Override
public CAstNodeTypeMap getNodeTypeMap() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<CAstQualifier> getQualifiers() {
// TODO Auto-generated method stub
return null;
}
@Override
public CAstType getType() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<CAstAnnotation> getAnnotations() {
// TODO Auto-generated method stub
return null;
}
};
}
private CAstNode root1 =
ast.makeNode(CAstNode.BLOCK_STMT,
ast.makeNode(CAstNode.ASSIGN,
ast.makeNode(CAstNode.VAR,
ast.makeConstant("var1")),
ast.makeConstant(15)));
@Test
public void testSegmentsRoot1() {
Collection<Segments> x = CAstPattern.findAll(AstConstantCollector.simpleValuePattern, fakeEntity(root1));
assert x.size() == 1;
}
@Test
public void testRoot1() {
Map<String,Object> x = AstConstantCollector.collectConstants(fakeEntity(root1));
assert x.size() == 1;
}
private CAstNode root2 =
ast.makeNode(CAstNode.BLOCK_STMT,
ast.makeNode(CAstNode.ASSIGN,
ast.makeNode(CAstNode.VAR,
ast.makeConstant("var1")),
ast.makeConstant(15)),
ast.makeNode(CAstNode.ASSIGN,
ast.makeNode(CAstNode.VAR,
ast.makeConstant("var1")),
ast.makeConstant(14)));
@Test
public void testSegmentsRoot2() {
Collection<Segments> x = CAstPattern.findAll(AstConstantCollector.simpleValuePattern, fakeEntity(root2));
assert x.size() == 2;
}
@Test
public void testRoot2() {
Map<String,Object> x = AstConstantCollector.collectConstants(fakeEntity(root2));
assert x.size() == 0;
}
private CAstNode root3 =
ast.makeNode(CAstNode.BLOCK_EXPR,
ast.makeNode(CAstNode.ASSIGN,
ast.makeNode(CAstNode.VAR,
ast.makeConstant("var1")),
ast.makeConstant(15)),
ast.makeNode(CAstNode.BINARY_EXPR,
CAstOperator.OP_ADD,
ast.makeConstant(10),
ast.makeNode(CAstNode.VAR, ast.makeConstant("var1"))));
public static final CAstPattern toCodePattern3 = CAstPattern.parse("BINARY_EXPR(*,\"10\",\"15\")");
@Test
public void testRoot3() {
CAstEntity ce = fakeEntity(root3);
CAstEntity nce = AstConstantFolder.fold(ce);
Collection<Segments> matches = CAstPattern.findAll(toCodePattern3, nce);
assert matches.size() == 1;
}
}

View File

@ -4604,7 +4604,7 @@ public abstract class AstTranslator extends CAstVisitor<AstTranslator.WalkContex
nodeMap.put(Pair.make(root, c.key()), expr);
return expr;
} else {
return super.copyNodesHackForEclipse(root, cfg, c, nodeMap);
return super.copyNodes(root, cfg, c, nodeMap);
}
}
}).rewrite(included);

View File

@ -17,12 +17,13 @@ import com.ibm.wala.cast.tree.CAstControlFlowMap;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.tree.impl.CAstOperator;
import com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter;
import com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter.NonCopyingContext;
import com.ibm.wala.util.collections.Pair;
public abstract class ConstantFoldingRewriter extends CAstBasicRewriter {
public abstract class ConstantFoldingRewriter extends CAstBasicRewriter<NonCopyingContext> {
protected ConstantFoldingRewriter(CAst Ast) {
super(Ast, true);
super(Ast, new NonCopyingContext(), true);
}
protected abstract Object eval(CAstOperator op, Object lhs, Object rhs);

View File

@ -0,0 +1,85 @@
package com.ibm.wala.cast.tree.rewrite;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
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.CAstNodeTypeMap;
import com.ibm.wala.cast.tree.CAstSourcePositionMap;
import com.ibm.wala.cast.tree.impl.CAstImpl;
import com.ibm.wala.cast.tree.impl.DelegatingEntity;
import com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter.NonCopyingContext;
import com.ibm.wala.cast.tree.rewrite.CAstRewriter.Rewrite;
import com.ibm.wala.cast.util.AstConstantCollector;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Pair;
public class AstConstantFolder {
static class AssignSkipContext extends NonCopyingContext {
private Set<CAstNode> skip = HashSetFactory.make();
}
public static CAstEntity fold(CAstEntity ce) {
Map<String,Object> constants = AstConstantCollector.collectConstants(ce);
if (constants.isEmpty()) {
return ce;
} else {
Rewrite nce = new CAstCloner(new CAstImpl(), new AssignSkipContext(), true) {
@Override
protected CAstNode copyNodes(CAstNode root, CAstControlFlowMap cfg, NonCopyingContext c,
Map<Pair<CAstNode, NoKey>, CAstNode> nodeMap) {
if (root.getKind() == CAstNode.ASSIGN) {
((AssignSkipContext)c).skip.add(root.getChild(0));
}
if (root.getKind() == CAstNode.VAR && constants.containsKey(root.getChild(0).getValue()) && ! ((AssignSkipContext)c).skip.contains(root)) {
return Ast.makeConstant(constants.get(root.getChild(0).getValue()));
} else {
return super.copyNodes(root, cfg, c, nodeMap);
}
}
}.rewrite(ce.getAST(), ce.getControlFlow(), ce.getSourceMap(), ce.getNodeTypeMap(), ce.getAllScopedEntities());
return new DelegatingEntity(ce) {
@Override
public CAstNode getAST() {
return nce.newRoot();
}
@Override
public CAstControlFlowMap getControlFlow() {
return nce.newCfg();
}
@Override
public CAstSourcePositionMap getSourceMap() {
return nce.newPos();
}
@Override
public CAstNodeTypeMap getNodeTypeMap() {
return nce.newTypes();
}
@Override
public Map<CAstNode, Collection<CAstEntity>> getAllScopedEntities() {
return nce.newChildren();
}
@Override
public Iterator<CAstEntity> getScopedEntities(CAstNode construct) {
Collection<CAstEntity> children = nce.newChildren().get(construct);
return children==null? EmptyIterator.instance(): children.iterator();
}
};
}
}
}

View File

@ -23,8 +23,8 @@ import com.ibm.wala.util.debug.Assertions;
* abstract base class for {@link CAstRewriter}s that do no cloning of nodes
*
*/
public abstract class CAstBasicRewriter
extends CAstRewriter<CAstBasicRewriter.NonCopyingContext,
public abstract class CAstBasicRewriter<T extends CAstBasicRewriter.NonCopyingContext>
extends CAstRewriter<T,
CAstBasicRewriter.NoKey>
{
@ -69,11 +69,11 @@ public abstract class CAstBasicRewriter
}
}
protected CAstBasicRewriter(CAst Ast, boolean recursive) {
super(Ast, recursive, new NonCopyingContext());
protected CAstBasicRewriter(CAst Ast, T context, boolean recursive) {
super(Ast, recursive, context);
}
@Override
protected abstract CAstNode copyNodes(CAstNode root, final CAstControlFlowMap cfg, NonCopyingContext context, Map<Pair<CAstNode,NoKey>, CAstNode> nodeMap);
protected abstract CAstNode copyNodes(CAstNode root, final CAstControlFlowMap cfg, T context, Map<Pair<CAstNode,NoKey>, CAstNode> nodeMap);
}

View File

@ -22,25 +22,22 @@ import com.ibm.wala.cast.tree.CAstSourcePositionMap;
import com.ibm.wala.cast.tree.impl.CAstOperator;
import com.ibm.wala.util.collections.Pair;
public class CAstCloner extends CAstBasicRewriter {
public class CAstCloner extends CAstBasicRewriter<CAstBasicRewriter.NonCopyingContext> {
public CAstCloner(CAst Ast, boolean recursive) {
super(Ast, recursive);
this(Ast, new NonCopyingContext(), recursive);
}
public CAstCloner(CAst Ast) {
this(Ast, false);
}
protected CAstCloner(CAst Ast, NonCopyingContext context, boolean recursive) {
super(Ast, context, recursive);
}
@Override
protected CAstNode copyNodes(CAstNode root, final CAstControlFlowMap cfg, NonCopyingContext c, Map<Pair<CAstNode,NoKey>, CAstNode> nodeMap) {
return copyNodesHackForEclipse(root, cfg, c, nodeMap);
}
/**
* what is the hack here? --MS
*/
protected CAstNode copyNodesHackForEclipse(CAstNode root, final CAstControlFlowMap cfg, NonCopyingContext c, Map<Pair<CAstNode,NoKey>, CAstNode> nodeMap) {
final Pair<CAstNode, NoKey> pairKey = Pair.make(root, c.key());
if (root instanceof CAstOperator) {
nodeMap.put(pairKey, root);

View File

@ -361,7 +361,7 @@ public abstract class CAstRewriter<C extends CAstRewriter.RewriteContext<K>, K e
@Override
public CAstControlFlowMap newCfg() {
if (theCfg == null)
if (theCfg == null && cfg != null)
theCfg = copyFlow(nodes, cfg, newPos());
return theCfg;
}
@ -375,7 +375,7 @@ public abstract class CAstRewriter<C extends CAstRewriter.RewriteContext<K>, K e
@Override
public CAstNodeTypeMap newTypes() {
if (theTypes == null)
if (theTypes == null && types != null)
theTypes = copyTypes(nodes, types);
return theTypes;
}

View File

@ -0,0 +1,64 @@
package com.ibm.wala.cast.util;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.util.CAstPattern.Segments;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
public class AstConstantCollector {
public static final CAstPattern simplePreUpdatePattern = CAstPattern.parse("ASSIGN_PRE_OP(VAR(<name>CONSTANT()),**)");
public static final CAstPattern simplePostUpdatePattern = CAstPattern.parse("ASSIGN_POST_OP(VAR(<name>CONSTANT()),**)");
public static final CAstPattern simpleValuePattern = CAstPattern.parse("ASSIGN(VAR(<name>CONSTANT()),<value>*)");
public static Map<String,Object> collectConstants(CAstEntity function, Map<String,Object> values) {
if (function.getAST() != null) {
Set<String> bad = HashSetFactory.make();
for(Segments s : CAstPattern.findAll(simplePreUpdatePattern, function)) {
bad.add((String) s.getSingle("name").getValue());
}
for(Segments s : CAstPattern.findAll(simplePostUpdatePattern, function)) {
bad.add((String) s.getSingle("name").getValue());
}
for(Segments s : CAstPattern.findAll(simpleValuePattern, function)) {
String var = (String) s.getSingle("name").getValue();
if (s.getSingle("value").getKind() != CAstNode.CONSTANT) {
bad.add(var);
} else {
Object val = s.getSingle("value").getValue();
if (! bad.contains(var)) {
if (values.containsKey(var)) {
if (!values.get(var).equals(val)) {
values.remove(var);
bad.add(var);
}
} else {
values.put(var, val);
}
}
}
}
}
for(Collection<CAstEntity> ces : function.getAllScopedEntities().values()) {
for(CAstEntity ce : ces) {
collectConstants(ce, values);
}
}
return values;
}
public static Map<String,Object> collectConstants(CAstEntity function) {
Map<String,Object> values = HashMapFactory.make();
collectConstants(function, values);
return values;
}
}

View File

@ -408,6 +408,13 @@ public class CAstPattern {
visit(top, c, this);
return result;
}
@Override
protected boolean doVisit(CAstNode n, Context context, CAstVisitor<Context> visitor) {
return true;
}
}
private static class Parser {

View File

@ -3,6 +3,5 @@ source.. = src/,\
output.. = bin/test
bin.includes = META-INF/,\
.,\
plugin.xml,\
plugin.properties
plugin.properties
javacProjectSettings = true

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<!DOCTYPE plugin>
<plugin>
<extension id="HeadlessWALA"
point="org.eclipse.core.runtime.applications">
<application>
<run class="com.ibm.wala.eclipse.headless.Main"/>
</application>
</extension>
</plugin>

View File

@ -118,7 +118,7 @@ public class AnalysisScope {
*/
private SetOfClasses exclusions;
final protected LinkedHashMap<Atom, ClassLoaderReference> loadersByName = new LinkedHashMap<>();
public final LinkedHashMap<Atom, ClassLoaderReference> loadersByName = new LinkedHashMap<>();
/**
* Special class loader for array instances