package dk.brics.jsrefactoring; import java.util.HashSet; import java.util.Set; import dk.brics.jsparser.AstUtil; import dk.brics.jsparser.Literals; import dk.brics.jsparser.node.ABinopExp; import dk.brics.jsparser.node.AConstExp; import dk.brics.jsparser.node.ADynamicPropertyExp; import dk.brics.jsparser.node.AForInStmt; import dk.brics.jsparser.node.ANameExp; import dk.brics.jsparser.node.ANormalObjectLiteralProperty; import dk.brics.jsparser.node.APropertyExp; import dk.brics.jsparser.node.AStringConst; import dk.brics.jsparser.node.EBinop; import dk.brics.jsparser.node.IPropertyAccessNode; import dk.brics.jsparser.node.Node; import dk.brics.jsparser.node.PExp; import dk.brics.jspointers.lattice.values.ObjectValue; import dk.brics.jsrefactoring.renameprty.ConstantInExpNameNode; import dk.brics.jsrefactoring.renameprty.PropertyExpNameNode; import dk.brics.jsrefactoring.renameprty.PropertyInitializerNameNode; import dk.brics.jsrefactoring.renameprty.PropertyNameNode; import dk.brics.jsutil.MultiMap; import dk.brics.jsutil.Pair; /** * <p> * Utility class for computing alias-closed sets of objects and property accesses. * </p> * * @author max.schaefer@comlab.ox.ac.uk * */ public class AffectedNodeFinder { protected final Master input; protected final NodeFinder finder; protected final PropertyNameNode node; // caches the relation Base(-, node.getName()) protected final MultiMap<PropertyNameNode,ObjectValue> prtyname2base = new MultiMap<PropertyNameNode, ObjectValue>(); // caches the inverse relation of prtyname2base protected final MultiMap<ObjectValue,PropertyNameNode> base2prtyname = new MultiMap<ObjectValue,PropertyNameNode>(); private MultiMap<AForInStmt,ObjectValue> forin2base = new MultiMap<AForInStmt, ObjectValue>(); private MultiMap<ObjectValue,AForInStmt> base2forin = new MultiMap<ObjectValue, AForInStmt>(); private MultiMap<ADynamicPropertyExp,ObjectValue> dynprty2base = new MultiMap<ADynamicPropertyExp, ObjectValue>(); private MultiMap<ObjectValue,ADynamicPropertyExp> base2dynprty = new MultiMap<ObjectValue, ADynamicPropertyExp>(); private MultiMap<ADynamicPropertyExp,AForInStmt> dynprty2forin = new MultiMap<ADynamicPropertyExp, AForInStmt>(); private MultiMap<AForInStmt,ADynamicPropertyExp> forin2dynprty = new MultiMap<AForInStmt, ADynamicPropertyExp>(); private Set<ADynamicPropertyExp> unsafeDynPrty = new HashSet<ADynamicPropertyExp>(); private Set<AForInStmt> unsafeForin = new HashSet<AForInStmt>(); protected Set<ObjectValue> affectedObjects = null; protected Set<PropertyNameNode> affectedNames = null; private Set<ADynamicPropertyExp> affectedDynPrtyExps; private Set<AForInStmt> affectedForInStmts; public AffectedNodeFinder(Master input, NodeFinder finder, PropertyNameNode node) { this.input = input; this.finder = finder; this.node = node; } public AffectedNodeFinder(Master input, PropertyNameNode node) { // use standard node finder that looks for all nodes that could be accesses this(input, new NodeFinder(input, IPropertyAccessNode.class, ANameExp.class, ANormalObjectLiteralProperty.class, ABinopExp.class, AForInStmt.class), node); } protected void computeMaps() { String propertyName = node.getName(); prtyname2base.clear(); base2prtyname.clear(); // first, cache Base mapping and its inverse for (APropertyExp exp : finder.getAllNodesOfType(APropertyExp.class)) { String name = Literals.parseIdentifier(exp.getName().getText()); if (name.equals(node.getName())) { PropertyNameNode node = new PropertyExpNameNode(exp); for (ObjectValue base : node.getBase(input, name)) { prtyname2base.add(node, base); base2prtyname.add(base, node); } } } for (ANormalObjectLiteralProperty prty : finder.getAllNodesOfType(ANormalObjectLiteralProperty.class)) { // property names in object literals String name = AstUtil.getPropertyName(prty.getName()); PropertyNameNode node = new PropertyInitializerNameNode(prty); if (name.equals(node.getName())) { // note: getReceivers = getBase for this type of node for (ObjectValue obj : input.getInitializedObjects(prty)) { prtyname2base.add(node, obj); base2prtyname.add(obj, node); } } } for (ABinopExp exp : finder.getAllNodesOfType(ABinopExp.class)) { if (exp.getOp().kindPBinop() != EBinop.IN) continue; PExp left = exp.getLeft(); if (!(left instanceof AConstExp)) continue; // non-constant node cannot be renamed AConstExp leftc = (AConstExp)left; if (!(leftc.getConst() instanceof AStringConst)) continue; // int and boolean are safe AStringConst sc = (AStringConst)leftc.getConst(); String name = Literals.parseStringLiteral(sc.getStringLiteral().getText()); PropertyNameNode node = new ConstantInExpNameNode(sc, exp); if (name.equals(node.getName())) { for (ObjectValue base : node.getBase(input, name)) { prtyname2base.add(node, base); base2prtyname.add(base, node); } } } // prepare dynamic property expressions for (ADynamicPropertyExp exp : finder.getAllNodesOfType(ADynamicPropertyExp.class)) { Set<ObjectValue> receivers = input.getReceivers(exp); Set<ObjectValue> bases; if (AstUtil.isReadFrom(exp)) { bases = input.getAllPrototypes(receivers, true); } else { bases = receivers; } for (ObjectValue base : bases) { if (!input.isPropertyDefinitelyAbsent(base, propertyName)) { dynprty2base.add(exp, base); base2dynprty.add(base, exp); } } } // prepare for-in statements for (AForInStmt forin : finder.getAllNodesOfType(AForInStmt.class)) { Set<ObjectValue> bases = input.getAllPrototypes(input.getForInObjects(forin), true); for (ObjectValue base : bases) { if (!input.isPropertyDefinitelyAbsent(base, propertyName)) { forin2base.add(forin, base); base2forin.add(base, forin); } } } for (ANameExp name : finder.getAllNodesOfType(ANameExp.class)) { Pair<Set<AForInStmt>,Boolean> p = input.getForInHosts(name); Set<AForInStmt> forins = p.fst; boolean onlyForIn = p.snd; Node parent = name.parent(); if (parent instanceof ADynamicPropertyExp && ((ADynamicPropertyExp)parent).getPropertyExp() == name) { ADynamicPropertyExp dynprty = (ADynamicPropertyExp)parent; // relate property exprs and for-in statements if (onlyForIn) { for (AForInStmt forin : forins) { dynprty2forin.add(dynprty, forin); forin2dynprty.add(forin, dynprty); } } else { unsafeDynPrty.add(dynprty); } } else { // mark for-in statements unsafe unsafeForin.addAll(forins); } } } protected void computeAffected() { computeMaps(); affectedNames = new HashSet<PropertyNameNode>(); affectedObjects = new HashSet<ObjectValue>(); affectedDynPrtyExps = new HashSet<ADynamicPropertyExp>(); affectedForInStmts = new HashSet<AForInStmt>(); Set<PropertyNameNode> namequeue = new HashSet<PropertyNameNode>(); Set<ObjectValue> objqueue = new HashSet<ObjectValue>(); Set<ADynamicPropertyExp> dynprtyqueue = new HashSet<ADynamicPropertyExp>(); Set<AForInStmt> forinqueue = new HashSet<AForInStmt>(); affectedNames.add(node); namequeue.add(node); while (!namequeue.isEmpty() || !objqueue.isEmpty() || !dynprtyqueue.isEmpty() || !forinqueue.isEmpty()) { for (PropertyNameNode exp : namequeue) { for (ObjectValue obj : prtyname2base.getView(exp)) { if (affectedObjects.add(obj)) { objqueue.add(obj); } } } namequeue.clear(); for (ADynamicPropertyExp exp : dynprtyqueue) { for (ObjectValue obj : dynprty2base.getView(exp)) { if (affectedObjects.add(obj)) { objqueue.add(obj); } } for (AForInStmt forin : dynprty2forin.getView(exp)) { if (affectedForInStmts.add(forin)) { forinqueue.add(forin); } } } dynprtyqueue.clear(); for (AForInStmt forin : forinqueue) { for (ObjectValue obj : forin2base.getView(forin)) { if (affectedObjects.add(obj)) { objqueue.add(obj); } } for (ADynamicPropertyExp exp : forin2dynprty.getView(forin)) { if (affectedDynPrtyExps.add(exp)) { dynprtyqueue.add(exp); } } } forinqueue.clear(); for (ObjectValue obj : objqueue) { for (PropertyNameNode exp : base2prtyname.getView(obj)) { if (affectedNames.add(exp)) { namequeue.add(exp); } } for (ADynamicPropertyExp exp : base2dynprty.getView(obj)) { if (affectedDynPrtyExps.add(exp)) { dynprtyqueue.add(exp); } } for (AForInStmt forin : base2forin.getView(obj)) { if (affectedForInStmts.add(forin)) { forinqueue.add(forin); } } } objqueue.clear(); } } public Set<ObjectValue> getAffectedObjects() { if(affectedObjects == null) computeAffected(); return affectedObjects; } public Set<PropertyNameNode> getAffectedNames() { if(affectedNames == null) computeAffected(); return affectedNames; } public Set<ADynamicPropertyExp> getAffectedDynPrtyExps() { if (affectedDynPrtyExps == null) computeAffected(); return affectedDynPrtyExps; } public Set<AForInStmt> getAffectedForInStmts() { if (affectedForInStmts == null) computeAffected(); return affectedForInStmts; } }