package com.crawljax.plugins.jsmodify;
import org.mozilla.javascript.CompilerEnvirons;
import org.mozilla.javascript.Parser;
import org.mozilla.javascript.ast.AstNode;
import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.Block;
import org.mozilla.javascript.ast.ForLoop;
import org.mozilla.javascript.ast.FunctionNode;
import org.mozilla.javascript.ast.IfStatement;
import org.mozilla.javascript.ast.Name;
import org.mozilla.javascript.ast.NodeVisitor;
import org.mozilla.javascript.ast.WhileLoop;
/**
* Abstract class that is used to define the interface and some functionality for the NodeVisitors
* that modify JavaScript.
*
*/
public abstract class JSASTModifier implements NodeVisitor {
/**
* This is used by the JavaScript node creation functions that follow.
*/
private CompilerEnvirons compilerEnvirons = new CompilerEnvirons();
/**
* Contains the scopename of the AST we are visiting. Generally this will be the filename
*/
private String scopeName = null;
/**
* @param scopeName
* the scopeName to set
*/
public void setScopeName(String scopeName) {
this.scopeName = scopeName;
}
/**
* @return the scopeName
*/
public String getScopeName() {
return scopeName;
}
/**
* Abstract constructor to initialize the mapper variable.
*/
public JSASTModifier() {
}
/**
* Parse some JavaScript to a simple AST.
*
* @param code
* The JavaScript source code to parse.
* @return The AST node.
*/
public AstNode parse(String code) {
Parser p = new Parser(compilerEnvirons, null);
return p.parse(code, null, 0);
}
/**
* Find out the function name of a certain node and return "anonymous" if it's an anonymous
* function.
*
* @param f
* The function node.
* @return The function name.
*/
protected String getFunctionName(FunctionNode f) {
Name functionName = f.getFunctionName();
if (functionName == null) {
return "anonymous" + f.getLineno();
} else {
return functionName.toSource();
}
}
/**
* Creates a node that can be inserted at a certain point in function.
*
* @param function
* The function that will enclose the node.
*
* @param lineNo
* Linenumber where the node will be inserted.
* @return The new node.
*/
public abstract AstNode createNodeInFunction(FunctionNode function, int lineNo);
public abstract AstNode createNode(AstNode... node);
/**
*
*
* @param shouldLog
* The variable that should be logged (for example jQuery('#test').attr('style'))
* @param lineNo
* The line number where this will be inserted.
* @return The new node.
*/
public abstract AstNode createPointNode(String shouldLog, int lineNo);
/**
* Create a new block node with two children.
*
* @param node
* The child.
* @return The new block.
*/
private Block createBlockWithNode(AstNode node) {
Block b = new Block();
b.addChild(node);
return b;
}
/**
* @param node
* The node we want to have wrapped.
* @return The (new) node parent (the block probably)
*/
public AstNode makeSureBlockExistsAround(AstNode node) {
AstNode parent = node.getParent();
if (parent instanceof IfStatement) {
/* the parent is an if and there are no braces, so we should make a new block */
IfStatement i = (IfStatement) parent;
/* replace the if or the then, depending on what the current node is */
if (i.getThenPart().equals(node)) {
i.setThenPart(createBlockWithNode(node));
} else {
i.setElsePart(createBlockWithNode(node));
}
} else if (parent instanceof WhileLoop) {
/* the parent is a while and there are no braces, so we should make a new block */
/* I don't think you can find this in the real world, but just to be sure */
WhileLoop w = (WhileLoop) parent;
w.setBody(createBlockWithNode(node));
} else if (parent instanceof ForLoop) {
/* the parent is a for and there are no braces, so we should make a new block */
/* I don't think you can find this in the real world, but just to be sure */
ForLoop f = (ForLoop) parent;
f.setBody(createBlockWithNode(node));
}
return node.getParent();
}
/**
* Actual visiting method.
*
* @param node
* The node that is currently visited.
* @return Whether to visit the children.
*/
@Override
public abstract boolean visit(AstNode node);
/**
* This method is called when the complete AST has been traversed.
*
* @param node
* The AST root node.
*/
public abstract void finish(AstRoot node);
/**
* This method is called before the AST is going to be traversed.
*/
public abstract void start();
}