Commit 4f86a8e4 authored by Sergio Pérez's avatar Sergio Pérez
Browse files

Fixes

* The CFG ignores unreachable clauses in if expressions
* High order call slicing improved
* Fix Anonymous routines lack of value dependencies
parent a70c8934
Loading
Loading
Loading
Loading
+19 −17
Original line number Diff line number Diff line
@@ -390,7 +390,7 @@ public class InterproceduralEdgeGenerator extends EdgeGenerator
		//		(2) A FUNCTION CALL TO:
		//			(2.1) AN ANONYMOUS NAMED FUNCTION
		//			(2.2) A CALL TO AN AD-HOC DEFINED ANONYMOUS FUNCTION
		//			(2.3) A FUNCTION STORED IN A VARIABLE (CAN BE ANY FUNCTION)
		//			(2.3) A FUNCTION STORED IN A VARIABLE (DEPENDENCE ESTABLISHED BY FLOW)

		final List<Node> routines = new LinkedList<>();
		if (nameType == Node.Type.Literal) { // (1) A FUNCTION CALL OF A PARTICULAR FUNCTION
@@ -400,7 +400,9 @@ public class InterproceduralEdgeGenerator extends EdgeGenerator
			routines.addAll(edg.getNodes(Node.Type.AnonymousRoutine));
		}

		routines.removeIf(n -> !n.getName().equals(routineName)); // CONNECT TO MATCHING ROUTINE DEFINITIONS
		// CONNECT TO MATCHING ROUTINE DEFINITIONS
		// NULL NAME ROUTINES ARE ANONYMOUS NON-REFERENCED FUNCTIONS
		routines.removeIf(n -> n.getName() == null || !n.getName().equals(routineName));
		for (Node routine : routines) {
			Node module = edg.getAncestor(routine,Node.Type.Module);
			if (moduleName.equals("_") || moduleName.equals(module.getName())) {
@@ -417,20 +419,20 @@ public class InterproceduralEdgeGenerator extends EdgeGenerator
		if (possibleClauses.isEmpty()) {
			if (nameType == Node.Type.AnonymousRoutine) // (2.2) A CALL TO AN AD-HOC DEFINED ANONYMOUS FUNCTION
				possibleClauses.addAll(edg.getChildrenNonResult(name));
			else { // (2.3) CONNECT TO ALL POTENTIAL FUNCTION DEFINITIONS
				final List<Node> clauses = edg.getNodes(Node.Type.Clause);
				for (Node clause : clauses){
					Node clauseModule = edg.getAncestor(clause, Node.Type.Module);
					if (moduleName.equals("_") || moduleName.equals(clauseModule.getName())) {
						final Node clauseParameters = edg.getChild(clause, Node.Type.Parameters);
						final List<Node> parameters = edg.getChildrenNonResult(clauseParameters);
						if (arguments.size() != parameters.size())
							continue;

						possibleClauses.add(clause);
					}
				}
			}
//			else { // (2.3) CONNECT TO ALL POTENTIAL FUNCTION DEFINITIONS
//				final List<Node> clauses = edg.getNodes(Node.Type.Clause);
//				for (Node clause : clauses){
//					Node clauseModule = edg.getAncestor(clause, Node.Type.Module);
//					if (moduleName.equals("_") || moduleName.equals(clauseModule.getName())) {
//						final Node clauseParameters = edg.getChild(clause, Node.Type.Parameters);
//						final List<Node> parameters = edg.getChildrenNonResult(clauseParameters);
//						if (arguments.size() != parameters.size())
//							continue;
//
//						possibleClauses.add(clause);
//					}
//				}
//			}
		}

		return possibleClauses;
+51 −8
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ public class ControlFlowGenerator extends VoidVisitor<Void> implements Generator
	protected final Deque<Set<Node>> switchSelectorStack = new LinkedList<>();
	protected final Deque<Boolean> switchHasDefaultStack = new LinkedList<>();

	protected boolean absorbentCondition = false;
	protected boolean ifContext = false;

	protected int ttl = INFINITY;
	protected boolean generating = false;
	protected boolean parametersContext = false;
@@ -169,6 +172,11 @@ public class ControlFlowGenerator extends VoidVisitor<Void> implements Generator
	@Override
	public void visitSwitch(Node n, Void arg)
	{
		if (n.getInfo().getConstruction().equals("if"))
			ifContext = true;
		else
			ifContext = false;

		returnSet.push(new HashSet<>());
		switchStack.push(new HashSet<>());
		graph.getChild(n, Node.Type.Selector).accept(this, arg);
@@ -177,26 +185,44 @@ public class ControlFlowGenerator extends VoidVisitor<Void> implements Generator
		hangingNodes.addAll(returnSet.peek());
		connectTo(n);
		returnSet.pop();

		absorbentCondition = false;
		ifContext = false;
	}

	@Override
	public void visitCases(Node n, Void arg){
		prevCaseGuardHanging.push(new HashSet<>());
		super.visitCases(n, arg);
		returnSet.peek().addAll(Set.copyOf(prevCaseGuardHanging.peek()));
		prevCaseGuardHanging.pop();
	}

	@Override
	public void visitCase(Node n, Void arg)
	{

		if(!absorbentCondition) {
			hangingNodes.addAll(prevCaseGuardHanging.peekFirst());
			prevCaseGuardHanging.peek().clear();
			graph.getChild(n, Node.Type.Selectable).accept(this, arg);
			graph.getChild(n, Node.Type.Guard).accept(this, arg);
			prevCaseGuardHanging.peek().addAll(Set.copyOf(hangingNodes));

			boolean prevIfContext = ifContext;
			boolean prevAbsorbentCondition = absorbentCondition;
			graph.getChild(n, Node.Type.Body).accept(this, arg);
			absorbentCondition = prevAbsorbentCondition;
			ifContext = prevIfContext;

			returnSet.peek().addAll(Set.copyOf(hangingNodes));
		//connectTo(n);
		}
	}

	@Override
	public void visitGuard(Node n, Void arg){
		if (this.isIfTrueGuard(n))
			absorbentCondition = true;
		super.visitGuard(n,arg);
	}

	@Override
@@ -318,4 +344,21 @@ public class ControlFlowGenerator extends VoidVisitor<Void> implements Generator
		super.visitList(n, arg);
		connectTo(n);
	}

	private boolean isIfTrueGuard(Node n) {
		List<Node> guardChildren = graph.getChildren(n);
		if (guardChildren.isEmpty())
			return false;

		Node exprNode = graph.getChildren(
							graph.getChildren(
									guardChildren.get(0)
							).get(0)
						).get(0);
		return exprNode.getType() == Node.Type.Literal &&
				exprNode.getName().equals("true") &&
				ifContext;
	}


}
 No newline at end of file
