JSONWriter.java revision 953:221a84ef44c0
1164426Ssam/* 2177505Ssam * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3164426Ssam * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4164426Ssam * 5164426Ssam * This code is free software; you can redistribute it and/or modify it 6164426Ssam * under the terms of the GNU General Public License version 2 only, as 7164426Ssam * published by the Free Software Foundation. Oracle designates this 8164426Ssam * particular file as subject to the "Classpath" exception as provided 9164426Ssam * by Oracle in the LICENSE file that accompanied this code. 10164426Ssam * 11164426Ssam * This code is distributed in the hope that it will be useful, but WITHOUT 12164426Ssam * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13164426Ssam * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14164426Ssam * version 2 for more details (a copy is included in the LICENSE file that 15164426Ssam * accompanied this code). 16164426Ssam * 17164426Ssam * You should have received a copy of the GNU General Public License version 18164426Ssam * 2 along with this work; if not, write to the Free Software Foundation, 19164426Ssam * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20164426Ssam * 21164426Ssam * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22164426Ssam * or visit www.oracle.com if you need additional information or have any 23164426Ssam * questions. 24164426Ssam */ 25164426Ssam 26164426Ssampackage jdk.nashorn.internal.ir.debug; 27164426Ssam 28164426Ssamimport static jdk.nashorn.internal.runtime.Source.sourceFor; 29164426Ssam 30164426Ssamimport java.util.ArrayList; 31164426Ssamimport java.util.Arrays; 32164426Ssamimport java.util.List; 33164426Ssamimport jdk.nashorn.internal.ir.AccessNode; 34164426Ssamimport jdk.nashorn.internal.ir.BinaryNode; 35164426Ssamimport jdk.nashorn.internal.ir.Block; 36164426Ssamimport jdk.nashorn.internal.ir.BlockStatement; 37164426Ssamimport jdk.nashorn.internal.ir.BreakNode; 38164426Ssamimport jdk.nashorn.internal.ir.CallNode; 39164426Ssamimport jdk.nashorn.internal.ir.CaseNode; 40164426Ssamimport jdk.nashorn.internal.ir.CatchNode; 41164426Ssamimport jdk.nashorn.internal.ir.ContinueNode; 42164426Ssamimport jdk.nashorn.internal.ir.EmptyNode; 43164426Ssamimport jdk.nashorn.internal.ir.Expression; 44164426Ssamimport jdk.nashorn.internal.ir.ExpressionStatement; 45164426Ssamimport jdk.nashorn.internal.ir.ForNode; 46164426Ssamimport jdk.nashorn.internal.ir.FunctionNode; 47164426Ssamimport jdk.nashorn.internal.ir.IdentNode; 48164426Ssamimport jdk.nashorn.internal.ir.IfNode; 49164426Ssamimport jdk.nashorn.internal.ir.IndexNode; 50164426Ssamimport jdk.nashorn.internal.ir.JoinPredecessorExpression; 51164426Ssamimport jdk.nashorn.internal.ir.LabelNode; 52164426Ssamimport jdk.nashorn.internal.ir.LexicalContext; 53164426Ssamimport jdk.nashorn.internal.ir.LiteralNode; 54164426Ssamimport jdk.nashorn.internal.ir.Node; 55164426Ssamimport jdk.nashorn.internal.ir.ObjectNode; 56164426Ssamimport jdk.nashorn.internal.ir.PropertyNode; 57164426Ssamimport jdk.nashorn.internal.ir.ReturnNode; 58164426Ssamimport jdk.nashorn.internal.ir.RuntimeNode; 59164426Ssamimport jdk.nashorn.internal.ir.SplitNode; 60164426Ssamimport jdk.nashorn.internal.ir.Statement; 61164426Ssamimport jdk.nashorn.internal.ir.SwitchNode; 62164426Ssamimport jdk.nashorn.internal.ir.TernaryNode; 63164426Ssamimport jdk.nashorn.internal.ir.ThrowNode; 64164426Ssamimport jdk.nashorn.internal.ir.TryNode; 65164426Ssamimport jdk.nashorn.internal.ir.UnaryNode; 66164426Ssamimport jdk.nashorn.internal.ir.VarNode; 67164426Ssamimport jdk.nashorn.internal.ir.WhileNode; 68259342Sianimport jdk.nashorn.internal.ir.WithNode; 69164426Ssamimport jdk.nashorn.internal.ir.visitor.NodeVisitor; 70164426Ssamimport jdk.nashorn.internal.parser.JSONParser; 71164426Ssamimport jdk.nashorn.internal.parser.Lexer.RegexToken; 72164426Ssamimport jdk.nashorn.internal.parser.Parser; 73164426Ssamimport jdk.nashorn.internal.parser.TokenType; 74164426Ssamimport jdk.nashorn.internal.runtime.Context; 75164426Ssamimport jdk.nashorn.internal.runtime.ParserException; 76164426Ssamimport jdk.nashorn.internal.runtime.Source; 77164426Ssam 78164426Ssam/** 79164426Ssam * This IR writer produces a JSON string that represents AST as a JSON string. 80164426Ssam */ 81164426Ssampublic final class JSONWriter extends NodeVisitor<LexicalContext> { 82164426Ssam 83164426Ssam /** 84164426Ssam * Returns AST as JSON compatible string. 85164426Ssam * 86164426Ssam * @param context context 87164426Ssam * @param code code to be parsed 88164426Ssam * @param name name of the code source (used for location) 89186352Ssam * @param includeLoc tells whether to include location information for nodes or not 90186352Ssam * @return JSON string representation of AST of the supplied code 91164426Ssam */ 92164426Ssam public static String parse(final Context context, final String code, final String name, final boolean includeLoc) { 93236987Simp final Parser parser = new Parser(context.getEnv(), sourceFor(name, code), new Context.ThrowErrorManager(), context.getEnv()._strict, context.getLogger(Parser.class)); 94236987Simp final JSONWriter jsonWriter = new JSONWriter(includeLoc); 95166064Scognet try { 96166064Scognet final FunctionNode functionNode = parser.parse(); //symbol name is ":program", default 97166064Scognet functionNode.accept(jsonWriter); 98166064Scognet return jsonWriter.getString(); 99164426Ssam } catch (final ParserException e) { 100164426Ssam e.throwAsEcmaException(); 101164426Ssam return null; 102164426Ssam } 103164426Ssam } 104164426Ssam 105164426Ssam @Override 106164426Ssam public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinPredecessorExpression) { 107164426Ssam final Expression expr = joinPredecessorExpression.getExpression(); 108164426Ssam if(expr != null) { 109164426Ssam expr.accept(this); 110164426Ssam } else { 111164426Ssam nullValue(); 112164426Ssam } 113164426Ssam return false; 114164426Ssam } 115164426Ssam 116164426Ssam @Override 117164426Ssam protected boolean enterDefault(final Node node) { 118164426Ssam objectStart(); 119164426Ssam location(node); 120164426Ssam 121164426Ssam return true; 122164426Ssam } 123164426Ssam 124164426Ssam private boolean leave() { 125164426Ssam objectEnd(); 126164426Ssam return false; 127186352Ssam } 128164426Ssam 129164426Ssam @Override 130164426Ssam protected Node leaveDefault(final Node node) { 131164426Ssam objectEnd(); 132166339Skevlo return null; 133164426Ssam } 134164426Ssam 135164426Ssam @Override 136164426Ssam public boolean enterAccessNode(final AccessNode accessNode) { 137164426Ssam enterDefault(accessNode); 138164426Ssam 139164426Ssam type("MemberExpression"); 140164426Ssam comma(); 141164426Ssam 142164426Ssam property("object"); 143164426Ssam accessNode.getBase().accept(this); 144164426Ssam comma(); 145164426Ssam 146192660Ssam property("property", accessNode.getProperty()); 147164426Ssam comma(); 148164426Ssam 149164426Ssam property("computed", false); 150186352Ssam 151164426Ssam return leave(); 152164426Ssam } 153164426Ssam 154164426Ssam @Override 155164426Ssam public boolean enterBlock(final Block block) { 156194321Ssam enterDefault(block); 157194321Ssam 158194321Ssam type("BlockStatement"); 159164426Ssam comma(); 160164426Ssam 161186352Ssam array("body", block.getStatements()); 162164426Ssam 163177505Ssam return leave(); 164164426Ssam } 165164426Ssam 166164426Ssam @Override 167164426Ssam public boolean enterBinaryNode(final BinaryNode binaryNode) { 168186352Ssam enterDefault(binaryNode); 169186352Ssam 170186352Ssam final String name; 171186352Ssam if (binaryNode.isAssignment()) { 172186352Ssam name = "AssignmentExpression"; 173186352Ssam } else if (binaryNode.isLogical()) { 174186352Ssam name = "LogicalExpression"; 175186352Ssam } else { 176186352Ssam name = "BinaryExpression"; 177186352Ssam } 178186352Ssam 179186352Ssam type(name); 180194321Ssam comma(); 181177505Ssam 182164426Ssam property("operator", binaryNode.tokenType().getName()); 183164426Ssam comma(); 184164426Ssam 185164426Ssam property("left"); 186164426Ssam binaryNode.lhs().accept(this); 187186352Ssam comma(); 188186352Ssam 189194321Ssam property("right"); 190177505Ssam binaryNode.rhs().accept(this); 191164426Ssam 192164426Ssam return leave(); 193164426Ssam } 194164426Ssam 195164426Ssam @Override 196164426Ssam public boolean enterBreakNode(final BreakNode breakNode) { 197164426Ssam enterDefault(breakNode); 198164426Ssam 199164426Ssam type("BreakStatement"); 200164426Ssam comma(); 201164426Ssam 202164426Ssam final String label = breakNode.getLabelName(); 203164426Ssam if(label != null) { 204164426Ssam property("label", label); 205164426Ssam } else { 206164426Ssam property("label"); 207164426Ssam nullValue(); 208164426Ssam } 209164426Ssam 210164426Ssam return leave(); 211164426Ssam } 212164426Ssam 213164426Ssam @Override 214164426Ssam public boolean enterCallNode(final CallNode callNode) { 215164426Ssam enterDefault(callNode); 216164426Ssam 217164426Ssam type("CallExpression"); 218164426Ssam comma(); 219164426Ssam 220164426Ssam property("callee"); 221164426Ssam callNode.getFunction().accept(this); 222186352Ssam comma(); 223164426Ssam 224164426Ssam array("arguments", callNode.getArgs()); 225164426Ssam 226164426Ssam return leave(); 227164426Ssam } 228164426Ssam 229164426Ssam @Override 230164426Ssam public boolean enterCaseNode(final CaseNode caseNode) { 231164426Ssam enterDefault(caseNode); 232193096Sattilio 233164426Ssam type("SwitchCase"); 234164426Ssam comma(); 235164426Ssam 236164426Ssam final Node test = caseNode.getTest(); 237166339Skevlo property("test"); 238164426Ssam if (test != null) { 239164426Ssam test.accept(this); 240164426Ssam } else { 241164426Ssam nullValue(); 242194321Ssam } 243186352Ssam comma(); 244164426Ssam 245164426Ssam array("consequent", caseNode.getBody().getStatements()); 246164426Ssam 247164426Ssam return leave(); 248164426Ssam } 249164426Ssam 250164426Ssam @Override 251164426Ssam public boolean enterCatchNode(final CatchNode catchNode) { 252164426Ssam enterDefault(catchNode); 253164426Ssam 254227309Sed type("CatchClause"); 255227309Sed comma(); 256164426Ssam 257164426Ssam property("param"); 258164426Ssam catchNode.getException().accept(this); 259186352Ssam comma(); 260186420Ssam 261164426Ssam final Node guard = catchNode.getExceptionCondition(); 262164426Ssam if (guard != null) { 263164426Ssam property("guard"); 264164426Ssam guard.accept(this); 265164426Ssam comma(); 266164426Ssam } 267164426Ssam 268164426Ssam property("body"); 269164426Ssam catchNode.getBody().accept(this); 270164426Ssam 271164426Ssam return leave(); 272164426Ssam } 273164426Ssam 274164426Ssam @Override 275164426Ssam public boolean enterContinueNode(final ContinueNode continueNode) { 276164426Ssam enterDefault(continueNode); 277164426Ssam 278164426Ssam type("ContinueStatement"); 279164426Ssam comma(); 280164426Ssam 281164426Ssam final String label = continueNode.getLabelName(); 282186352Ssam if(label != null) { 283186352Ssam property("label", label); 284186352Ssam } else { 285186352Ssam property("label"); 286186352Ssam nullValue(); 287186352Ssam } 288186352Ssam 289186352Ssam return leave(); 290186352Ssam } 291186352Ssam 292186352Ssam @Override 293186352Ssam public boolean enterEmptyNode(final EmptyNode emptyNode) { 294186352Ssam enterDefault(emptyNode); 295186352Ssam 296164426Ssam type("EmptyStatement"); 297164426Ssam 298186352Ssam return leave(); 299186352Ssam } 300186352Ssam 301186352Ssam @Override 302186352Ssam public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) { 303186420Ssam // handle debugger statement 304186352Ssam final Node expression = expressionStatement.getExpression(); 305164426Ssam if (expression instanceof RuntimeNode) { 306236987Simp expression.accept(this); 307186420Ssam return false; 308186420Ssam } 309186420Ssam 310186420Ssam enterDefault(expressionStatement); 311186352Ssam 312186352Ssam type("ExpressionStatement"); 313186420Ssam comma(); 314186352Ssam 315186420Ssam property("expression"); 316164426Ssam expression.accept(this); 317164426Ssam 318186352Ssam return leave(); 319164426Ssam } 320164426Ssam 321164426Ssam @Override 322164426Ssam public boolean enterBlockStatement(final BlockStatement blockStatement) { 323164426Ssam enterDefault(blockStatement); 324164426Ssam 325164426Ssam type("BlockStatement"); 326164426Ssam comma(); 327164426Ssam 328164426Ssam property("block"); 329186352Ssam blockStatement.getBlock().accept(this); 330164426Ssam 331164426Ssam return leave(); 332164426Ssam } 333164426Ssam 334164426Ssam @Override 335164426Ssam public boolean enterForNode(final ForNode forNode) { 336164426Ssam enterDefault(forNode); 337164426Ssam 338164426Ssam if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) { 339164426Ssam type("ForInStatement"); 340186352Ssam comma(); 341186352Ssam 342186352Ssam final Node init = forNode.getInit(); 343164426Ssam assert init != null; 344164426Ssam property("left"); 345164426Ssam init.accept(this); 346186352Ssam comma(); 347186352Ssam 348164426Ssam final Node modify = forNode.getModify(); 349164426Ssam assert modify != null; 350186352Ssam property("right"); 351186352Ssam modify.accept(this); 352164426Ssam comma(); 353186352Ssam 354164426Ssam property("body"); 355164426Ssam forNode.getBody().accept(this); 356164426Ssam comma(); 357164426Ssam 358164426Ssam property("each", forNode.isForEach()); 359164426Ssam } else { 360164426Ssam type("ForStatement"); 361164426Ssam comma(); 362164426Ssam 363164426Ssam final Node init = forNode.getInit(); 364207554Ssobomax property("init"); 365164426Ssam if (init != null) { 366164426Ssam init.accept(this); 367164426Ssam } else { 368164426Ssam nullValue(); 369189645Ssam } 370189645Ssam comma(); 371189645Ssam 372164426Ssam final Node test = forNode.getTest(); 373164426Ssam property("test"); 374164426Ssam if (test != null) { 375164426Ssam test.accept(this); 376164426Ssam } else { 377164426Ssam nullValue(); 378164426Ssam } 379164426Ssam comma(); 380192660Ssam 381192660Ssam final Node update = forNode.getModify(); 382164426Ssam property("update"); 383164426Ssam if (update != null) { 384164426Ssam update.accept(this); 385164426Ssam } else { 386164426Ssam nullValue(); 387164426Ssam } 388186352Ssam comma(); 389186352Ssam 390164426Ssam property("body"); 391164426Ssam forNode.getBody().accept(this); 392164426Ssam } 393164426Ssam 394164426Ssam return leave(); 395164426Ssam } 396164426Ssam 397164426Ssam @Override 398164426Ssam public boolean enterFunctionNode(final FunctionNode functionNode) { 399164426Ssam final boolean program = functionNode.isProgram(); 400164426Ssam if (program) { 401164426Ssam return emitProgram(functionNode); 402164426Ssam } 403164426Ssam 404164426Ssam enterDefault(functionNode); 405164426Ssam final String name; 406164426Ssam if (functionNode.isDeclared()) { 407164426Ssam name = "FunctionDeclaration"; 408164426Ssam } else { 409164426Ssam name = "FunctionExpression"; 410164426Ssam } 411164426Ssam type(name); 412164426Ssam comma(); 413164426Ssam 414164426Ssam property("id"); 415164426Ssam final FunctionNode.Kind kind = functionNode.getKind(); 416164426Ssam if (functionNode.isAnonymous() || kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) { 417164426Ssam nullValue(); 418164426Ssam } else { 419164426Ssam functionNode.getIdent().accept(this); 420164426Ssam } 421164426Ssam comma(); 422164426Ssam 423164426Ssam array("params", functionNode.getParameters()); 424164426Ssam comma(); 425164426Ssam 426164426Ssam arrayStart("defaults"); 427164426Ssam arrayEnd(); 428164426Ssam comma(); 429164426Ssam 430164426Ssam property("rest"); 431164426Ssam nullValue(); 432164426Ssam comma(); 433164426Ssam 434164426Ssam property("body"); 435164426Ssam functionNode.getBody().accept(this); 436164426Ssam comma(); 437164426Ssam 438164426Ssam property("generator", false); 439195049Srwatson comma(); 440164426Ssam 441164426Ssam property("expression", false); 442164426Ssam 443164426Ssam return leave(); 444164426Ssam } 445164426Ssam 446164426Ssam private boolean emitProgram(final FunctionNode functionNode) { 447164426Ssam enterDefault(functionNode); 448164426Ssam type("Program"); 449195049Srwatson comma(); 450164426Ssam 451164426Ssam // body consists of nested functions and statements 452164426Ssam final List<Statement> stats = functionNode.getBody().getStatements(); 453164426Ssam final int size = stats.size(); 454164426Ssam int idx = 0; 455164426Ssam arrayStart("body"); 456164426Ssam 457164426Ssam for (final Node stat : stats) { 458164426Ssam stat.accept(this); 459164426Ssam if (idx != (size - 1)) { 460164426Ssam comma(); 461164426Ssam } 462164426Ssam idx++; 463164426Ssam } 464164426Ssam arrayEnd(); 465164426Ssam 466164426Ssam return leave(); 467164426Ssam } 468164426Ssam 469164426Ssam @Override 470164426Ssam public boolean enterIdentNode(final IdentNode identNode) { 471164426Ssam enterDefault(identNode); 472164426Ssam 473164426Ssam final String name = identNode.getName(); 474164426Ssam if ("this".equals(name)) { 475164426Ssam type("ThisExpression"); 476164426Ssam } else { 477164426Ssam type("Identifier"); 478164426Ssam comma(); 479164426Ssam property("name", identNode.getName()); 480164426Ssam } 481164426Ssam 482164426Ssam return leave(); 483183886Ssam } 484164426Ssam 485164426Ssam @Override 486164426Ssam public boolean enterIfNode(final IfNode ifNode) { 487164426Ssam enterDefault(ifNode); 488164426Ssam 489166064Scognet type("IfStatement"); 490164426Ssam comma(); 491164426Ssam 492164426Ssam property("test"); 493164426Ssam ifNode.getTest().accept(this); 494164426Ssam comma(); 495164426Ssam 496164426Ssam property("consequent"); 497164426Ssam ifNode.getPass().accept(this); 498164426Ssam final Node elsePart = ifNode.getFail(); 499164426Ssam comma(); 500236987Simp 501164426Ssam property("alternate"); 502164426Ssam if (elsePart != null) { 503164426Ssam elsePart.accept(this); 504164426Ssam } else { 505164426Ssam nullValue(); 506164426Ssam } 507164426Ssam 508164426Ssam return leave(); 509164426Ssam } 510164426Ssam 511164426Ssam @Override 512164426Ssam public boolean enterIndexNode(final IndexNode indexNode) { 513164426Ssam enterDefault(indexNode); 514164426Ssam 515164426Ssam type("MemberExpression"); 516164426Ssam comma(); 517164426Ssam 518164426Ssam property("object"); 519164426Ssam indexNode.getBase().accept(this); 520164426Ssam comma(); 521164426Ssam 522164426Ssam property("property"); 523164426Ssam indexNode.getIndex().accept(this); 524164426Ssam comma(); 525164426Ssam 526164426Ssam property("computed", true); 527164426Ssam 528164426Ssam return leave(); 529164426Ssam } 530164426Ssam 531164426Ssam @Override 532164426Ssam public boolean enterLabelNode(final LabelNode labelNode) { 533164426Ssam enterDefault(labelNode); 534164426Ssam 535164426Ssam type("LabeledStatement"); 536164426Ssam comma(); 537164426Ssam 538164426Ssam property("label", labelNode.getLabelName()); 539164426Ssam comma(); 540164426Ssam 541164426Ssam property("body"); 542164426Ssam labelNode.getBody().accept(this); 543164426Ssam 544164426Ssam return leave(); 545164426Ssam } 546164426Ssam 547164426Ssam @SuppressWarnings("rawtypes") 548164426Ssam @Override 549164426Ssam public boolean enterLiteralNode(final LiteralNode literalNode) { 550164426Ssam enterDefault(literalNode); 551164426Ssam 552164426Ssam if (literalNode instanceof LiteralNode.ArrayLiteralNode) { 553164426Ssam type("ArrayExpression"); 554164426Ssam comma(); 555164426Ssam 556164426Ssam final Node[] value = literalNode.getArray(); 557164426Ssam array("elements", Arrays.asList(value)); 558164426Ssam } else { 559164426Ssam type("Literal"); 560164426Ssam comma(); 561164426Ssam 562164426Ssam property("value"); 563164426Ssam final Object value = literalNode.getValue(); 564164426Ssam if (value instanceof RegexToken) { 565164426Ssam // encode RegExp literals as Strings of the form /.../<flags> 566164426Ssam final RegexToken regex = (RegexToken)value; 567164426Ssam final StringBuilder regexBuf = new StringBuilder(); 568164426Ssam regexBuf.append('/'); 569164426Ssam regexBuf.append(regex.getExpression()); 570164426Ssam regexBuf.append('/'); 571164426Ssam regexBuf.append(regex.getOptions()); 572164426Ssam buf.append(quote(regexBuf.toString())); 573164426Ssam } else { 574164426Ssam final String str = literalNode.getString(); 575164426Ssam // encode every String literal with prefix '$' so that script 576164426Ssam // can differentiate b/w RegExps as Strings and Strings. 577164426Ssam buf.append(literalNode.isString()? quote("$" + str) : str); 578164426Ssam } 579164426Ssam } 580164426Ssam 581186352Ssam return leave(); 582177505Ssam } 583177505Ssam 584177505Ssam @Override 585177505Ssam public boolean enterObjectNode(final ObjectNode objectNode) { 586177505Ssam enterDefault(objectNode); 587177505Ssam 588177505Ssam type("ObjectExpression"); 589177505Ssam comma(); 590177505Ssam 591186352Ssam array("properties", objectNode.getElements()); 592177505Ssam 593177505Ssam return leave(); 594177505Ssam } 595177505Ssam 596186352Ssam @Override 597186352Ssam public boolean enterPropertyNode(final PropertyNode propertyNode) { 598186352Ssam final Node key = propertyNode.getKey(); 599177505Ssam 600177505Ssam final Node value = propertyNode.getValue(); 601177505Ssam if (value != null) { 602177505Ssam objectStart(); 603177505Ssam location(propertyNode); 604177505Ssam 605177505Ssam property("key"); 606177505Ssam key.accept(this); 607177505Ssam comma(); 608177505Ssam 609177505Ssam property("value"); 610177505Ssam value.accept(this); 611186352Ssam comma(); 612186352Ssam 613186352Ssam property("kind", "init"); 614186352Ssam 615186352Ssam objectEnd(); 616186352Ssam } else { 617186352Ssam // getter 618186352Ssam final Node getter = propertyNode.getGetter(); 619186352Ssam if (getter != null) { 620186352Ssam objectStart(); 621186352Ssam location(propertyNode); 622186352Ssam 623186352Ssam property("key"); 624186352Ssam key.accept(this); 625186352Ssam comma(); 626186352Ssam 627186352Ssam property("value"); 628186352Ssam getter.accept(this); 629186352Ssam comma(); 630186352Ssam 631186352Ssam property("kind", "get"); 632186352Ssam 633186352Ssam objectEnd(); 634186352Ssam } 635177505Ssam 636177505Ssam // setter 637177505Ssam final Node setter = propertyNode.getSetter(); 638177505Ssam if (setter != null) { 639177505Ssam if (getter != null) { 640177505Ssam comma(); 641177505Ssam } 642177505Ssam objectStart(); 643177505Ssam location(propertyNode); 644177505Ssam 645177505Ssam property("key"); 646177505Ssam key.accept(this); 647177505Ssam comma(); 648177505Ssam 649177505Ssam property("value"); 650177505Ssam setter.accept(this); 651177505Ssam comma(); 652177505Ssam 653177505Ssam property("kind", "set"); 654177505Ssam 655186352Ssam objectEnd(); 656186352Ssam } 657186352Ssam } 658186352Ssam 659186352Ssam return false; 660186352Ssam } 661186352Ssam 662186352Ssam @Override 663186352Ssam public boolean enterReturnNode(final ReturnNode returnNode) { 664186352Ssam enterDefault(returnNode); 665186352Ssam 666186352Ssam type("ReturnStatement"); 667186352Ssam comma(); 668164426Ssam 669164426Ssam final Node arg = returnNode.getExpression(); 670194321Ssam property("argument"); 671213893Smarius if (arg != null) { 672164426Ssam arg.accept(this); 673169954Ssam } else { 674186352Ssam nullValue(); 675186352Ssam } 676186352Ssam 677186352Ssam return leave(); 678186352Ssam } 679186352Ssam 680186352Ssam @Override 681186352Ssam public boolean enterRuntimeNode(final RuntimeNode runtimeNode) { 682186352Ssam final RuntimeNode.Request req = runtimeNode.getRequest(); 683186352Ssam 684186352Ssam if (req == RuntimeNode.Request.DEBUGGER) { 685186352Ssam enterDefault(runtimeNode); 686186352Ssam type("DebuggerStatement"); 687186352Ssam return leave(); 688186352Ssam } 689186352Ssam 690186352Ssam return false; 691186352Ssam } 692186352Ssam 693186352Ssam @Override 694186352Ssam public boolean enterSplitNode(final SplitNode splitNode) { 695186352Ssam return false; 696213893Smarius } 697213893Smarius 698186352Ssam @Override 699186352Ssam public boolean enterSwitchNode(final SwitchNode switchNode) { 700186352Ssam enterDefault(switchNode); 701186352Ssam 702186352Ssam type("SwitchStatement"); 703186352Ssam comma(); 704186352Ssam 705186352Ssam property("discriminant"); 706186352Ssam switchNode.getExpression().accept(this); 707186352Ssam comma(); 708186352Ssam 709186352Ssam array("cases", switchNode.getCases()); 710186352Ssam 711186352Ssam return leave(); 712186352Ssam } 713186352Ssam 714186352Ssam @Override 715186352Ssam public boolean enterTernaryNode(final TernaryNode ternaryNode) { 716186420Ssam enterDefault(ternaryNode); 717169954Ssam 718186420Ssam type("ConditionalExpression"); 719186420Ssam comma(); 720186420Ssam 721186420Ssam property("test"); 722169954Ssam ternaryNode.getTest().accept(this); 723164426Ssam comma(); 724213893Smarius 725213893Smarius property("consequent"); 726213893Smarius ternaryNode.getTrueExpression().accept(this); 727213893Smarius comma(); 728213893Smarius 729213893Smarius property("alternate"); 730177505Ssam ternaryNode.getFalseExpression().accept(this); 731164426Ssam 732164426Ssam return leave(); 733164426Ssam } 734164426Ssam 735164426Ssam @Override 736164426Ssam public boolean enterThrowNode(final ThrowNode throwNode) { 737164426Ssam enterDefault(throwNode); 738164426Ssam 739164426Ssam type("ThrowStatement"); 740166064Scognet comma(); 741164426Ssam 742164426Ssam property("argument"); 743164426Ssam throwNode.getExpression().accept(this); 744164426Ssam 745164426Ssam return leave(); 746164426Ssam } 747164426Ssam 748164426Ssam @Override 749164426Ssam public boolean enterTryNode(final TryNode tryNode) { 750164426Ssam enterDefault(tryNode); 751164426Ssam 752164426Ssam type("TryStatement"); 753164426Ssam comma(); 754164426Ssam 755164426Ssam property("block"); 756164426Ssam tryNode.getBody().accept(this); 757164426Ssam comma(); 758164426Ssam 759164426Ssam 760164426Ssam final List<? extends Node> catches = tryNode.getCatches(); 761164426Ssam final List<CatchNode> guarded = new ArrayList<>(); 762164426Ssam CatchNode unguarded = null; 763164426Ssam if (catches != null) { 764164426Ssam for (final Node n : catches) { 765164426Ssam final CatchNode cn = (CatchNode)n; 766164426Ssam if (cn.getExceptionCondition() != null) { 767164426Ssam guarded.add(cn); 768164426Ssam } else { 769164426Ssam assert unguarded == null: "too many unguarded?"; 770164426Ssam unguarded = cn; 771164426Ssam } 772164426Ssam } 773164426Ssam } 774164426Ssam 775164426Ssam array("guardedHandlers", guarded); 776164426Ssam comma(); 777164426Ssam 778164426Ssam property("handler"); 779164426Ssam if (unguarded != null) { 780164426Ssam unguarded.accept(this); 781186352Ssam } else { 782164426Ssam nullValue(); 783193096Sattilio } 784186352Ssam comma(); 785164426Ssam 786186352Ssam property("finalizer"); 787186352Ssam final Node finallyNode = tryNode.getFinallyBody(); 788186352Ssam if (finallyNode != null) { 789186352Ssam finallyNode.accept(this); 790186352Ssam } else { 791164426Ssam nullValue(); 792164426Ssam } 793164426Ssam 794186352Ssam return leave(); 795186352Ssam } 796186352Ssam 797186352Ssam @Override 798186352Ssam public boolean enterUnaryNode(final UnaryNode unaryNode) { 799164426Ssam enterDefault(unaryNode); 800164426Ssam 801164426Ssam final TokenType tokenType = unaryNode.tokenType(); 802164426Ssam if (tokenType == TokenType.NEW) { 803164426Ssam type("NewExpression"); 804164426Ssam comma(); 805164426Ssam 806186352Ssam final CallNode callNode = (CallNode)unaryNode.getExpression(); 807186352Ssam property("callee"); 808186352Ssam callNode.getFunction().accept(this); 809177505Ssam comma(); 810164426Ssam 811164426Ssam array("arguments", callNode.getArgs()); 812164426Ssam } else { 813164426Ssam final String operator; 814164426Ssam final boolean prefix; 815164426Ssam switch (tokenType) { 816164426Ssam case INCPOSTFIX: 817164426Ssam prefix = false; 818186352Ssam operator = "++"; 819164426Ssam break; 820164426Ssam case DECPOSTFIX: 821186352Ssam prefix = false; 822164426Ssam operator = "--"; 823186352Ssam break; 824186352Ssam case INCPREFIX: 825164426Ssam operator = "++"; 826164426Ssam prefix = true; 827164426Ssam break; 828164426Ssam case DECPREFIX: 829164426Ssam operator = "--"; 830164426Ssam prefix = true; 831164426Ssam break; 832164426Ssam default: 833164426Ssam prefix = true; 834164426Ssam operator = tokenType.getName(); 835186352Ssam break; 836164426Ssam } 837164426Ssam 838164426Ssam type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression"); 839164426Ssam comma(); 840164426Ssam 841164426Ssam property("operator", operator); 842164426Ssam comma(); 843164426Ssam 844164426Ssam property("prefix", prefix); 845164426Ssam comma(); 846164426Ssam 847164426Ssam property("argument"); 848164426Ssam unaryNode.getExpression().accept(this); 849164426Ssam } 850164426Ssam 851164426Ssam return leave(); 852164426Ssam } 853164426Ssam 854164426Ssam @Override 855164426Ssam public boolean enterVarNode(final VarNode varNode) { 856164426Ssam final Node init = varNode.getInit(); 857164426Ssam if (init instanceof FunctionNode && ((FunctionNode)init).isDeclared()) { 858164426Ssam // function declaration - don't emit VariableDeclaration instead 859164426Ssam // just emit FunctionDeclaration using 'init' Node. 860164426Ssam init.accept(this); 861164426Ssam return false; 862164426Ssam } 863164426Ssam 864164426Ssam enterDefault(varNode); 865164426Ssam 866164426Ssam type("VariableDeclaration"); 867164426Ssam comma(); 868164426Ssam 869164426Ssam arrayStart("declarations"); 870164426Ssam 871164426Ssam // VariableDeclarator 872164426Ssam objectStart(); 873164426Ssam location(varNode.getName()); 874164426Ssam 875164426Ssam type("VariableDeclarator"); 876192660Ssam comma(); 877192660Ssam 878164426Ssam property("id"); 879164426Ssam varNode.getName().accept(this); 880164426Ssam comma(); 881164426Ssam 882164426Ssam property("init"); 883192660Ssam if (init != null) { 884192660Ssam init.accept(this); 885192660Ssam } else { 886192660Ssam nullValue(); 887192660Ssam } 888192660Ssam 889192660Ssam // VariableDeclarator 890192660Ssam objectEnd(); 891192660Ssam 892192660Ssam // declarations 893192660Ssam arrayEnd(); 894164426Ssam 895164426Ssam return leave(); 896164426Ssam } 897164426Ssam 898164426Ssam @Override 899164426Ssam public boolean enterWhileNode(final WhileNode whileNode) { 900164426Ssam enterDefault(whileNode); 901192660Ssam 902192660Ssam type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement"); 903192660Ssam comma(); 904164426Ssam 905164426Ssam if (whileNode.isDoWhile()) { 906164426Ssam property("body"); 907164426Ssam whileNode.getBody().accept(this); 908164426Ssam comma(); 909164426Ssam 910164426Ssam property("test"); 911164426Ssam whileNode.getTest().accept(this); 912164426Ssam } else { 913164426Ssam property("test"); 914164426Ssam whileNode.getTest().accept(this); 915164426Ssam comma(); 916164426Ssam 917164426Ssam property("body"); 918164426Ssam whileNode.getBody().accept(this); 919164426Ssam } 920164426Ssam 921164426Ssam return leave(); 922164426Ssam } 923164426Ssam 924164426Ssam @Override 925192660Ssam public boolean enterWithNode(final WithNode withNode) { 926164426Ssam enterDefault(withNode); 927164426Ssam 928164426Ssam type("WithStatement"); 929164426Ssam comma(); 930164426Ssam 931164426Ssam property("object"); 932164426Ssam withNode.getExpression().accept(this); 933164426Ssam comma(); 934164426Ssam 935164426Ssam property("body"); 936164426Ssam withNode.getBody().accept(this); 937164426Ssam 938164426Ssam return leave(); 939164426Ssam } 940164426Ssam 941164426Ssam // Internals below 942164426Ssam 943164426Ssam private JSONWriter(final boolean includeLocation) { 944164426Ssam super(new LexicalContext()); 945164426Ssam this.buf = new StringBuilder(); 946164426Ssam this.includeLocation = includeLocation; 947164426Ssam } 948186352Ssam 949164426Ssam private final StringBuilder buf; 950164426Ssam private final boolean includeLocation; 951164426Ssam 952164426Ssam private String getString() { 953164426Ssam return buf.toString(); 954164426Ssam } 955164426Ssam 956166339Skevlo private void property(final String key, final String value, final boolean escape) { 957166339Skevlo buf.append('"'); 958164426Ssam buf.append(key); 959164426Ssam buf.append("\":"); 960164426Ssam if (value != null) { 961164426Ssam if (escape) { 962164426Ssam buf.append('"'); 963164426Ssam } 964164426Ssam buf.append(value); 965164426Ssam if (escape) { 966164426Ssam buf.append('"'); 967164426Ssam } 968164426Ssam } 969164426Ssam } 970164426Ssam 971164426Ssam private void property(final String key, final String value) { 972164426Ssam property(key, value, true); 973164426Ssam } 974164426Ssam 975164426Ssam private void property(final String key, final boolean value) { 976164426Ssam property(key, Boolean.toString(value), false); 977164426Ssam } 978164426Ssam 979164426Ssam private void property(final String key, final int value) { 980164426Ssam property(key, Integer.toString(value), false); 981164426Ssam } 982164426Ssam 983164426Ssam private void property(final String key) { 984164426Ssam property(key, null); 985164426Ssam } 986164426Ssam 987164426Ssam private void type(final String value) { 988164426Ssam property("type", value); 989164426Ssam } 990164426Ssam 991164426Ssam private void objectStart(final String name) { 992164426Ssam buf.append('"'); 993164426Ssam buf.append(name); 994164426Ssam buf.append("\":{"); 995164426Ssam } 996164426Ssam 997164426Ssam private void objectStart() { 998164426Ssam buf.append('{'); 999164426Ssam } 1000164426Ssam 1001164426Ssam private void objectEnd() { 1002164426Ssam buf.append('}'); 1003164426Ssam } 1004164426Ssam 1005164426Ssam private void array(final String name, final List<? extends Node> nodes) { 1006166339Skevlo // The size, idx comparison is just to avoid trailing comma.. 1007164426Ssam final int size = nodes.size(); 1008164426Ssam int idx = 0; 1009164426Ssam arrayStart(name); 1010164426Ssam for (final Node node : nodes) { 1011164426Ssam if (node != null) { 1012164426Ssam node.accept(this); 1013164426Ssam } else { 1014164426Ssam nullValue(); 1015164426Ssam } 1016164426Ssam if (idx != (size - 1)) { 1017164426Ssam comma(); 1018164426Ssam } 1019164426Ssam idx++; 1020164426Ssam } 1021164426Ssam arrayEnd(); 1022164426Ssam } 1023164426Ssam 1024164426Ssam private void arrayStart(final String name) { 1025164426Ssam buf.append('"'); 1026164426Ssam buf.append(name); 1027164426Ssam buf.append('"'); 1028164426Ssam buf.append(':'); 1029164426Ssam buf.append('['); 1030194321Ssam } 1031164426Ssam 1032164426Ssam private void arrayEnd() { 1033164426Ssam buf.append(']'); 1034164426Ssam } 1035164426Ssam 1036164426Ssam private void comma() { 1037164426Ssam buf.append(','); 1038164426Ssam } 1039164426Ssam 1040164426Ssam private void nullValue() { 1041164426Ssam buf.append("null"); 1042164426Ssam } 1043164426Ssam 1044164426Ssam private void location(final Node node) { 1045164426Ssam if (includeLocation) { 1046164426Ssam objectStart("loc"); 1047164426Ssam 1048164426Ssam // source name 1049194321Ssam final Source src = lc.getCurrentFunction().getSource(); 1050194321Ssam property("source", src.getName()); 1051164426Ssam comma(); 1052164426Ssam 1053164426Ssam // start position 1054164426Ssam objectStart("start"); 1055164426Ssam final int start = node.getStart(); 1056164426Ssam property("line", src.getLine(start)); 1057164426Ssam comma(); 1058164426Ssam property("column", src.getColumn(start)); 1059164426Ssam objectEnd(); 1060164426Ssam comma(); 1061164426Ssam 1062164426Ssam // end position 1063164426Ssam objectStart("end"); 1064164426Ssam final int end = node.getFinish(); 1065164426Ssam property("line", src.getLine(end)); 1066164426Ssam comma(); 1067243882Sglebius property("column", src.getColumn(end)); 1068164426Ssam objectEnd(); 1069164426Ssam 1070164426Ssam // end 'loc' 1071164426Ssam objectEnd(); 1072164426Ssam 1073164426Ssam comma(); 1074164426Ssam } 1075164426Ssam } 1076266406Sian 1077164426Ssam private static String quote(final String str) { 1078164426Ssam return JSONParser.quote(str); 1079164426Ssam } 1080164426Ssam} 1081164426Ssam