--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java 2020-04-15 18:48:17.000000000 +0530 +++ /dev/null 2020-04-15 18:48:17.000000000 +0530 @@ -1,1082 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.internal.ir.debug; - -import static jdk.nashorn.internal.runtime.Source.sourceFor; - -import java.util.ArrayList; -import java.util.List; -import jdk.nashorn.internal.ir.AccessNode; -import jdk.nashorn.internal.ir.BinaryNode; -import jdk.nashorn.internal.ir.Block; -import jdk.nashorn.internal.ir.BlockStatement; -import jdk.nashorn.internal.ir.BreakNode; -import jdk.nashorn.internal.ir.CallNode; -import jdk.nashorn.internal.ir.CaseNode; -import jdk.nashorn.internal.ir.CatchNode; -import jdk.nashorn.internal.ir.ContinueNode; -import jdk.nashorn.internal.ir.DebuggerNode; -import jdk.nashorn.internal.ir.EmptyNode; -import jdk.nashorn.internal.ir.Expression; -import jdk.nashorn.internal.ir.ExpressionStatement; -import jdk.nashorn.internal.ir.ForNode; -import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.IdentNode; -import jdk.nashorn.internal.ir.IfNode; -import jdk.nashorn.internal.ir.IndexNode; -import jdk.nashorn.internal.ir.JoinPredecessorExpression; -import jdk.nashorn.internal.ir.LabelNode; -import jdk.nashorn.internal.ir.LiteralNode; -import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.ObjectNode; -import jdk.nashorn.internal.ir.PropertyNode; -import jdk.nashorn.internal.ir.ReturnNode; -import jdk.nashorn.internal.ir.RuntimeNode; -import jdk.nashorn.internal.ir.SplitNode; -import jdk.nashorn.internal.ir.Statement; -import jdk.nashorn.internal.ir.SwitchNode; -import jdk.nashorn.internal.ir.TernaryNode; -import jdk.nashorn.internal.ir.ThrowNode; -import jdk.nashorn.internal.ir.TryNode; -import jdk.nashorn.internal.ir.UnaryNode; -import jdk.nashorn.internal.ir.VarNode; -import jdk.nashorn.internal.ir.WhileNode; -import jdk.nashorn.internal.ir.WithNode; -import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; -import jdk.nashorn.internal.parser.JSONParser; -import jdk.nashorn.internal.parser.Lexer.RegexToken; -import jdk.nashorn.internal.parser.Parser; -import jdk.nashorn.internal.parser.TokenType; -import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.ParserException; -import jdk.nashorn.internal.runtime.Source; - -/** - * This IR writer produces a JSON string that represents AST as a JSON string. - */ -public final class JSONWriter extends SimpleNodeVisitor { - - /** - * Returns AST as JSON compatible string. - * - * @param context context - * @param code code to be parsed - * @param name name of the code source (used for location) - * @param includeLoc tells whether to include location information for nodes or not - * @return JSON string representation of AST of the supplied code - */ - public static String parse(final Context context, final String code, final String name, final boolean includeLoc) { - final Parser parser = new Parser(context.getEnv(), sourceFor(name, code), new Context.ThrowErrorManager(), context.getEnv()._strict, context.getLogger(Parser.class)); - final JSONWriter jsonWriter = new JSONWriter(includeLoc); - try { - final FunctionNode functionNode = parser.parse(); //symbol name is ":program", default - functionNode.accept(jsonWriter); - return jsonWriter.getString(); - } catch (final ParserException e) { - e.throwAsEcmaException(); - return null; - } - } - - @Override - public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinPredecessorExpression) { - final Expression expr = joinPredecessorExpression.getExpression(); - if(expr != null) { - expr.accept(this); - } else { - nullValue(); - } - return false; - } - - @Override - protected boolean enterDefault(final Node node) { - objectStart(); - location(node); - - return true; - } - - private boolean leave() { - objectEnd(); - return false; - } - - @Override - protected Node leaveDefault(final Node node) { - objectEnd(); - return null; - } - - @Override - public boolean enterAccessNode(final AccessNode accessNode) { - enterDefault(accessNode); - - type("MemberExpression"); - comma(); - - property("object"); - accessNode.getBase().accept(this); - comma(); - - property("property", accessNode.getProperty()); - comma(); - - property("computed", false); - - return leave(); - } - - @Override - public boolean enterBlock(final Block block) { - enterDefault(block); - - type("BlockStatement"); - comma(); - - array("body", block.getStatements()); - - return leave(); - } - - @Override - public boolean enterBinaryNode(final BinaryNode binaryNode) { - enterDefault(binaryNode); - - final String name; - if (binaryNode.isAssignment()) { - name = "AssignmentExpression"; - } else if (binaryNode.isLogical()) { - name = "LogicalExpression"; - } else { - name = "BinaryExpression"; - } - - type(name); - comma(); - - property("operator", binaryNode.tokenType().getName()); - comma(); - - property("left"); - binaryNode.lhs().accept(this); - comma(); - - property("right"); - binaryNode.rhs().accept(this); - - return leave(); - } - - @Override - public boolean enterBreakNode(final BreakNode breakNode) { - enterDefault(breakNode); - - type("BreakStatement"); - comma(); - - final String label = breakNode.getLabelName(); - if(label != null) { - property("label", label); - } else { - property("label"); - nullValue(); - } - - return leave(); - } - - @Override - public boolean enterCallNode(final CallNode callNode) { - enterDefault(callNode); - - type("CallExpression"); - comma(); - - property("callee"); - callNode.getFunction().accept(this); - comma(); - - array("arguments", callNode.getArgs()); - - return leave(); - } - - @Override - public boolean enterCaseNode(final CaseNode caseNode) { - enterDefault(caseNode); - - type("SwitchCase"); - comma(); - - final Node test = caseNode.getTest(); - property("test"); - if (test != null) { - test.accept(this); - } else { - nullValue(); - } - comma(); - - array("consequent", caseNode.getBody().getStatements()); - - return leave(); - } - - @Override - public boolean enterCatchNode(final CatchNode catchNode) { - enterDefault(catchNode); - - type("CatchClause"); - comma(); - - property("param"); - catchNode.getException().accept(this); - comma(); - - final Node guard = catchNode.getExceptionCondition(); - if (guard != null) { - property("guard"); - guard.accept(this); - comma(); - } - - property("body"); - catchNode.getBody().accept(this); - - return leave(); - } - - @Override - public boolean enterContinueNode(final ContinueNode continueNode) { - enterDefault(continueNode); - - type("ContinueStatement"); - comma(); - - final String label = continueNode.getLabelName(); - if(label != null) { - property("label", label); - } else { - property("label"); - nullValue(); - } - - return leave(); - } - - @Override - public boolean enterDebuggerNode(final DebuggerNode debuggerNode) { - enterDefault(debuggerNode); - type("DebuggerStatement"); - return leave(); - } - - @Override - public boolean enterEmptyNode(final EmptyNode emptyNode) { - enterDefault(emptyNode); - - type("EmptyStatement"); - - return leave(); - } - - @Override - public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) { - // handle debugger statement - final Node expression = expressionStatement.getExpression(); - if (expression instanceof RuntimeNode) { - assert false : "should not reach here: RuntimeNode"; - return false; - } - - enterDefault(expressionStatement); - - type("ExpressionStatement"); - comma(); - - property("expression"); - expression.accept(this); - - return leave(); - } - - @Override - public boolean enterBlockStatement(final BlockStatement blockStatement) { - if (blockStatement.isSynthetic()) { - final Block blk = blockStatement.getBlock(); - blk.getStatements().get(0).accept(this); - return false; - } - - enterDefault(blockStatement); - - type("BlockStatement"); - comma(); - - array("body", blockStatement.getBlock().getStatements()); - return leave(); - } - - @Override - public boolean enterForNode(final ForNode forNode) { - enterDefault(forNode); - - if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) { - type("ForInStatement"); - comma(); - - final Node init = forNode.getInit(); - assert init != null; - property("left"); - init.accept(this); - comma(); - - final Node modify = forNode.getModify(); - assert modify != null; - property("right"); - modify.accept(this); - comma(); - - property("body"); - forNode.getBody().accept(this); - comma(); - - property("each", forNode.isForEach()); - } else { - type("ForStatement"); - comma(); - - final Node init = forNode.getInit(); - property("init"); - if (init != null) { - init.accept(this); - } else { - nullValue(); - } - comma(); - - final Node test = forNode.getTest(); - property("test"); - if (test != null) { - test.accept(this); - } else { - nullValue(); - } - comma(); - - final Node update = forNode.getModify(); - property("update"); - if (update != null) { - update.accept(this); - } else { - nullValue(); - } - comma(); - - property("body"); - forNode.getBody().accept(this); - } - - return leave(); - } - - @Override - public boolean enterFunctionNode(final FunctionNode functionNode) { - final boolean program = functionNode.isProgram(); - if (program) { - return emitProgram(functionNode); - } - - enterDefault(functionNode); - final String name; - if (functionNode.isDeclared()) { - name = "FunctionDeclaration"; - } else { - name = "FunctionExpression"; - } - type(name); - comma(); - - property("id"); - final FunctionNode.Kind kind = functionNode.getKind(); - if (functionNode.isAnonymous() || kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) { - nullValue(); - } else { - functionNode.getIdent().accept(this); - } - comma(); - - array("params", functionNode.getParameters()); - comma(); - - arrayStart("defaults"); - arrayEnd(); - comma(); - - property("rest"); - nullValue(); - comma(); - - property("body"); - functionNode.getBody().accept(this); - comma(); - - property("generator", false); - comma(); - - property("expression", false); - - return leave(); - } - - private boolean emitProgram(final FunctionNode functionNode) { - enterDefault(functionNode); - type("Program"); - comma(); - - // body consists of nested functions and statements - final List stats = functionNode.getBody().getStatements(); - final int size = stats.size(); - int idx = 0; - arrayStart("body"); - - for (final Node stat : stats) { - stat.accept(this); - if (idx != (size - 1)) { - comma(); - } - idx++; - } - arrayEnd(); - - return leave(); - } - - @Override - public boolean enterIdentNode(final IdentNode identNode) { - enterDefault(identNode); - - final String name = identNode.getName(); - if ("this".equals(name)) { - type("ThisExpression"); - } else { - type("Identifier"); - comma(); - property("name", identNode.getName()); - } - - return leave(); - } - - @Override - public boolean enterIfNode(final IfNode ifNode) { - enterDefault(ifNode); - - type("IfStatement"); - comma(); - - property("test"); - ifNode.getTest().accept(this); - comma(); - - property("consequent"); - ifNode.getPass().accept(this); - final Node elsePart = ifNode.getFail(); - comma(); - - property("alternate"); - if (elsePart != null) { - elsePart.accept(this); - } else { - nullValue(); - } - - return leave(); - } - - @Override - public boolean enterIndexNode(final IndexNode indexNode) { - enterDefault(indexNode); - - type("MemberExpression"); - comma(); - - property("object"); - indexNode.getBase().accept(this); - comma(); - - property("property"); - indexNode.getIndex().accept(this); - comma(); - - property("computed", true); - - return leave(); - } - - @Override - public boolean enterLabelNode(final LabelNode labelNode) { - enterDefault(labelNode); - - type("LabeledStatement"); - comma(); - - property("label", labelNode.getLabelName()); - comma(); - - property("body"); - labelNode.getBody().accept(this); - - return leave(); - } - - @SuppressWarnings("rawtypes") - @Override - public boolean enterLiteralNode(final LiteralNode literalNode) { - enterDefault(literalNode); - - if (literalNode instanceof LiteralNode.ArrayLiteralNode) { - type("ArrayExpression"); - comma(); - - array("elements", ((LiteralNode.ArrayLiteralNode)literalNode).getElementExpressions()); - } else { - type("Literal"); - comma(); - - property("value"); - final Object value = literalNode.getValue(); - if (value instanceof RegexToken) { - // encode RegExp literals as Strings of the form /.../ - final RegexToken regex = (RegexToken)value; - final StringBuilder regexBuf = new StringBuilder(); - regexBuf.append('/'); - regexBuf.append(regex.getExpression()); - regexBuf.append('/'); - regexBuf.append(regex.getOptions()); - buf.append(quote(regexBuf.toString())); - } else { - final String str = literalNode.getString(); - // encode every String literal with prefix '$' so that script - // can differentiate b/w RegExps as Strings and Strings. - buf.append(literalNode.isString()? quote("$" + str) : str); - } - } - - return leave(); - } - - @Override - public boolean enterObjectNode(final ObjectNode objectNode) { - enterDefault(objectNode); - - type("ObjectExpression"); - comma(); - - array("properties", objectNode.getElements()); - - return leave(); - } - - @Override - public boolean enterPropertyNode(final PropertyNode propertyNode) { - final Node key = propertyNode.getKey(); - - final Node value = propertyNode.getValue(); - if (value != null) { - objectStart(); - location(propertyNode); - - property("key"); - key.accept(this); - comma(); - - property("value"); - value.accept(this); - comma(); - - property("kind", "init"); - - objectEnd(); - } else { - // getter - final Node getter = propertyNode.getGetter(); - if (getter != null) { - objectStart(); - location(propertyNode); - - property("key"); - key.accept(this); - comma(); - - property("value"); - getter.accept(this); - comma(); - - property("kind", "get"); - - objectEnd(); - } - - // setter - final Node setter = propertyNode.getSetter(); - if (setter != null) { - if (getter != null) { - comma(); - } - objectStart(); - location(propertyNode); - - property("key"); - key.accept(this); - comma(); - - property("value"); - setter.accept(this); - comma(); - - property("kind", "set"); - - objectEnd(); - } - } - - return false; - } - - @Override - public boolean enterReturnNode(final ReturnNode returnNode) { - enterDefault(returnNode); - - type("ReturnStatement"); - comma(); - - final Node arg = returnNode.getExpression(); - property("argument"); - if (arg != null) { - arg.accept(this); - } else { - nullValue(); - } - - return leave(); - } - - @Override - public boolean enterRuntimeNode(final RuntimeNode runtimeNode) { - assert false : "should not reach here: RuntimeNode"; - return false; - } - - @Override - public boolean enterSplitNode(final SplitNode splitNode) { - assert false : "should not reach here: SplitNode"; - return false; - } - - @Override - public boolean enterSwitchNode(final SwitchNode switchNode) { - enterDefault(switchNode); - - type("SwitchStatement"); - comma(); - - property("discriminant"); - switchNode.getExpression().accept(this); - comma(); - - array("cases", switchNode.getCases()); - - return leave(); - } - - @Override - public boolean enterTernaryNode(final TernaryNode ternaryNode) { - enterDefault(ternaryNode); - - type("ConditionalExpression"); - comma(); - - property("test"); - ternaryNode.getTest().accept(this); - comma(); - - property("consequent"); - ternaryNode.getTrueExpression().accept(this); - comma(); - - property("alternate"); - ternaryNode.getFalseExpression().accept(this); - - return leave(); - } - - @Override - public boolean enterThrowNode(final ThrowNode throwNode) { - enterDefault(throwNode); - - type("ThrowStatement"); - comma(); - - property("argument"); - throwNode.getExpression().accept(this); - - return leave(); - } - - @Override - public boolean enterTryNode(final TryNode tryNode) { - enterDefault(tryNode); - - type("TryStatement"); - comma(); - - property("block"); - tryNode.getBody().accept(this); - comma(); - - - final List catches = tryNode.getCatches(); - final List guarded = new ArrayList<>(); - CatchNode unguarded = null; - if (catches != null) { - for (final Node n : catches) { - final CatchNode cn = (CatchNode)n; - if (cn.getExceptionCondition() != null) { - guarded.add(cn); - } else { - assert unguarded == null: "too many unguarded?"; - unguarded = cn; - } - } - } - - array("guardedHandlers", guarded); - comma(); - - property("handler"); - if (unguarded != null) { - unguarded.accept(this); - } else { - nullValue(); - } - comma(); - - property("finalizer"); - final Node finallyNode = tryNode.getFinallyBody(); - if (finallyNode != null) { - finallyNode.accept(this); - } else { - nullValue(); - } - - return leave(); - } - - @Override - public boolean enterUnaryNode(final UnaryNode unaryNode) { - enterDefault(unaryNode); - - final TokenType tokenType = unaryNode.tokenType(); - if (tokenType == TokenType.NEW) { - type("NewExpression"); - comma(); - - final CallNode callNode = (CallNode)unaryNode.getExpression(); - property("callee"); - callNode.getFunction().accept(this); - comma(); - - array("arguments", callNode.getArgs()); - } else { - final String operator; - final boolean prefix; - switch (tokenType) { - case INCPOSTFIX: - prefix = false; - operator = "++"; - break; - case DECPOSTFIX: - prefix = false; - operator = "--"; - break; - case INCPREFIX: - operator = "++"; - prefix = true; - break; - case DECPREFIX: - operator = "--"; - prefix = true; - break; - default: - prefix = true; - operator = tokenType.getName(); - break; - } - - type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression"); - comma(); - - property("operator", operator); - comma(); - - property("prefix", prefix); - comma(); - - property("argument"); - unaryNode.getExpression().accept(this); - } - - return leave(); - } - - @Override - public boolean enterVarNode(final VarNode varNode) { - final Node init = varNode.getInit(); - if (init instanceof FunctionNode && ((FunctionNode)init).isDeclared()) { - // function declaration - don't emit VariableDeclaration instead - // just emit FunctionDeclaration using 'init' Node. - init.accept(this); - return false; - } - - enterDefault(varNode); - - type("VariableDeclaration"); - comma(); - - arrayStart("declarations"); - - // VariableDeclarator - objectStart(); - location(varNode.getName()); - - type("VariableDeclarator"); - comma(); - - property("id"); - varNode.getName().accept(this); - comma(); - - property("init"); - if (init != null) { - init.accept(this); - } else { - nullValue(); - } - - // VariableDeclarator - objectEnd(); - - // declarations - arrayEnd(); - - return leave(); - } - - @Override - public boolean enterWhileNode(final WhileNode whileNode) { - enterDefault(whileNode); - - type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement"); - comma(); - - if (whileNode.isDoWhile()) { - property("body"); - whileNode.getBody().accept(this); - comma(); - - property("test"); - whileNode.getTest().accept(this); - } else { - property("test"); - whileNode.getTest().accept(this); - comma(); - - property("body"); - whileNode.getBody().accept(this); - } - - return leave(); - } - - @Override - public boolean enterWithNode(final WithNode withNode) { - enterDefault(withNode); - - type("WithStatement"); - comma(); - - property("object"); - withNode.getExpression().accept(this); - comma(); - - property("body"); - withNode.getBody().accept(this); - - return leave(); - } - - // Internals below - - private JSONWriter(final boolean includeLocation) { - this.buf = new StringBuilder(); - this.includeLocation = includeLocation; - } - - private final StringBuilder buf; - private final boolean includeLocation; - - private String getString() { - return buf.toString(); - } - - private void property(final String key, final String value, final boolean escape) { - buf.append('"'); - buf.append(key); - buf.append("\":"); - if (value != null) { - if (escape) { - buf.append('"'); - } - buf.append(value); - if (escape) { - buf.append('"'); - } - } - } - - private void property(final String key, final String value) { - property(key, value, true); - } - - private void property(final String key, final boolean value) { - property(key, Boolean.toString(value), false); - } - - private void property(final String key, final int value) { - property(key, Integer.toString(value), false); - } - - private void property(final String key) { - property(key, null); - } - - private void type(final String value) { - property("type", value); - } - - private void objectStart(final String name) { - buf.append('"'); - buf.append(name); - buf.append("\":{"); - } - - private void objectStart() { - buf.append('{'); - } - - private void objectEnd() { - buf.append('}'); - } - - private void array(final String name, final List nodes) { - // The size, idx comparison is just to avoid trailing comma.. - final int size = nodes.size(); - int idx = 0; - arrayStart(name); - for (final Node node : nodes) { - if (node != null) { - node.accept(this); - } else { - nullValue(); - } - if (idx != (size - 1)) { - comma(); - } - idx++; - } - arrayEnd(); - } - - private void arrayStart(final String name) { - buf.append('"'); - buf.append(name); - buf.append('"'); - buf.append(':'); - buf.append('['); - } - - private void arrayEnd() { - buf.append(']'); - } - - private void comma() { - buf.append(','); - } - - private void nullValue() { - buf.append("null"); - } - - private void location(final Node node) { - if (includeLocation) { - objectStart("loc"); - - // source name - final Source src = lc.getCurrentFunction().getSource(); - property("source", src.getName()); - comma(); - - // start position - objectStart("start"); - final int start = node.getStart(); - property("line", src.getLine(start)); - comma(); - property("column", src.getColumn(start)); - objectEnd(); - comma(); - - // end position - objectStart("end"); - final int end = node.getFinish(); - property("line", src.getLine(end)); - comma(); - property("column", src.getColumn(end)); - objectEnd(); - - // end 'loc' - objectEnd(); - - comma(); - } - } - - private static String quote(final String str) { - return JSONParser.quote(str); - } -}