+57 −36
Original line number Diff line number Diff line
@@ -183,7 +183,12 @@ public class ErlangCodeFactory
		clauseElements[1] = new OtpErlangLong(1);
		clauseElements[2] = this.parse(parameterChildren, true);
		clauseElements[3] = this.parseGuard(guard);
		clauseElements[4] = this.parse(expressions, false);
		OtpErlangList exprs = this.parse(expressions, false);
		if (exprs.elements().length == 0) {
			final OtpErlangObject[] expressionsElements = this.toArray(List.of(this.parseEmptyLiteral()));
			clauseElements[4] = new OtpErlangList(expressionsElements);
		} else
			clauseElements[4] = exprs;

		return new OtpErlangTuple(clauseElements);
	}
@@ -283,7 +288,7 @@ public class ErlangCodeFactory
			case Filter:
				return List.of(this.parseFilter(node));
			case Return:
				return List.of(this.parseReturn(node));
				return this.parseReturn(node);
			case Routine:
			case AnonymousRoutine:
				return List.of(this.parseAnonymousRoutine(node));
@@ -311,25 +316,30 @@ public class ErlangCodeFactory
		switch (construction)
		{
			case "if":
				return List.of(this.parseIf(_switch));
				return this.parseIf(_switch);
			case "case":
				return this.parseCase(_switch);
			default:
				throw new RuntimeException("Switch type not contemplated: " + construction);
		}
	}
	private OtpErlangTuple parseIf(Node _switch)
	private List<OtpErlangTuple> parseIf(Node _switch)
	{
		final Node cases = edg.getChild(_switch, Node.Type.Cases);

		if (slice != null && !slice.contains(cases))
			return List.of();

		final List<Node> casesChildren = edg.getChildrenNonResult(cases);
		final OtpErlangObject[] ifElements = new OtpErlangObject[3];

		ifElements[0] = new OtpErlangAtom("if");
		ifElements[1] = new OtpErlangLong(1);
		ifElements[2] = this.parseClauses(casesChildren);
		ifElements[2] = this.parseCaseClauses(casesChildren);

		return new OtpErlangTuple(ifElements);
		return List.of(new OtpErlangTuple(ifElements));
	}

	private List<OtpErlangTuple> parseCase(Node _switch)
	{
		// Case 1: Only assignments in the selector node (Cases Node is not part of the slice)
@@ -354,7 +364,7 @@ public class ErlangCodeFactory

	private OtpErlangList parseCaseClauses(List<Node> clauses)
	{
		final List<OtpErlangObject> clausesList = new LinkedList<OtpErlangObject>();
		final List<OtpErlangObject> clausesList = new LinkedList<>();

		for (Node clause : clauses)
		{
@@ -384,7 +394,12 @@ public class ErlangCodeFactory
		clauseElements[1] = new OtpErlangLong(1);
		clauseElements[2] = this.parse(selectables, true);
		clauseElements[3] = this.parseGuard(guard);
		clauseElements[4] = this.parse(expressions, false);
		OtpErlangList exprs = this.parse(expressions, false);
		if (exprs.elements().length == 0) {
			final OtpErlangObject[] expressionsElements = this.toArray(List.of(this.parseEmptyLiteral()));
			clauseElements[4] = new OtpErlangList(expressionsElements);
		} else
			clauseElements[4] = exprs;

		return new OtpErlangTuple(clauseElements);
	}
@@ -468,8 +483,9 @@ public class ErlangCodeFactory
		final Node operationExpression2 = edg.getChild(operation, 1);

		if (slice != null && !slice.contains(operation)) {
			List<OtpErlangTuple> leftExpr = this.parse(operationExpression1);
			List<OtpErlangTuple> rightExpr = this.parse(operationExpression2);
			List<OtpErlangTuple> leftExpr = new LinkedList<>();
			leftExpr.addAll(this.parse(operationExpression1, false));
			List<OtpErlangTuple> rightExpr = this.parse(operationExpression2, false);
			leftExpr.addAll(rightExpr);
			return leftExpr;
		}
@@ -495,6 +511,10 @@ public class ErlangCodeFactory
					throw new RuntimeException("The parsed element are not OtpTuples");
				containedArgs.add((OtpErlangTuple) parsedElem);
			}

			if (containedArgs.isEmpty() && edg.isPatternZone(dataConstructor))
				containedArgs.add(parseEmpty(dataConstructor));

			return containedArgs;
		}

