/* * EDG, a library to generate and slice Expression Dependence Graphs. * Copyright (c) 2021. David Insa, Sergio PĂ©rez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package eknife.erlang; import java.util.List; import java.util.Set; import edg.constraint.*; import edg.graph.Edge; import edg.graph.LAST; import edg.graph.Node; public class ValueEdgeGenerator { protected final LAST last; public ValueEdgeGenerator(LAST last) { this.last = last; } public void generate() { this.generateEqualityEdges(); this.generateDataConstructorEdges(); this.generateListEdges(); this.generateOperationEdges(); this.generateSwitchEdges(); this.generateListComprehensionEdges(); this.generateGeneratorEdges(); this.generateRoutineCallEdges(); this.generateReturnEdges(); this.generateRoutineEdges(); } private void generateEqualityEdges() { final List equalities = this.last.getNodes(Node.Type.Equality); for (Node equality : equalities) { final Node pattern = this.last.getChild(equality, Node.Type.Pattern); final Node expression = this.last.getChild(equality, Node.Type.Value); this.last.addEdge(expression, equality, Edge.Type.Value); this.last.addEdge(equality, pattern, Edge.Type.Value); } } private void generateDataConstructorEdges() { final List dataConstructors = this.last.getNodes(Node.Type.DataConstructor); for (Node dataConstructor : dataConstructors) { final boolean isPatternZone = this.last.isPatternZone(dataConstructor); final AccessConstraint.Operation operation = isPatternZone ? AccessConstraint.Operation.Add : AccessConstraint.Operation.Remove; final List dataConstructorChildren = this.last.getChildren(dataConstructor); for (int childIndex = 0; childIndex < dataConstructorChildren.size(); childIndex++) { final Node dataConstructorChild = dataConstructorChildren.get(childIndex); final Node from = isPatternZone ? dataConstructor : dataConstructorChild; final Node to = isPatternZone ? dataConstructorChild : dataConstructor; final DataConstructorConstraint constraint = new DataConstructorConstraint(operation, childIndex + ""); this.last.addEdge(from, to, Edge.Type.Value, constraint); if (!isPatternZone){ switch(dataConstructorChild.getType()){ case List: ListConstraint lc = new ListConstraint(AccessConstraint.Operation.Remove, ListConstraint.Position.S); this.last.addEdge(dataConstructorChild, dataConstructor, Edge.Type.ValueStructure, lc); break; case DataConstructor: DataConstructorConstraint dc = new DataConstructorConstraint(AccessConstraint.Operation.Remove, "S"); this.last.addEdge(dataConstructorChild, dataConstructor, Edge.Type.ValueStructure, dc); break; // case Literal: // LiteralConstraint litc = new LiteralConstraint(AccessConstraint.Operation.Remove); // this.last.addEdge(dataConstructorChild, dataConstructor, Edge.Type.ValueStructure, litc); // break; default: break; } } } } } private void generateListEdges() { final List lists = this.last.getNodes(Node.Type.List); for (Node list : lists) { final boolean isPatternZone = this.last.isPatternZone(list); final AccessConstraint.Operation operation = isPatternZone ? AccessConstraint.Operation.Add : AccessConstraint.Operation.Remove; if (this.last.getChildren(list).isEmpty()) continue; final Node head = this.last.getChild(list, Node.Type.Value); final ListConstraint headConstraint = new ListConstraint(operation, ListConstraint.Position.H); final Node tail = this.last.getChild(list, Node.Type.List); final ListConstraint tailConstraint = new ListConstraint(operation, ListConstraint.Position.T); if (isPatternZone) { this.last.addEdge(list, head, Edge.Type.Value, headConstraint); this.last.addEdge(list, tail, Edge.Type.Value, tailConstraint); } else { this.last.addEdge(head, list, Edge.Type.Value, headConstraint); this.last.addEdge(tail, list, Edge.Type.Value, tailConstraint); } } } // private void generateDataConstructorAccessEdges() // { // final List dataConstructorAccesses = this.last.getNodes(Node.Type.DataConstructorAccess); // // for (Node dataConstructorAccess : dataConstructorAccesses) // { // final Node dataConstructorAccessResult = this.last.getSibling(dataConstructorAccess, Node.Type.Result); // final Node dataConstructor = this.last.getChild(dataConstructorAccess, 0); // final Node dataConstructorResult = this.last.getSibling(dataConstructor, Node.Type.Result); // final Node indexExpression = this.last.getChild(dataConstructorAccess, 1); // final Node index = this.last.getChild(indexExpression, 0); // final Node indexResult = this.last.getSibling(index, Node.Type.Result); // final String indexValue = index.getType() == Node.Type.Literal ? index.getName() : "*"; // final EdgeConstraint constraint = new DataConstructorConstraint(AccessConstraint.Operation.Add, indexValue); // // this.last.addEdge(dataConstructorResult, dataConstructorAccessResult, Edge.Type.Value, constraint); // this.last.addEdge(indexResult, dataConstructorAccessResult, Edge.Type.Value, AsteriskConstraint.getConstraint()); // } // } private void generateOperationEdges() { final List operations = this.last.getNodes(Node.Type.Operation); for (Node operation : operations) { final List operators = this.last.getChildren(operation); for (Node operator : operators) this.last.addEdge(operator, operation, Edge.Type.Value); } } private void generateSwitchEdges() { final List switches = this.last.getNodes(Node.Type.Switch); for (Node _switch : switches) { final Node selectorNode = this.last.getChild(_switch, Node.Type.Selector); final Node casesNode = this.last.getChild(_switch, Node.Type.Cases); final List cases = this.last.getChildren(casesNode); for (Node _case : cases) { final Node selectableNode = this.last.getChild(_case, Node.Type.Selectable); final Node selector = this.last.getChild(selectorNode, Node.Type.Value); if (selector != null) { final Node selectable = this.last.getChild(selectableNode, Node.Type.Value); this.last.addEdge(selector, selectable, Edge.Type.Value, EmptyConstraint.getConstraint()); } } } } private boolean isCFGReached(Node node) { Set outgoingCFGEdges = this.last.getEdges(node, LAST.Direction.Forwards); outgoingCFGEdges.removeIf(e -> e.getType() != Edge.Type.ControlFlow); return outgoingCFGEdges.size() > 0; } private void generateListComprehensionEdges() { final List listComprehensionNodes = this.last.getNodes(Node.Type.ListComprehension); final EdgeConstraint negativeConstraint = new ListComprehensionConstraint(ListComprehensionConstraint.Operation.Remove); for (Node listComprehensionNode : listComprehensionNodes) { final Node valueNode = this.last.getChild(listComprehensionNode, Node.Type.Value); final Node value = this.last.getChild(valueNode, Node.Type.Value); this.last.addEdge(value, listComprehensionNode, Edge.Type.Value, negativeConstraint); } } private void generateGeneratorEdges() { final List generators = this.last.getNodes(Node.Type.Generator); final EdgeConstraint positiveConstraint = new ListComprehensionConstraint(ListComprehensionConstraint.Operation.Add); final EdgeConstraint listStructureConstraint = new ListConstraint(AccessConstraint.Operation.Add, ListConstraint.Position.S); for (Node generator : generators) { final Node pattern = this.last.getChild(generator, Node.Type.Variable); final Node expression = this.last.getChild(generator, Node.Type.Iterator); this.generateGeneratorPatternStructureEdges(generator, pattern); this.last.addEdge(expression, generator, Edge.Type.Value, listStructureConstraint); this.last.addEdge(expression, pattern, Edge.Type.Value, positiveConstraint); } } private void generateGeneratorPatternStructureEdges(Node parent, Node pattern){ switch(pattern.getType()){ case List: ListConstraint lc = new ListConstraint(AccessConstraint.Operation.Add, ListConstraint.Position.S); this.last.addEdge(pattern, parent, Edge.Type.ValueStructure, lc); generateGeneratorPatternStructureEdges(pattern); break; case DataConstructor: DataConstructorConstraint dc = new DataConstructorConstraint(AccessConstraint.Operation.Add, "S"); this.last.addEdge(pattern, parent, Edge.Type.ValueStructure, dc); generateGeneratorPatternStructureEdges(pattern); break; case Literal: if (parent.getType() == Node.Type.Generator) this.last.addEdge(pattern, parent, Edge.Type.ValueStructure, EmptyConstraint.getConstraint()); else { final Node genNode = this.last.getAncestor(parent, Node.Type.Generator); this.last.addEdge(pattern, genNode, Edge.Type.Value, EmptyConstraint.getConstraint()); } // generateGeneratorPatternStructureEdges(pattern); break; default: break; } } private void generateGeneratorPatternStructureEdges(Node pattern) { List children = last.getChildrenNonResult(pattern); boolean allChildrenLeaves = true; for (Node child : children) if (child.getType() == Node.Type.DataConstructor || child.getType() == Node.Type.List || child.getType() == Node.Type.Literal) { allChildrenLeaves = false; this.generateGeneratorPatternStructureEdges(pattern, child); } if (allChildrenLeaves) { Node generator = this.last.getAncestor(pattern, Node.Type.Generator); Node variableIt = this.last.getChild(generator, Node.Type.Iterator); final EdgeConstraint positiveConstraint = new ListComprehensionConstraint(ListComprehensionConstraint.Operation.Add); this.last.addEdge(variableIt, pattern, Edge.Type.ValueStructure, positiveConstraint); } } private void generateRoutineCallEdges() { final List calls = this.last.getNodes(Node.Type.Call); for (Node call : calls) { final Node callee = this.last.getChild(call, Node.Type.Callee); this.last.addEdge(callee, call, Edge.Type.Value, AsteriskConstraint.getConstraint()); final Node scopeNode = this.last.getChild(callee, Node.Type.Scope); if (!this.last.getChildren(scopeNode).isEmpty()) { final Node scope = this.last.getChild(scopeNode, Node.Type.Value); this.last.addEdge(scope, callee, Edge.Type.Value, EmptyConstraint.getConstraint()); } final Node nameNode = this.last.getChild(callee, Node.Type.Name); final Node name = this.last.getChild(nameNode, Node.Type.Value); this.last.addEdge(name, callee, Edge.Type.Value, EmptyConstraint.getConstraint()); } } private void generateReturnEdges() { final List returns = this.last.getNodes(Node.Type.Return); for (Node returnNode : returns) { if (this.last.getChildren(returnNode).isEmpty()) continue; if (!isCFGReached(returnNode)) continue; final Node returnExpr = this.last.getChild(returnNode, Node.Type.Value); final String returnText = returnNode.getName(); final int dstId = Integer.parseInt(returnText.substring(returnText.lastIndexOf(" ") + 1)); final Node dstNode = this.last.getNode(dstId); this.last.addEdge(returnExpr, dstNode, Edge.Type.Value, EmptyConstraint.getConstraint()); } } private void generateRoutineEdges() { final List routines = this.last.getNodes(Node.Type.Routine); routines.addAll(this.last.getNodes(Node.Type.AnonymousRoutine)); for (Node routine : routines) { // final Node routineParent = this.last.getParent(routine); // final Node.Type routineParentType = routineParent.getType(); // if (routineParentType == Node.Type.Module) // continue; final List clauses = this.last.getChildren(routine); for (Node clause : clauses) this.last.addEdge(clause, routine, Edge.Type.Value, EmptyConstraint.getConstraint()); } } }