diff --git a/reusablecfg/src/main/jastadd/CFG.ast b/reusablecfg/src/main/jastadd/CFG.ast deleted file mode 100644 index b1e7cbe5349021cc8790f4a6ee2613ee6b1eb168..0000000000000000000000000000000000000000 --- a/reusablecfg/src/main/jastadd/CFG.ast +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** A node in a Control Flow Graph (CFG). */ -abstract CfgNode; - -/** The CFG entry node. */ -CfgEntry : CfgNode ::= <Succ:CfgNode>; - -/** The CFG exit node. */ -CfgExit : CfgNode; - -/** A method call in the CFG. */ -CfgMethodCall : CfgNode; - -/** A conditional branch in the CFG. */ -CfgBranch : CfgNode; - -/** A branch in the CFG caused by potential thrown exceptions. */ -CfgException : CfgNode; - -/** A marker node used to mark try block entry points or the end of if-statement branches. */ -CfgMarker : CfgNode; diff --git a/reusablecfg/src/main/jastadd/CFG.relast b/reusablecfg/src/main/jastadd/CFG.relast new file mode 100644 index 0000000000000000000000000000000000000000..f34de746a7373ea0c4defeb1b10e9288eae4f002 --- /dev/null +++ b/reusablecfg/src/main/jastadd/CFG.relast @@ -0,0 +1,26 @@ +CFG ::= CfgNode*; + +/** A node in a Control Flow Graph (CFG). */ +abstract CfgNode; + +/** The CFG entry node. */ +CfgEntry : CfgNode ::= <Succ:CfgNode>; + +/** The CFG exit node. */ +CfgExit : CfgNode; + +/** A method call in the CFG. */ +CfgMethodCall : CfgNode; + +/** A conditional branch in the CFG. */ +CfgBranch : CfgNode; + +/** A branch in the CFG caused by potential thrown exceptions. */ +CfgException : CfgNode; + +/** A marker node used to mark try block entry points or the end of if-statement branches. */ +CfgMarker : CfgNode; + +rel CfgNode.successor <-> CfgNode.predecessor ; + +rel CfgEntry -> diff --git a/reusablecfg/src/main/jastadd/ReusableCFG.jadd b/reusablecfg/src/main/jastadd/ReusableCFG.jadd new file mode 100644 index 0000000000000000000000000000000000000000..ecc7306b3e23f541a7d183692011d27624e68fbc --- /dev/null +++ b/reusablecfg/src/main/jastadd/ReusableCFG.jadd @@ -0,0 +1,103 @@ +aspect ReusableCfg { + + /** + * Set that contains either one or two unique objects. The objects are + * compared with reference equality. + */ + class IdentityTupleSet<E> implements Set<E> { + final E a, b; + + public IdentityTupleSet(E a, E b) { + this.a = a; + this.b = b; + } + + @Override + public boolean add(E e) { + throw new UnsupportedOperationException(); + } + @Override + public boolean addAll(Collection<? extends E> e) { + throw new UnsupportedOperationException(); + } + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + @Override + public boolean contains(Object o) { + return o == a || o == b; + } + @Override + public boolean containsAll(Collection<?> c) { + for (Object o : c) { + if (!contains(o)) { + return false; + } + } + return true; + } + @Override + public int hashCode() { + return a.hashCode() + b.hashCode(); + } + @Override + public boolean isEmpty() { + return false; + } + @Override + public Iterator<E> iterator() { + return new Iterator<E>() { + int index = 0; + @Override + public boolean hasNext() { + return (a == b && index < 1) + || (a != b && index < 2); + } + @Override + public E next() { + return ++index == 1 ? a : b; + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + @Override + public boolean removeAll(Collection<?> c) { + throw new UnsupportedOperationException(); + } + @Override + public boolean retainAll(Collection<?> c) { + throw new UnsupportedOperationException(); + } + @Override + public int size() { + return a == b ? 1 : 2; + } + @Override + public Object[] toArray() { + return new Object[] { a, b }; + } + @Override + public <T> T[] toArray(T[] array) { + array[0] = (T) a; + array[1] = (T) b; + return array; + } + @Override + public String toString() { + if (a == b) { + return "[" + a + "]"; + } else { + return "[" + a + ", " + b + "]"; + } + } + } + +} diff --git a/reusablecfg/src/main/jastadd/ReusableCfg.jrag b/reusablecfg/src/main/jastadd/ReusableCfg.jrag new file mode 100644 index 0000000000000000000000000000000000000000..0e700ba7233991ee498ad35c7d9abf7387984345 --- /dev/null +++ b/reusablecfg/src/main/jastadd/ReusableCfg.jrag @@ -0,0 +1,138 @@ +class ReusableCfg { + /** + * Find the next CFG node representing the next branch, or the next + * method access following this statement. + */ + inh CfgNode Stmt.follow(); + inh CfgNode Expr.follow(); + + // Needed for completeness, but never used by anything relevant. + eq Program.getChild().follow() = new CfgExit(); + + + eq TryStmt.getBlock().follow() = + hasNonEmptyFinally() + ? getFinally().entry() + : follow(); + + eq TryStmt.getCatchClause(int index).follow() = + hasNonEmptyFinally() + ? getFinally().entry() + : follow(); + + eq BreakStmt.marker().follow() = + hasFinally() + ? getFinally().entry() + : targetStmt().follow(); + + eq ReturnStmt.marker().follow() = + hasResult() + ? getResult().entry() + : returnTarget(); + + eq ReturnStmt.getResult().follow() = returnTarget(); + + eq ReturnStmt.getFinally().follow() = methodExit(); + + // Since we not have precise type lookups we need to approximate the possible + // exception branches. + eq ThrowStmt.getExpr().follow() = exceptionNode(); + + eq ClassInstanceExpr.getArg(int index).follow() = + index+1 < getNumArg() + ? getArg(index+1).entry() + : getAccess().entry(); + + // If we have arguments the CfgMethodCall is placed after the last argument. + eq MethodAccess.getArg(int index).follow() = + index+1 < getNumArg() + ? getArg(index+1).entry() + : call(); + + // If we have arguments the CfgMethodCall is placed after the last argument. + eq ConstructorAccess.getArg(int index).follow() = + index+1 < getNumArg() + ? getArg(index+1).entry() + : follow(); + + eq ArrayInit.getInit(int index).follow() = + index+1 < getNumInit() + ? getInit(index+1).entry() + : follow(); + + eq AssignExpr.getSource().follow() = getDest().entry(); + + + eq Binary.getLeftOperand().follow() = getRightOperand().entry(); + + + eq Dot.getLeft().follow() = getRight().entry(); + + + eq ConditionalExpr.getCondition().follow() = branch(); + eq ConditionalExpr.getTrueExpr().follow() = thenEndMarker(); + eq ConditionalExpr.getFalseExpr().follow() = elseEndMarker(); + eq ConditionalExpr.thenEndMarker().follow() = follow(); + eq ConditionalExpr.elseEndMarker().follow() = follow(); + + + eq IfStmt.getCondition().follow() = branch(); + eq IfStmt.getThen().follow() = thenEndMarker(); + eq IfStmt.getElse().follow() = elseEndMarker(); + eq IfStmt.thenEndMarker().follow() = follow(); + eq IfStmt.elseEndMarker().follow() = follow(); + + eq ForStmt.getInitStmt(int index).follow() = + index+1 < getNumInitStmt() + ? getInitStmt(index+1).entry() + : getCondition().entry(); + + eq ForStmt.getCondition().follow() = branch(); + + eq ForStmt.getUpdateStmt(int index).follow() = + index+1 < getNumUpdateStmt() + ? getUpdateStmt(index+1).entry() + : getCondition().entry(); + + eq ForStmt.getStmt().follow() = loopEndMarker(); + + eq ForStmt.loopEndMarker().follow() = + getNumUpdateStmt() > 0 + ? getUpdateStmt(0).entry() + : getCondition().entry(); + + + eq EnhancedForStmt.getExpr().follow() = branch(); + + eq EnhancedForStmt.getStmt().follow() = loopEndMarker(); + + eq EnhancedForStmt.loopEndMarker().follow() = entry(); // Loop back. + + eq WhileStmt.getCondition().follow() = branch(); + + eq WhileStmt.getStmt().follow() = loopEndMarker(); + + eq WhileStmt.loopEndMarker().follow() = entry(); // Loop back. + + + eq DoStmt.doEntryMarker().follow() = getStmt().entry(); + + eq DoStmt.getStmt().follow() = getCondition().entry(); + + eq DoStmt.getCondition().follow() = branch(); + + + eq SwitchStmt.getExpr().follow() = branch(); + + eq BodyDecl.getChild().follow() = exit(); + + eq Block.getStmt(int index).follow() = + index+1 < getNumStmt() + ? getStmt(index+1).entry() + : follow(); + + + eq BlockLambdaBody.getBlock().follow() = exit(); + eq ExprLambdaBody.getExpr().follow() = exit(); + +} diff --git a/reusablecfg/src/main/jastadd/SimpleCFG.jrag b/reusablecfg/src/main/jastadd/SimpleCFG.jrag index 162622bb10fa429e861076736a12e726958f7378..68474b5db41cd395175968d2a444dd0d60ca3880 100644 --- a/reusablecfg/src/main/jastadd/SimpleCFG.jrag +++ b/reusablecfg/src/main/jastadd/SimpleCFG.jrag @@ -59,106 +59,6 @@ aspect SimpleCFG { eq CfgMarker.successors() = succ(); - /** - * Set that contains either one or two unique objects. The objects are - * compared with reference equality. - */ - class IdentityTupleSet<E> implements Set<E> { - final E a, b; - - public IdentityTupleSet(E a, E b) { - this.a = a; - this.b = b; - } - - @Override - public boolean add(E e) { - throw new UnsupportedOperationException(); - } - @Override - public boolean addAll(Collection<? extends E> e) { - throw new UnsupportedOperationException(); - } - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - @Override - public boolean contains(Object o) { - return o == a || o == b; - } - @Override - public boolean containsAll(Collection<?> c) { - for (Object o : c) { - if (!contains(o)) { - return false; - } - } - return true; - } - @Override - public int hashCode() { - return a.hashCode() + b.hashCode(); - } - @Override - public boolean isEmpty() { - return false; - } - @Override - public Iterator<E> iterator() { - return new Iterator<E>() { - int index = 0; - @Override - public boolean hasNext() { - return (a == b && index < 1) - || (a != b && index < 2); - } - @Override - public E next() { - return ++index == 1 ? a : b; - } - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - @Override - public boolean remove(Object o) { - throw new UnsupportedOperationException(); - } - @Override - public boolean removeAll(Collection<?> c) { - throw new UnsupportedOperationException(); - } - @Override - public boolean retainAll(Collection<?> c) { - throw new UnsupportedOperationException(); - } - @Override - public int size() { - return a == b ? 1 : 2; - } - @Override - public Object[] toArray() { - return new Object[] { a, b }; - } - @Override - public <T> T[] toArray(T[] array) { - array[0] = (T) a; - array[1] = (T) b; - return array; - } - @Override - public String toString() { - if (a == b) { - return "[" + a + "]"; - } else { - return "[" + a + ", " + b + "]"; - } - } - } - /** Successors to this branch node. */ inh Set<? extends CfgNode> CfgBranch.succ(); @@ -181,16 +81,9 @@ aspect SimpleCFG { */ syn CfgNode Stmt.entry() = follow(); - /** - * Find the next CFG node representing the next branch, or the next - * method access following this statement. - */ - inh CfgNode Stmt.follow(); - inh CfgNode Expr.follow(); - // Needed for completeness, but never used by anything relevant. - eq Program.getChild().follow() = new CfgExit(); + /** * The entry node in a filtered CFG. @@ -327,15 +220,6 @@ aspect SimpleCFG { return set; } - eq TryStmt.getBlock().follow() = - hasNonEmptyFinally() - ? getFinally().entry() - : follow(); - - eq TryStmt.getCatchClause(int index).follow() = - hasNonEmptyFinally() - ? getFinally().entry() - : follow(); /** The CFG marker for this break statement. */ syn nta CfgMarker BreakStmt.marker() = new CfgMarker(); @@ -345,10 +229,6 @@ aspect SimpleCFG { hasFinally() ? Collections.singleton(getFinally().entry()) : Collections.singleton(targetStmt().follow()); - eq BreakStmt.marker().follow() = - hasFinally() - ? getFinally().entry() - : targetStmt().follow(); eq BreakStmt.getFinally().follow() = targetStmt().follow(); @@ -375,12 +255,6 @@ aspect SimpleCFG { hasResult() ? Collections.singleton(getResult().entry()) : Collections.singleton(returnTarget()); - eq ReturnStmt.marker().follow() = - hasResult() - ? getResult().entry() - : returnTarget(); - - eq ReturnStmt.getResult().follow() = returnTarget(); inh CompilationUnit ReturnStmt.compilationUnit(); @@ -389,8 +263,6 @@ aspect SimpleCFG { ? getFinally().entry() : methodExit(); - eq ReturnStmt.getFinally().follow() = methodExit(); - /** * Finds the CFG exit node for the enclosing method, constructor, or * initializer. @@ -403,10 +275,6 @@ aspect SimpleCFG { eq ThrowStmt.entry() = getExpr().entry(); - // Since we not have precise type lookups we need to approximate the possible - // exception branches. - eq ThrowStmt.getExpr().follow() = exceptionNode(); - /** * This node represents the control flow path taken when an exception * interrupts the call. @@ -468,44 +336,22 @@ aspect SimpleCFG { ? getArg(0).entry() : call(); - // If we have arguments the CfgMethodCall is placed after the last argument. - eq MethodAccess.getArg(int index).follow() = - index+1 < getNumArg() - ? getArg(index+1).entry() - : call(); - // If there are arguments control flow passes to the arguments first. eq ConstructorAccess.entry() = getNumArg() > 0 ? getArg(0).entry() : follow(); - // If we have arguments the CfgMethodCall is placed after the last argument. - eq ConstructorAccess.getArg(int index).follow() = - index+1 < getNumArg() - ? getArg(index+1).entry() - : follow(); - eq ClassInstanceExpr.entry() = getNumArg() > 0 ? getArg(0).entry() : getAccess().entry(); - eq ClassInstanceExpr.getArg(int index).follow() = - index+1 < getNumArg() - ? getArg(index+1).entry() - : getAccess().entry(); - eq ArrayInit.entry() = getNumInit() > 0 ? getInit(0).entry() : follow(); - eq ArrayInit.getInit(int index).follow() = - index+1 < getNumInit() - ? getInit(index+1).entry() - : follow(); - /** @return {@code true} if this statement is inside a try block. */ inh boolean MethodAccess.isInsideTryBlockOrResource(); inh boolean Stmt.isInsideTryBlockOrResource(); @@ -524,8 +370,6 @@ aspect SimpleCFG { eq AssignExpr.entry() = getSource().entry(); - eq AssignExpr.getSource().follow() = getDest().entry(); - eq Literal.entry() = follow(); eq ArrayCreationExpr.entry() = @@ -534,10 +378,8 @@ aspect SimpleCFG { : follow(); eq Binary.entry() = getLeftOperand().entry(); - eq Binary.getLeftOperand().follow() = getRightOperand().entry(); eq Dot.entry() = getLeft().entry(); - eq Dot.getLeft().follow() = getRight().entry(); /** The branch node for this conditional expression. */ syn nta CfgBranch ConditionalExpr.branch() = new CfgBranch(); @@ -555,11 +397,6 @@ aspect SimpleCFG { syn nta CfgMarker ConditionalExpr.elseEndMarker() = new CfgMarker(); eq ConditionalExpr.entry() = getCondition().entry(); - eq ConditionalExpr.getCondition().follow() = branch(); - eq ConditionalExpr.getTrueExpr().follow() = thenEndMarker(); - eq ConditionalExpr.getFalseExpr().follow() = elseEndMarker(); - eq ConditionalExpr.thenEndMarker().follow() = follow(); - eq ConditionalExpr.elseEndMarker().follow() = follow(); eq ConditionalExpr.thenEndMarker().succ() = Collections.singleton(follow()); eq ConditionalExpr.elseEndMarker().succ() = Collections.singleton(follow()); @@ -577,11 +414,6 @@ aspect SimpleCFG { eq IfStmt.entry() = getCondition().entry(); - eq IfStmt.getCondition().follow() = branch(); - eq IfStmt.getThen().follow() = thenEndMarker(); - eq IfStmt.getElse().follow() = elseEndMarker(); - eq IfStmt.thenEndMarker().follow() = follow(); - eq IfStmt.elseEndMarker().follow() = follow(); eq IfStmt.thenEndMarker().succ() = Collections.singleton(follow()); eq IfStmt.elseEndMarker().succ() = Collections.singleton(follow()); @@ -601,25 +433,6 @@ aspect SimpleCFG { ? getInitStmt(0).entry() : getCondition().entry(); - eq ForStmt.getInitStmt(int index).follow() = - index+1 < getNumInitStmt() - ? getInitStmt(index+1).entry() - : getCondition().entry(); - - eq ForStmt.getCondition().follow() = branch(); - - eq ForStmt.getUpdateStmt(int index).follow() = - index+1 < getNumUpdateStmt() - ? getUpdateStmt(index+1).entry() - : getCondition().entry(); - - eq ForStmt.getStmt().follow() = loopEndMarker(); - - eq ForStmt.loopEndMarker().follow() = - getNumUpdateStmt() > 0 - ? getUpdateStmt(0).entry() - : getCondition().entry(); - eq ForStmt.loopEndMarker().succ() = getNumUpdateStmt() > 0 ? Collections.singleton(getUpdateStmt(0).entry()) @@ -646,12 +459,6 @@ aspect SimpleCFG { eq EnhancedForStmt.entry() = getExpr().entry(); - eq EnhancedForStmt.getExpr().follow() = branch(); - - eq EnhancedForStmt.getStmt().follow() = loopEndMarker(); - - eq EnhancedForStmt.loopEndMarker().follow() = entry(); // Loop back. - eq EnhancedForStmt.loopEndMarker().succ() = Collections.singleton(entry()); /** The branch node for this statement. */ @@ -662,12 +469,6 @@ aspect SimpleCFG { eq WhileStmt.entry() = getCondition().entry(); - eq WhileStmt.getCondition().follow() = branch(); - - eq WhileStmt.getStmt().follow() = loopEndMarker(); - - eq WhileStmt.loopEndMarker().follow() = entry(); // Loop back. - eq WhileStmt.loopEndMarker().succ() = Collections.singleton(entry()); eq WhileStmt.branch().succ() { @@ -688,14 +489,9 @@ aspect SimpleCFG { eq DoStmt.entry() = doEntryMarker(); - eq DoStmt.doEntryMarker().follow() = getStmt().entry(); eq DoStmt.doEntryMarker().succ() = Collections.singleton(getStmt().entry()); - eq DoStmt.getStmt().follow() = getCondition().entry(); - - eq DoStmt.getCondition().follow() = branch(); - // Loop back. eq DoStmt.branch().succ() { if (getCondition().isTrue()) { @@ -711,8 +507,6 @@ aspect SimpleCFG { eq SwitchStmt.entry() = getExpr().entry(); - eq SwitchStmt.getExpr().follow() = branch(); - eq SwitchStmt.branch().succ() { Set<CfgNode> set = Collections.newSetFromMap( new IdentityHashMap<CfgNode, Boolean>()); @@ -731,22 +525,12 @@ aspect SimpleCFG { return set; } - eq BodyDecl.getChild().follow() = exit(); - - eq Block.getStmt(int index).follow() = - index+1 < getNumStmt() - ? getStmt(index+1).entry() - : follow(); - syn lazy CfgEntry LambdaBody.entry(); eq BlockLambdaBody.entry() = new CfgEntry(getBlock().entry()); eq ExprLambdaBody.entry() = new CfgEntry(getExpr().entry()); syn nta CfgExit LambdaBody.exit() = new CfgExit(); - eq BlockLambdaBody.getBlock().follow() = exit(); - eq ExprLambdaBody.getExpr().follow() = exit(); - /** Find the method access which this call node is associated with. */ inh MethodAccess CfgMethodCall.methodAccess(); eq MethodAccess.call().methodAccess() = this;