@@ -507,6 +527,14 @@ public class ErlangCodeFactory
	}
	private List<OtpErlangTuple> parseList(Node list)
	{
		if (edg.getChildren(list).isEmpty())
		{
			final OtpErlangObject[] nilElements = new OtpErlangObject[2];
			nilElements[0] = new OtpErlangAtom("nil");
			nilElements[1] = new OtpErlangLong(1);
			return List.of(new OtpErlangTuple(nilElements));
		}

		final List<Node> listChildren = edg.getChildrenNonResult(list);
		if (slice != null && !slice.contains(list)) {
			final List<OtpErlangTuple> containedArgs = new LinkedList<>();
@@ -519,14 +547,6 @@ public class ErlangCodeFactory
			return containedArgs;
		}

		if (edg.getChildren(list).isEmpty())
		{
			final OtpErlangObject[] nilElements = new OtpErlangObject[2];
			nilElements[0] = new OtpErlangAtom("nil");
			nilElements[1] = new OtpErlangLong(1);
			return List.of(new OtpErlangTuple(nilElements));
		}

		final Node listHead = edg.getChild(list, Node.Type.Value);
		final Node listTail = edg.getChild(list, Node.Type.List);
		final OtpErlangObject[] listElements = new OtpErlangObject[4];
@@ -589,11 +609,10 @@ public class ErlangCodeFactory
		final Node filterExpr = edg.getChild(filter, Node.Type.Value);
		return this.parse(filterExpr).get(0);
	}
	private OtpErlangTuple parseReturn(Node returnNode)
	private List<OtpErlangTuple> parseReturn(Node returnNode)
	{
		final Node returnExpression = edg.getChild(returnNode, Node.Type.Value);

		return this.parse(returnExpression).get(0);
		return this.parse(returnExpression);
	}
	private OtpErlangTuple parseAnonymousRoutine(Node anonymousRoutine)
	{
@@ -624,21 +643,21 @@ public class ErlangCodeFactory
		}
		else
		{
			final Node clause = edg.getChild(anonymousRoutine, Node.Type.Clause);
			final Node body = edg.getChild(clause, Node.Type.Body); // TODO: Analyse the parsing of these routines
			final Node returnNode = edg.getChild(body, 0);
			final Node expression = edg.getChild(returnNode, 0);
			final Node call = edg.getChild(expression, 0);
			final Node callee = edg.getChild(call, 0);
			final Node scopeNode = edg.getChild(callee, 0);
			final List<Node> scopeChildren = edg.getChildren(scopeNode);
			final Node scope0 = scopeChildren.isEmpty() ? null : scopeChildren.get(0);
			final Node scope = scope0 == null ? null : edg.getChild(scope0, 0);
			final Node nameNode = edg.getChild(callee, 1);
			final Node name0 = edg.getChild(nameNode, 0);
			final Node name = name0 == null ? null : edg.getChild(name0, 0);
			final Node argumentsNode = edg.getChild(call, 1);
			final List<Node> arguments = edg.getChildren(argumentsNode);
			final List<Node> clauses = edg.getChildrenNonResult(anonymousRoutine);
			assert !clauses.isEmpty();
			final Node clause = clauses.get(0);
			final Node body = edg.getChild(clause, Node.Type.Body);
			final Node returnNode = edg.getChildren(body, Node.Type.Return).get(0);
			final Node call = edg.getChild(returnNode, Node.Type.Value);
			final Node callee = edg.getChild(call, Node.Type.Callee);
			final Node scopeNode = edg.getChild(callee, Node.Type.Scope);
			final Node scope = edg.getChildren(scopeNode).isEmpty() ? null :
					edg.getChild(scopeNode, Node.Type.Value);
			final Node nameNode = edg.getChild(callee, Node.Type.Name);
			final Node name = edg.getChildrenNonResult(nameNode).isEmpty() ? null :
					edg.getChild(nameNode, Node.Type.Value);
			final Node argumentsNode = edg.getChild(call, Node.Type.Arguments);
			final List<Node> arguments = edg.getChildrenNonResult(argumentsNode);
			final int arity = arguments.size();

			final OtpErlangObject[] funElements = new OtpErlangObject[3];
@@ -894,6 +913,8 @@ public class ErlangCodeFactory
			case Iterator:
			case DataConstructor:
			case DataConstructorAccess:
			case List:
			case Return:
				return true;
			default:
				return false;
+1 −0
Original line number Diff line number Diff line
@@ -222,6 +222,7 @@ public class ValueEdgeGenerator {
	private void generateRoutineEdges()
	{
		final List<Node> routines = this.last.getNodes(Node.Type.Routine);
		routines.addAll(this.last.getNodes(Node.Type.AnonymousRoutine));

		for (Node routine : routines)
		{