/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.visit;
import java.util.*;
import polyglot.ast.*;
import polyglot.frontend.Compiler;
import polyglot.util.CodeWriter;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
/**
* The <code>NodeScrambler</code> is test case generator of sorts. Since it
* is ofter useful to introduce ``random'' errors into source code, this
* class provides a way of doing so in a semi-structed manner. The process
* takes place in two phases. First, a "FirstPass" is made to collect
* a list of nodes and their parents. Then a second pass is made to randomly
* replace a branch of the tree with another suitable branch.
*/
public class NodeScrambler extends NodeVisitor
{
public FirstPass fp;
protected Map<Node, LinkedList<Node>> pairs;
protected LinkedList<Node> nodes;
protected LinkedList<Node> currentParents;
protected long seed;
protected Random ran;
protected boolean scrambled = false;
protected CodeWriter cw;
public NodeScrambler()
{
this(new Random().nextLong());
}
/**
* Create a new <code>NodeScrambler</code> with the given random number
* generator seed.
*/
public NodeScrambler( long seed)
{
this.fp = new FirstPass();
this.pairs = CollectionFactory.newHashMap();
this.nodes = new LinkedList<Node>();
this.currentParents = new LinkedList<Node>();
this.cw = Compiler.createCodeWriter(System.err, 72);
this.seed = seed;
this.ran = new Random( seed);
}
/**
* Scans throught the AST, create a list of all nodes present, along with
* the set of parents for each node in the tree. <b>This visitor should be
* run before the main <code>NodeScrambler</code> visits the tree.</b>
*/
public class FirstPass extends NodeVisitor
{
@SuppressWarnings("unchecked") // Casting to a generic type
public NodeVisitor enter( Node n)
{
pairs.put( n, (LinkedList<Node>) currentParents.clone());
nodes.add( n);
currentParents.add( n);
return this;
}
public Node leave( Node old, Node n, NodeVisitor v)
{
currentParents.remove( n);
return n;
}
}
public long getSeed()
{
return seed;
}
public Node override( Node n)
{
if( coinFlip()) {
Node m = potentialScramble( n);
if( m == null) {
/* No potential replacement. */
return null;
}
else {
scrambled = true;
try {
System.err.println( "Replacing:");
n.del().dump(System.err);
System.err.println( "With:");
m.del().dump(System.err);
}
catch( Exception e)
{
e.printStackTrace();
return null;
}
return m;
}
}
else {
return null;
}
}
protected boolean coinFlip()
{
if( scrambled) {
return false;
}
else {
if( ran.nextDouble() > 0.9) {
return true;
}
else {
return false;
}
}
}
protected Node potentialScramble( Node n)
{
Class<?> required = Node.class;
if( n instanceof SourceFile) {
return null;
}
if( n instanceof Import) {
required = Import.class;
}
else if( n instanceof TypeNode) {
required = TypeNode.class;
}
else if( n instanceof ClassDecl) {
required = ClassDecl.class;
}
else if( n instanceof ClassMember) {
required = ClassMember.class;
}
else if( n instanceof Formal) {
required = Formal.class;
}
else if( n instanceof Expr) {
required = Expr.class;
}
else if( n instanceof Block) {
required = Block.class;
}
else if( n instanceof Catch) {
required = Catch.class;
}
else if( n instanceof LocalDecl) {
required = LocalDecl.class;
}
else if( n instanceof Stmt) {
required = Stmt.class;
}
LinkedList<Node> parents = (LinkedList<Node>)pairs.get( n);
boolean isParent;
for (Node m : nodes) {
if( required.isAssignableFrom( m.getClass())) {
isParent = false;
for (Node p : parents) {
if( m == p) {
isParent = true;
}
}
if( !isParent && m != n) {
return m;
}
}
}
return null;
}
}