package edg.edge; import java.util.*; import edg.constraint.*; import edg.graph.*; import edg.slicing.ConstrainedAlgorithm; import edg.slicing.Phase; import edg.graph.LAST.Direction; import edg.work.NodeWork; import edg.work.Work; import edg.work.WorkList; public class SummaryEdgeGenerator extends EdgeGenerator { public SummaryEdgeGenerator(EDG edg) { super(edg); } public void generate() { this.generateExternalSummaryEdges(); this.generateInternalSummaryEdges(); } /* ********************************** */ /* ************ External ************ */ /* ********************************** */ private void generateExternalSummaryEdges() { final List calls = edg.getNodes(Node.Type.Call); for (Node call : calls) { final Node callee = edg.getChild(call, Node.Type.Callee); final Node calleeResult = edg.getResFromNode(callee); final Set callEdges = edg.getEdges(calleeResult, LAST.Direction.Forwards, Edge.Type.Call); if (!callEdges.isEmpty()) continue; final Node callResult = edg.getResFromNode(call); final Node argumentsNode = edg.getChild(call, Node.Type.Arguments); final List arguments = edg.getChildren(argumentsNode); arguments.removeIf(n -> n.getType() == Node.Type.Result); for (Node argument : arguments) { final Node argumentResult = edg.getResFromNode(argument); this.edg.addEdge(argumentResult, callResult, new Edge(Edge.Type.Summary, AsteriskConstraint.getConstraint())); } } } /* ********************************** */ /* ************ Internal ************ */ /* ********************************** */ private void generateInternalSummaryEdges() { final List initialWorks = this.getInitialWorks(); final WorkList workList = new WorkList(initialWorks); final ConstrainedAlgorithm slicingAlgorithm = new ConstrainedAlgorithm(edg); while (workList.hasMore()) { final Work work = workList.next(); final Node initialNode = work.getInitialNode(); final Constraints constraints = work.getConstraints(); boolean isFormalIn = false; if (work instanceof NodeWork) { final NodeWork nodeWork = (NodeWork) work; final Node currentNode = nodeWork.getCurrentNode(); // The current node is the clause node of the initial node or the routine node of this clause result if (currentNode == edg.getNodeFromRes(initialNode) || currentNode == edg.getParent(initialNode)) continue; // This avoids the multiple evaluation of the same expressions which are in a circular dependency // WARNING: This may lead to incomplete grammars due to lack of looped constraints if (workList.getDoneNodes().contains(currentNode) && edg.getNodeFromRes(initialNode).getType() != Node.Type.Clause) continue; if (isFormalIn = this.isFormalIn(currentNode, initialNode)) { final List nodesToContinue = this.createSummaryEdges(currentNode, constraints); this.rependWorks(workList, nodesToContinue); } } workList.done(work); if (isFormalIn) continue; final List newWorks = slicingAlgorithm.processWork(Phase.SummaryGeneration, work); workList.pendAll(newWorks); } } private List getInitialWorks() { final List workList = new LinkedList<>(); final List routines = edg.getNodes(Node.Type.Routine); routines.addAll(edg.getNodes(Node.Type.AnonymousRoutine)); for (Node routine : routines) { final List clauses = edg.getChildrenNonResult(routine); for (Node clause : clauses) { Set incomingEdges = edg.getEdges(clause,Direction.Backwards); incomingEdges.removeIf(e -> e.getType() != Edge.Type.Call); if (incomingEdges.isEmpty()) continue; // Summary Edges for the result node final Node clauseResult = edg.getResFromNode(clause); workList.add(new NodeWork(clauseResult, clauseResult, new Constraints())); } } return workList; } private boolean isFormalIn(Node node, Node formalOutNode) // formalOutNode is only ClauseResult in Erlang { final Node parent = edg.getParent(node); if (parent == null) return false; // We reach a parameters node that does not correspond to the formalOut (anonymous functions) if (parent.getType() == Node.Type.Parameters) { final Node clause = edg.getParent(parent); final Node formalInClauseResult = edg.getResFromNode(clause); return formalInClauseResult == formalOutNode; } return false; } private List createSummaryEdges(Node formalIn, Constraints constraints) { final Grammar grammar = this.edg.getGrammar(); final GrammarConstraint grammarConstraint = new GrammarConstraint(grammar, formalIn); boolean isNewGrammarTerm = grammar.isNewGrammarTerm(grammarConstraint); if (!isLeftRecursiveGrammar(grammarConstraint, constraints)) this.edg.addProduction(grammarConstraint, constraints); final List nodesToContinue = new LinkedList<>(); if (!isNewGrammarTerm) return nodesToContinue; final List inputs = edg.getInputs(formalIn, Direction.Backwards); for (Node input : inputs) { final Node call = edg.getAncestor(input, Node.Type.Call); if (call == null) continue; final Node callResult = edg.getResFromNode(call); this.edg.addEdge(input, callResult, new Edge(Edge.Type.Summary, grammarConstraint)); nodesToContinue.add(callResult); } return nodesToContinue; } private void rependWorks(WorkList workList, List nodesToContinue) { for (Node nodeToContinue : nodesToContinue) { final String id = nodeToContinue.getId() + ""; workList.repend(id); } } private boolean isLeftRecursiveGrammar(GrammarConstraint grammarConstraint, Constraints constraints) { if (constraints.getEdgeConstraints().isEmpty()) return false; String grammarId = grammarConstraint.getRefNode().getId() + ""; String stackBottomTerm = constraints.getEdgeConstraints().firstElement().toString(); return grammarId.equals(stackBottomTerm); } }