src/jdk/nashorn/internal/ir/debug/JSONWriter.java

Thu, 24 May 2018 16:39:31 +0800

author
aoqi
date
Thu, 24 May 2018 16:39:31 +0800
changeset 1959
61ffdd1b89f2
parent 1552
b0888b955b31
parent 1490
d85f981c8cf8
permissions
-rw-r--r--

Merge

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation. Oracle designates this
aoqi@0 8 * particular file as subject to the "Classpath" exception as provided
aoqi@0 9 * by Oracle in the LICENSE file that accompanied this code.
aoqi@0 10 *
aoqi@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 14 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 15 * accompanied this code).
aoqi@0 16 *
aoqi@0 17 * You should have received a copy of the GNU General Public License version
aoqi@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 20 *
aoqi@0 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 22 * or visit www.oracle.com if you need additional information or have any
aoqi@0 23 * questions.
aoqi@0 24 */
aoqi@0 25
aoqi@0 26 package jdk.nashorn.internal.ir.debug;
aoqi@0 27
aoqi@0 28 import static jdk.nashorn.internal.runtime.Source.sourceFor;
aoqi@0 29
attila@962 30 import java.util.ArrayList;
aoqi@0 31 import java.util.List;
aoqi@0 32 import jdk.nashorn.internal.ir.AccessNode;
aoqi@0 33 import jdk.nashorn.internal.ir.BinaryNode;
aoqi@0 34 import jdk.nashorn.internal.ir.Block;
aoqi@0 35 import jdk.nashorn.internal.ir.BlockStatement;
aoqi@0 36 import jdk.nashorn.internal.ir.BreakNode;
aoqi@0 37 import jdk.nashorn.internal.ir.CallNode;
aoqi@0 38 import jdk.nashorn.internal.ir.CaseNode;
aoqi@0 39 import jdk.nashorn.internal.ir.CatchNode;
aoqi@0 40 import jdk.nashorn.internal.ir.ContinueNode;
aoqi@0 41 import jdk.nashorn.internal.ir.EmptyNode;
attila@963 42 import jdk.nashorn.internal.ir.Expression;
aoqi@0 43 import jdk.nashorn.internal.ir.ExpressionStatement;
aoqi@0 44 import jdk.nashorn.internal.ir.ForNode;
aoqi@0 45 import jdk.nashorn.internal.ir.FunctionNode;
aoqi@0 46 import jdk.nashorn.internal.ir.IdentNode;
aoqi@0 47 import jdk.nashorn.internal.ir.IfNode;
aoqi@0 48 import jdk.nashorn.internal.ir.IndexNode;
attila@963 49 import jdk.nashorn.internal.ir.JoinPredecessorExpression;
aoqi@0 50 import jdk.nashorn.internal.ir.LabelNode;
aoqi@0 51 import jdk.nashorn.internal.ir.LiteralNode;
aoqi@0 52 import jdk.nashorn.internal.ir.Node;
aoqi@0 53 import jdk.nashorn.internal.ir.ObjectNode;
aoqi@0 54 import jdk.nashorn.internal.ir.PropertyNode;
aoqi@0 55 import jdk.nashorn.internal.ir.ReturnNode;
aoqi@0 56 import jdk.nashorn.internal.ir.RuntimeNode;
aoqi@0 57 import jdk.nashorn.internal.ir.SplitNode;
aoqi@0 58 import jdk.nashorn.internal.ir.Statement;
aoqi@0 59 import jdk.nashorn.internal.ir.SwitchNode;
aoqi@0 60 import jdk.nashorn.internal.ir.TernaryNode;
aoqi@0 61 import jdk.nashorn.internal.ir.ThrowNode;
aoqi@0 62 import jdk.nashorn.internal.ir.TryNode;
aoqi@0 63 import jdk.nashorn.internal.ir.UnaryNode;
aoqi@0 64 import jdk.nashorn.internal.ir.VarNode;
aoqi@0 65 import jdk.nashorn.internal.ir.WhileNode;
aoqi@0 66 import jdk.nashorn.internal.ir.WithNode;
attila@1552 67 import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
aoqi@0 68 import jdk.nashorn.internal.parser.JSONParser;
aoqi@0 69 import jdk.nashorn.internal.parser.Lexer.RegexToken;
aoqi@0 70 import jdk.nashorn.internal.parser.Parser;
aoqi@0 71 import jdk.nashorn.internal.parser.TokenType;
aoqi@0 72 import jdk.nashorn.internal.runtime.Context;
aoqi@0 73 import jdk.nashorn.internal.runtime.ParserException;
aoqi@0 74 import jdk.nashorn.internal.runtime.Source;
aoqi@0 75
aoqi@0 76 /**
aoqi@0 77 * This IR writer produces a JSON string that represents AST as a JSON string.
aoqi@0 78 */
attila@1552 79 public final class JSONWriter extends SimpleNodeVisitor {
aoqi@0 80
aoqi@0 81 /**
aoqi@0 82 * Returns AST as JSON compatible string.
aoqi@0 83 *
attila@963 84 * @param context context
aoqi@0 85 * @param code code to be parsed
aoqi@0 86 * @param name name of the code source (used for location)
aoqi@0 87 * @param includeLoc tells whether to include location information for nodes or not
aoqi@0 88 * @return JSON string representation of AST of the supplied code
aoqi@0 89 */
attila@963 90 public static String parse(final Context context, final String code, final String name, final boolean includeLoc) {
attila@963 91 final Parser parser = new Parser(context.getEnv(), sourceFor(name, code), new Context.ThrowErrorManager(), context.getEnv()._strict, context.getLogger(Parser.class));
aoqi@0 92 final JSONWriter jsonWriter = new JSONWriter(includeLoc);
aoqi@0 93 try {
attila@963 94 final FunctionNode functionNode = parser.parse(); //symbol name is ":program", default
aoqi@0 95 functionNode.accept(jsonWriter);
aoqi@0 96 return jsonWriter.getString();
aoqi@0 97 } catch (final ParserException e) {
aoqi@0 98 e.throwAsEcmaException();
aoqi@0 99 return null;
aoqi@0 100 }
aoqi@0 101 }
aoqi@0 102
aoqi@0 103 @Override
attila@963 104 public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinPredecessorExpression) {
attila@963 105 final Expression expr = joinPredecessorExpression.getExpression();
attila@963 106 if(expr != null) {
attila@963 107 expr.accept(this);
attila@963 108 } else {
attila@963 109 nullValue();
attila@963 110 }
attila@963 111 return false;
attila@963 112 }
attila@963 113
attila@963 114 @Override
aoqi@0 115 protected boolean enterDefault(final Node node) {
aoqi@0 116 objectStart();
aoqi@0 117 location(node);
aoqi@0 118
aoqi@0 119 return true;
aoqi@0 120 }
aoqi@0 121
aoqi@0 122 private boolean leave() {
aoqi@0 123 objectEnd();
aoqi@0 124 return false;
aoqi@0 125 }
aoqi@0 126
aoqi@0 127 @Override
aoqi@0 128 protected Node leaveDefault(final Node node) {
aoqi@0 129 objectEnd();
aoqi@0 130 return null;
aoqi@0 131 }
aoqi@0 132
aoqi@0 133 @Override
aoqi@0 134 public boolean enterAccessNode(final AccessNode accessNode) {
aoqi@0 135 enterDefault(accessNode);
aoqi@0 136
aoqi@0 137 type("MemberExpression");
aoqi@0 138 comma();
aoqi@0 139
aoqi@0 140 property("object");
aoqi@0 141 accessNode.getBase().accept(this);
aoqi@0 142 comma();
aoqi@0 143
attila@963 144 property("property", accessNode.getProperty());
aoqi@0 145 comma();
aoqi@0 146
aoqi@0 147 property("computed", false);
aoqi@0 148
aoqi@0 149 return leave();
aoqi@0 150 }
aoqi@0 151
aoqi@0 152 @Override
aoqi@0 153 public boolean enterBlock(final Block block) {
aoqi@0 154 enterDefault(block);
aoqi@0 155
aoqi@0 156 type("BlockStatement");
aoqi@0 157 comma();
aoqi@0 158
aoqi@0 159 array("body", block.getStatements());
aoqi@0 160
aoqi@0 161 return leave();
aoqi@0 162 }
aoqi@0 163
aoqi@0 164 @Override
aoqi@0 165 public boolean enterBinaryNode(final BinaryNode binaryNode) {
aoqi@0 166 enterDefault(binaryNode);
aoqi@0 167
aoqi@0 168 final String name;
aoqi@0 169 if (binaryNode.isAssignment()) {
aoqi@0 170 name = "AssignmentExpression";
attila@963 171 } else if (binaryNode.isLogical()) {
aoqi@0 172 name = "LogicalExpression";
aoqi@0 173 } else {
aoqi@0 174 name = "BinaryExpression";
aoqi@0 175 }
aoqi@0 176
aoqi@0 177 type(name);
aoqi@0 178 comma();
aoqi@0 179
aoqi@0 180 property("operator", binaryNode.tokenType().getName());
aoqi@0 181 comma();
aoqi@0 182
aoqi@0 183 property("left");
aoqi@0 184 binaryNode.lhs().accept(this);
aoqi@0 185 comma();
aoqi@0 186
aoqi@0 187 property("right");
aoqi@0 188 binaryNode.rhs().accept(this);
aoqi@0 189
aoqi@0 190 return leave();
aoqi@0 191 }
aoqi@0 192
aoqi@0 193 @Override
aoqi@0 194 public boolean enterBreakNode(final BreakNode breakNode) {
aoqi@0 195 enterDefault(breakNode);
aoqi@0 196
aoqi@0 197 type("BreakStatement");
aoqi@0 198 comma();
aoqi@0 199
attila@963 200 final String label = breakNode.getLabelName();
attila@963 201 if(label != null) {
attila@963 202 property("label", label);
aoqi@0 203 } else {
attila@963 204 property("label");
aoqi@0 205 nullValue();
aoqi@0 206 }
aoqi@0 207
aoqi@0 208 return leave();
aoqi@0 209 }
aoqi@0 210
aoqi@0 211 @Override
aoqi@0 212 public boolean enterCallNode(final CallNode callNode) {
aoqi@0 213 enterDefault(callNode);
aoqi@0 214
aoqi@0 215 type("CallExpression");
aoqi@0 216 comma();
aoqi@0 217
aoqi@0 218 property("callee");
aoqi@0 219 callNode.getFunction().accept(this);
aoqi@0 220 comma();
aoqi@0 221
aoqi@0 222 array("arguments", callNode.getArgs());
aoqi@0 223
aoqi@0 224 return leave();
aoqi@0 225 }
aoqi@0 226
aoqi@0 227 @Override
aoqi@0 228 public boolean enterCaseNode(final CaseNode caseNode) {
aoqi@0 229 enterDefault(caseNode);
aoqi@0 230
aoqi@0 231 type("SwitchCase");
aoqi@0 232 comma();
aoqi@0 233
aoqi@0 234 final Node test = caseNode.getTest();
aoqi@0 235 property("test");
aoqi@0 236 if (test != null) {
aoqi@0 237 test.accept(this);
aoqi@0 238 } else {
aoqi@0 239 nullValue();
aoqi@0 240 }
aoqi@0 241 comma();
aoqi@0 242
aoqi@0 243 array("consequent", caseNode.getBody().getStatements());
aoqi@0 244
aoqi@0 245 return leave();
aoqi@0 246 }
aoqi@0 247
aoqi@0 248 @Override
aoqi@0 249 public boolean enterCatchNode(final CatchNode catchNode) {
aoqi@0 250 enterDefault(catchNode);
aoqi@0 251
aoqi@0 252 type("CatchClause");
aoqi@0 253 comma();
aoqi@0 254
aoqi@0 255 property("param");
aoqi@0 256 catchNode.getException().accept(this);
aoqi@0 257 comma();
aoqi@0 258
aoqi@0 259 final Node guard = catchNode.getExceptionCondition();
aoqi@0 260 if (guard != null) {
aoqi@0 261 property("guard");
aoqi@0 262 guard.accept(this);
aoqi@0 263 comma();
aoqi@0 264 }
aoqi@0 265
aoqi@0 266 property("body");
aoqi@0 267 catchNode.getBody().accept(this);
aoqi@0 268
aoqi@0 269 return leave();
aoqi@0 270 }
aoqi@0 271
aoqi@0 272 @Override
aoqi@0 273 public boolean enterContinueNode(final ContinueNode continueNode) {
aoqi@0 274 enterDefault(continueNode);
aoqi@0 275
aoqi@0 276 type("ContinueStatement");
aoqi@0 277 comma();
aoqi@0 278
attila@963 279 final String label = continueNode.getLabelName();
attila@963 280 if(label != null) {
attila@963 281 property("label", label);
aoqi@0 282 } else {
attila@963 283 property("label");
aoqi@0 284 nullValue();
aoqi@0 285 }
aoqi@0 286
aoqi@0 287 return leave();
aoqi@0 288 }
aoqi@0 289
aoqi@0 290 @Override
aoqi@0 291 public boolean enterEmptyNode(final EmptyNode emptyNode) {
aoqi@0 292 enterDefault(emptyNode);
aoqi@0 293
aoqi@0 294 type("EmptyStatement");
aoqi@0 295
aoqi@0 296 return leave();
aoqi@0 297 }
aoqi@0 298
aoqi@0 299 @Override
aoqi@0 300 public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
aoqi@0 301 // handle debugger statement
aoqi@0 302 final Node expression = expressionStatement.getExpression();
aoqi@0 303 if (expression instanceof RuntimeNode) {
aoqi@0 304 expression.accept(this);
aoqi@0 305 return false;
aoqi@0 306 }
aoqi@0 307
aoqi@0 308 enterDefault(expressionStatement);
aoqi@0 309
aoqi@0 310 type("ExpressionStatement");
aoqi@0 311 comma();
aoqi@0 312
aoqi@0 313 property("expression");
aoqi@0 314 expression.accept(this);
aoqi@0 315
aoqi@0 316 return leave();
aoqi@0 317 }
aoqi@0 318
aoqi@0 319 @Override
attila@962 320 public boolean enterBlockStatement(final BlockStatement blockStatement) {
aoqi@0 321 enterDefault(blockStatement);
aoqi@0 322
aoqi@0 323 type("BlockStatement");
aoqi@0 324 comma();
aoqi@0 325
aoqi@0 326 property("block");
aoqi@0 327 blockStatement.getBlock().accept(this);
aoqi@0 328
aoqi@0 329 return leave();
aoqi@0 330 }
aoqi@0 331
aoqi@0 332 @Override
aoqi@0 333 public boolean enterForNode(final ForNode forNode) {
aoqi@0 334 enterDefault(forNode);
aoqi@0 335
aoqi@0 336 if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) {
aoqi@0 337 type("ForInStatement");
aoqi@0 338 comma();
aoqi@0 339
attila@962 340 final Node init = forNode.getInit();
aoqi@0 341 assert init != null;
aoqi@0 342 property("left");
aoqi@0 343 init.accept(this);
aoqi@0 344 comma();
aoqi@0 345
attila@962 346 final Node modify = forNode.getModify();
aoqi@0 347 assert modify != null;
aoqi@0 348 property("right");
aoqi@0 349 modify.accept(this);
aoqi@0 350 comma();
aoqi@0 351
aoqi@0 352 property("body");
aoqi@0 353 forNode.getBody().accept(this);
aoqi@0 354 comma();
aoqi@0 355
aoqi@0 356 property("each", forNode.isForEach());
aoqi@0 357 } else {
aoqi@0 358 type("ForStatement");
aoqi@0 359 comma();
aoqi@0 360
aoqi@0 361 final Node init = forNode.getInit();
aoqi@0 362 property("init");
aoqi@0 363 if (init != null) {
aoqi@0 364 init.accept(this);
aoqi@0 365 } else {
aoqi@0 366 nullValue();
aoqi@0 367 }
aoqi@0 368 comma();
aoqi@0 369
aoqi@0 370 final Node test = forNode.getTest();
aoqi@0 371 property("test");
aoqi@0 372 if (test != null) {
aoqi@0 373 test.accept(this);
aoqi@0 374 } else {
aoqi@0 375 nullValue();
aoqi@0 376 }
aoqi@0 377 comma();
aoqi@0 378
aoqi@0 379 final Node update = forNode.getModify();
aoqi@0 380 property("update");
aoqi@0 381 if (update != null) {
aoqi@0 382 update.accept(this);
aoqi@0 383 } else {
aoqi@0 384 nullValue();
aoqi@0 385 }
aoqi@0 386 comma();
aoqi@0 387
aoqi@0 388 property("body");
aoqi@0 389 forNode.getBody().accept(this);
aoqi@0 390 }
aoqi@0 391
aoqi@0 392 return leave();
aoqi@0 393 }
aoqi@0 394
aoqi@0 395 @Override
aoqi@0 396 public boolean enterFunctionNode(final FunctionNode functionNode) {
aoqi@0 397 final boolean program = functionNode.isProgram();
aoqi@0 398 if (program) {
aoqi@0 399 return emitProgram(functionNode);
aoqi@0 400 }
aoqi@0 401
aoqi@0 402 enterDefault(functionNode);
aoqi@0 403 final String name;
aoqi@0 404 if (functionNode.isDeclared()) {
aoqi@0 405 name = "FunctionDeclaration";
aoqi@0 406 } else {
aoqi@0 407 name = "FunctionExpression";
aoqi@0 408 }
aoqi@0 409 type(name);
aoqi@0 410 comma();
aoqi@0 411
aoqi@0 412 property("id");
aoqi@0 413 final FunctionNode.Kind kind = functionNode.getKind();
aoqi@0 414 if (functionNode.isAnonymous() || kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
aoqi@0 415 nullValue();
aoqi@0 416 } else {
aoqi@0 417 functionNode.getIdent().accept(this);
aoqi@0 418 }
aoqi@0 419 comma();
aoqi@0 420
aoqi@0 421 array("params", functionNode.getParameters());
aoqi@0 422 comma();
aoqi@0 423
aoqi@0 424 arrayStart("defaults");
aoqi@0 425 arrayEnd();
aoqi@0 426 comma();
aoqi@0 427
aoqi@0 428 property("rest");
aoqi@0 429 nullValue();
aoqi@0 430 comma();
aoqi@0 431
aoqi@0 432 property("body");
aoqi@0 433 functionNode.getBody().accept(this);
aoqi@0 434 comma();
aoqi@0 435
aoqi@0 436 property("generator", false);
aoqi@0 437 comma();
aoqi@0 438
aoqi@0 439 property("expression", false);
aoqi@0 440
aoqi@0 441 return leave();
aoqi@0 442 }
aoqi@0 443
aoqi@0 444 private boolean emitProgram(final FunctionNode functionNode) {
aoqi@0 445 enterDefault(functionNode);
aoqi@0 446 type("Program");
aoqi@0 447 comma();
aoqi@0 448
aoqi@0 449 // body consists of nested functions and statements
aoqi@0 450 final List<Statement> stats = functionNode.getBody().getStatements();
aoqi@0 451 final int size = stats.size();
aoqi@0 452 int idx = 0;
aoqi@0 453 arrayStart("body");
aoqi@0 454
aoqi@0 455 for (final Node stat : stats) {
aoqi@0 456 stat.accept(this);
aoqi@0 457 if (idx != (size - 1)) {
aoqi@0 458 comma();
aoqi@0 459 }
aoqi@0 460 idx++;
aoqi@0 461 }
aoqi@0 462 arrayEnd();
aoqi@0 463
aoqi@0 464 return leave();
aoqi@0 465 }
aoqi@0 466
aoqi@0 467 @Override
aoqi@0 468 public boolean enterIdentNode(final IdentNode identNode) {
aoqi@0 469 enterDefault(identNode);
aoqi@0 470
aoqi@0 471 final String name = identNode.getName();
aoqi@0 472 if ("this".equals(name)) {
aoqi@0 473 type("ThisExpression");
aoqi@0 474 } else {
aoqi@0 475 type("Identifier");
aoqi@0 476 comma();
aoqi@0 477 property("name", identNode.getName());
aoqi@0 478 }
aoqi@0 479
aoqi@0 480 return leave();
aoqi@0 481 }
aoqi@0 482
aoqi@0 483 @Override
aoqi@0 484 public boolean enterIfNode(final IfNode ifNode) {
aoqi@0 485 enterDefault(ifNode);
aoqi@0 486
aoqi@0 487 type("IfStatement");
aoqi@0 488 comma();
aoqi@0 489
aoqi@0 490 property("test");
aoqi@0 491 ifNode.getTest().accept(this);
aoqi@0 492 comma();
aoqi@0 493
aoqi@0 494 property("consequent");
aoqi@0 495 ifNode.getPass().accept(this);
aoqi@0 496 final Node elsePart = ifNode.getFail();
aoqi@0 497 comma();
aoqi@0 498
aoqi@0 499 property("alternate");
aoqi@0 500 if (elsePart != null) {
aoqi@0 501 elsePart.accept(this);
aoqi@0 502 } else {
aoqi@0 503 nullValue();
aoqi@0 504 }
aoqi@0 505
aoqi@0 506 return leave();
aoqi@0 507 }
aoqi@0 508
aoqi@0 509 @Override
aoqi@0 510 public boolean enterIndexNode(final IndexNode indexNode) {
aoqi@0 511 enterDefault(indexNode);
aoqi@0 512
aoqi@0 513 type("MemberExpression");
aoqi@0 514 comma();
aoqi@0 515
aoqi@0 516 property("object");
aoqi@0 517 indexNode.getBase().accept(this);
aoqi@0 518 comma();
aoqi@0 519
aoqi@0 520 property("property");
aoqi@0 521 indexNode.getIndex().accept(this);
aoqi@0 522 comma();
aoqi@0 523
aoqi@0 524 property("computed", true);
aoqi@0 525
aoqi@0 526 return leave();
aoqi@0 527 }
aoqi@0 528
aoqi@0 529 @Override
aoqi@0 530 public boolean enterLabelNode(final LabelNode labelNode) {
aoqi@0 531 enterDefault(labelNode);
aoqi@0 532
aoqi@0 533 type("LabeledStatement");
aoqi@0 534 comma();
aoqi@0 535
attila@963 536 property("label", labelNode.getLabelName());
aoqi@0 537 comma();
aoqi@0 538
aoqi@0 539 property("body");
aoqi@0 540 labelNode.getBody().accept(this);
aoqi@0 541
aoqi@0 542 return leave();
aoqi@0 543 }
aoqi@0 544
aoqi@0 545 @SuppressWarnings("rawtypes")
aoqi@0 546 @Override
aoqi@0 547 public boolean enterLiteralNode(final LiteralNode literalNode) {
aoqi@0 548 enterDefault(literalNode);
aoqi@0 549
aoqi@0 550 if (literalNode instanceof LiteralNode.ArrayLiteralNode) {
aoqi@0 551 type("ArrayExpression");
aoqi@0 552 comma();
aoqi@0 553
attila@1209 554 array("elements", ((LiteralNode.ArrayLiteralNode)literalNode).getElementExpressions());
aoqi@0 555 } else {
aoqi@0 556 type("Literal");
aoqi@0 557 comma();
aoqi@0 558
aoqi@0 559 property("value");
aoqi@0 560 final Object value = literalNode.getValue();
aoqi@0 561 if (value instanceof RegexToken) {
aoqi@0 562 // encode RegExp literals as Strings of the form /.../<flags>
aoqi@0 563 final RegexToken regex = (RegexToken)value;
aoqi@0 564 final StringBuilder regexBuf = new StringBuilder();
aoqi@0 565 regexBuf.append('/');
aoqi@0 566 regexBuf.append(regex.getExpression());
aoqi@0 567 regexBuf.append('/');
aoqi@0 568 regexBuf.append(regex.getOptions());
aoqi@0 569 buf.append(quote(regexBuf.toString()));
aoqi@0 570 } else {
aoqi@0 571 final String str = literalNode.getString();
aoqi@0 572 // encode every String literal with prefix '$' so that script
aoqi@0 573 // can differentiate b/w RegExps as Strings and Strings.
aoqi@0 574 buf.append(literalNode.isString()? quote("$" + str) : str);
aoqi@0 575 }
aoqi@0 576 }
aoqi@0 577
aoqi@0 578 return leave();
aoqi@0 579 }
aoqi@0 580
aoqi@0 581 @Override
aoqi@0 582 public boolean enterObjectNode(final ObjectNode objectNode) {
aoqi@0 583 enterDefault(objectNode);
aoqi@0 584
aoqi@0 585 type("ObjectExpression");
aoqi@0 586 comma();
aoqi@0 587
aoqi@0 588 array("properties", objectNode.getElements());
aoqi@0 589
aoqi@0 590 return leave();
aoqi@0 591 }
aoqi@0 592
aoqi@0 593 @Override
aoqi@0 594 public boolean enterPropertyNode(final PropertyNode propertyNode) {
aoqi@0 595 final Node key = propertyNode.getKey();
aoqi@0 596
aoqi@0 597 final Node value = propertyNode.getValue();
aoqi@0 598 if (value != null) {
aoqi@0 599 objectStart();
aoqi@0 600 location(propertyNode);
aoqi@0 601
aoqi@0 602 property("key");
aoqi@0 603 key.accept(this);
aoqi@0 604 comma();
aoqi@0 605
aoqi@0 606 property("value");
aoqi@0 607 value.accept(this);
aoqi@0 608 comma();
aoqi@0 609
aoqi@0 610 property("kind", "init");
aoqi@0 611
aoqi@0 612 objectEnd();
aoqi@0 613 } else {
aoqi@0 614 // getter
aoqi@0 615 final Node getter = propertyNode.getGetter();
aoqi@0 616 if (getter != null) {
aoqi@0 617 objectStart();
aoqi@0 618 location(propertyNode);
aoqi@0 619
aoqi@0 620 property("key");
aoqi@0 621 key.accept(this);
aoqi@0 622 comma();
aoqi@0 623
aoqi@0 624 property("value");
aoqi@0 625 getter.accept(this);
aoqi@0 626 comma();
aoqi@0 627
aoqi@0 628 property("kind", "get");
aoqi@0 629
aoqi@0 630 objectEnd();
aoqi@0 631 }
aoqi@0 632
aoqi@0 633 // setter
aoqi@0 634 final Node setter = propertyNode.getSetter();
aoqi@0 635 if (setter != null) {
aoqi@0 636 if (getter != null) {
aoqi@0 637 comma();
aoqi@0 638 }
aoqi@0 639 objectStart();
aoqi@0 640 location(propertyNode);
aoqi@0 641
aoqi@0 642 property("key");
aoqi@0 643 key.accept(this);
aoqi@0 644 comma();
aoqi@0 645
aoqi@0 646 property("value");
aoqi@0 647 setter.accept(this);
aoqi@0 648 comma();
aoqi@0 649
aoqi@0 650 property("kind", "set");
aoqi@0 651
aoqi@0 652 objectEnd();
aoqi@0 653 }
aoqi@0 654 }
aoqi@0 655
aoqi@0 656 return false;
aoqi@0 657 }
aoqi@0 658
aoqi@0 659 @Override
aoqi@0 660 public boolean enterReturnNode(final ReturnNode returnNode) {
aoqi@0 661 enterDefault(returnNode);
aoqi@0 662
aoqi@0 663 type("ReturnStatement");
aoqi@0 664 comma();
aoqi@0 665
aoqi@0 666 final Node arg = returnNode.getExpression();
aoqi@0 667 property("argument");
aoqi@0 668 if (arg != null) {
aoqi@0 669 arg.accept(this);
aoqi@0 670 } else {
aoqi@0 671 nullValue();
aoqi@0 672 }
aoqi@0 673
aoqi@0 674 return leave();
aoqi@0 675 }
aoqi@0 676
aoqi@0 677 @Override
aoqi@0 678 public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
aoqi@0 679 final RuntimeNode.Request req = runtimeNode.getRequest();
aoqi@0 680
aoqi@0 681 if (req == RuntimeNode.Request.DEBUGGER) {
aoqi@0 682 enterDefault(runtimeNode);
aoqi@0 683 type("DebuggerStatement");
aoqi@0 684 return leave();
aoqi@0 685 }
aoqi@0 686
aoqi@0 687 return false;
aoqi@0 688 }
aoqi@0 689
aoqi@0 690 @Override
aoqi@0 691 public boolean enterSplitNode(final SplitNode splitNode) {
aoqi@0 692 return false;
aoqi@0 693 }
aoqi@0 694
aoqi@0 695 @Override
aoqi@0 696 public boolean enterSwitchNode(final SwitchNode switchNode) {
aoqi@0 697 enterDefault(switchNode);
aoqi@0 698
aoqi@0 699 type("SwitchStatement");
aoqi@0 700 comma();
aoqi@0 701
aoqi@0 702 property("discriminant");
aoqi@0 703 switchNode.getExpression().accept(this);
aoqi@0 704 comma();
aoqi@0 705
aoqi@0 706 array("cases", switchNode.getCases());
aoqi@0 707
aoqi@0 708 return leave();
aoqi@0 709 }
aoqi@0 710
aoqi@0 711 @Override
aoqi@0 712 public boolean enterTernaryNode(final TernaryNode ternaryNode) {
aoqi@0 713 enterDefault(ternaryNode);
aoqi@0 714
aoqi@0 715 type("ConditionalExpression");
aoqi@0 716 comma();
aoqi@0 717
aoqi@0 718 property("test");
aoqi@0 719 ternaryNode.getTest().accept(this);
aoqi@0 720 comma();
aoqi@0 721
aoqi@0 722 property("consequent");
aoqi@0 723 ternaryNode.getTrueExpression().accept(this);
aoqi@0 724 comma();
aoqi@0 725
aoqi@0 726 property("alternate");
aoqi@0 727 ternaryNode.getFalseExpression().accept(this);
aoqi@0 728
aoqi@0 729 return leave();
aoqi@0 730 }
aoqi@0 731
aoqi@0 732 @Override
aoqi@0 733 public boolean enterThrowNode(final ThrowNode throwNode) {
aoqi@0 734 enterDefault(throwNode);
aoqi@0 735
aoqi@0 736 type("ThrowStatement");
aoqi@0 737 comma();
aoqi@0 738
aoqi@0 739 property("argument");
aoqi@0 740 throwNode.getExpression().accept(this);
aoqi@0 741
aoqi@0 742 return leave();
aoqi@0 743 }
aoqi@0 744
aoqi@0 745 @Override
aoqi@0 746 public boolean enterTryNode(final TryNode tryNode) {
aoqi@0 747 enterDefault(tryNode);
aoqi@0 748
aoqi@0 749 type("TryStatement");
aoqi@0 750 comma();
aoqi@0 751
aoqi@0 752 property("block");
aoqi@0 753 tryNode.getBody().accept(this);
aoqi@0 754 comma();
aoqi@0 755
aoqi@0 756
aoqi@0 757 final List<? extends Node> catches = tryNode.getCatches();
aoqi@0 758 final List<CatchNode> guarded = new ArrayList<>();
aoqi@0 759 CatchNode unguarded = null;
aoqi@0 760 if (catches != null) {
attila@962 761 for (final Node n : catches) {
attila@962 762 final CatchNode cn = (CatchNode)n;
aoqi@0 763 if (cn.getExceptionCondition() != null) {
aoqi@0 764 guarded.add(cn);
aoqi@0 765 } else {
aoqi@0 766 assert unguarded == null: "too many unguarded?";
aoqi@0 767 unguarded = cn;
aoqi@0 768 }
aoqi@0 769 }
aoqi@0 770 }
aoqi@0 771
aoqi@0 772 array("guardedHandlers", guarded);
aoqi@0 773 comma();
aoqi@0 774
aoqi@0 775 property("handler");
aoqi@0 776 if (unguarded != null) {
aoqi@0 777 unguarded.accept(this);
aoqi@0 778 } else {
aoqi@0 779 nullValue();
aoqi@0 780 }
aoqi@0 781 comma();
aoqi@0 782
aoqi@0 783 property("finalizer");
aoqi@0 784 final Node finallyNode = tryNode.getFinallyBody();
aoqi@0 785 if (finallyNode != null) {
aoqi@0 786 finallyNode.accept(this);
aoqi@0 787 } else {
aoqi@0 788 nullValue();
aoqi@0 789 }
aoqi@0 790
aoqi@0 791 return leave();
aoqi@0 792 }
aoqi@0 793
aoqi@0 794 @Override
aoqi@0 795 public boolean enterUnaryNode(final UnaryNode unaryNode) {
aoqi@0 796 enterDefault(unaryNode);
aoqi@0 797
aoqi@0 798 final TokenType tokenType = unaryNode.tokenType();
aoqi@0 799 if (tokenType == TokenType.NEW) {
aoqi@0 800 type("NewExpression");
aoqi@0 801 comma();
aoqi@0 802
attila@963 803 final CallNode callNode = (CallNode)unaryNode.getExpression();
aoqi@0 804 property("callee");
aoqi@0 805 callNode.getFunction().accept(this);
aoqi@0 806 comma();
aoqi@0 807
aoqi@0 808 array("arguments", callNode.getArgs());
aoqi@0 809 } else {
aoqi@0 810 final String operator;
aoqi@0 811 final boolean prefix;
aoqi@0 812 switch (tokenType) {
aoqi@0 813 case INCPOSTFIX:
aoqi@0 814 prefix = false;
aoqi@0 815 operator = "++";
aoqi@0 816 break;
aoqi@0 817 case DECPOSTFIX:
aoqi@0 818 prefix = false;
aoqi@0 819 operator = "--";
aoqi@0 820 break;
aoqi@0 821 case INCPREFIX:
aoqi@0 822 operator = "++";
aoqi@0 823 prefix = true;
aoqi@0 824 break;
aoqi@0 825 case DECPREFIX:
aoqi@0 826 operator = "--";
aoqi@0 827 prefix = true;
aoqi@0 828 break;
aoqi@0 829 default:
aoqi@0 830 prefix = true;
aoqi@0 831 operator = tokenType.getName();
aoqi@0 832 break;
aoqi@0 833 }
aoqi@0 834
aoqi@0 835 type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression");
aoqi@0 836 comma();
aoqi@0 837
aoqi@0 838 property("operator", operator);
aoqi@0 839 comma();
aoqi@0 840
aoqi@0 841 property("prefix", prefix);
aoqi@0 842 comma();
aoqi@0 843
aoqi@0 844 property("argument");
attila@963 845 unaryNode.getExpression().accept(this);
aoqi@0 846 }
aoqi@0 847
aoqi@0 848 return leave();
aoqi@0 849 }
aoqi@0 850
aoqi@0 851 @Override
aoqi@0 852 public boolean enterVarNode(final VarNode varNode) {
aoqi@0 853 final Node init = varNode.getInit();
aoqi@0 854 if (init instanceof FunctionNode && ((FunctionNode)init).isDeclared()) {
aoqi@0 855 // function declaration - don't emit VariableDeclaration instead
aoqi@0 856 // just emit FunctionDeclaration using 'init' Node.
aoqi@0 857 init.accept(this);
aoqi@0 858 return false;
aoqi@0 859 }
aoqi@0 860
aoqi@0 861 enterDefault(varNode);
aoqi@0 862
aoqi@0 863 type("VariableDeclaration");
aoqi@0 864 comma();
aoqi@0 865
aoqi@0 866 arrayStart("declarations");
aoqi@0 867
aoqi@0 868 // VariableDeclarator
aoqi@0 869 objectStart();
aoqi@0 870 location(varNode.getName());
aoqi@0 871
aoqi@0 872 type("VariableDeclarator");
aoqi@0 873 comma();
aoqi@0 874
aoqi@0 875 property("id");
aoqi@0 876 varNode.getName().accept(this);
aoqi@0 877 comma();
aoqi@0 878
aoqi@0 879 property("init");
aoqi@0 880 if (init != null) {
aoqi@0 881 init.accept(this);
aoqi@0 882 } else {
aoqi@0 883 nullValue();
aoqi@0 884 }
aoqi@0 885
aoqi@0 886 // VariableDeclarator
aoqi@0 887 objectEnd();
aoqi@0 888
aoqi@0 889 // declarations
aoqi@0 890 arrayEnd();
aoqi@0 891
aoqi@0 892 return leave();
aoqi@0 893 }
aoqi@0 894
aoqi@0 895 @Override
aoqi@0 896 public boolean enterWhileNode(final WhileNode whileNode) {
aoqi@0 897 enterDefault(whileNode);
aoqi@0 898
aoqi@0 899 type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
aoqi@0 900 comma();
aoqi@0 901
aoqi@0 902 if (whileNode.isDoWhile()) {
aoqi@0 903 property("body");
aoqi@0 904 whileNode.getBody().accept(this);
aoqi@0 905 comma();
aoqi@0 906
aoqi@0 907 property("test");
aoqi@0 908 whileNode.getTest().accept(this);
aoqi@0 909 } else {
aoqi@0 910 property("test");
aoqi@0 911 whileNode.getTest().accept(this);
aoqi@0 912 comma();
aoqi@0 913
aoqi@0 914 property("body");
aoqi@0 915 whileNode.getBody().accept(this);
aoqi@0 916 }
aoqi@0 917
aoqi@0 918 return leave();
aoqi@0 919 }
aoqi@0 920
aoqi@0 921 @Override
aoqi@0 922 public boolean enterWithNode(final WithNode withNode) {
aoqi@0 923 enterDefault(withNode);
aoqi@0 924
aoqi@0 925 type("WithStatement");
aoqi@0 926 comma();
aoqi@0 927
aoqi@0 928 property("object");
aoqi@0 929 withNode.getExpression().accept(this);
aoqi@0 930 comma();
aoqi@0 931
aoqi@0 932 property("body");
aoqi@0 933 withNode.getBody().accept(this);
aoqi@0 934
aoqi@0 935 return leave();
aoqi@0 936 }
aoqi@0 937
aoqi@0 938 // Internals below
aoqi@0 939
aoqi@0 940 private JSONWriter(final boolean includeLocation) {
aoqi@0 941 this.buf = new StringBuilder();
aoqi@0 942 this.includeLocation = includeLocation;
aoqi@0 943 }
aoqi@0 944
aoqi@0 945 private final StringBuilder buf;
aoqi@0 946 private final boolean includeLocation;
aoqi@0 947
aoqi@0 948 private String getString() {
aoqi@0 949 return buf.toString();
aoqi@0 950 }
aoqi@0 951
aoqi@0 952 private void property(final String key, final String value, final boolean escape) {
aoqi@0 953 buf.append('"');
aoqi@0 954 buf.append(key);
aoqi@0 955 buf.append("\":");
aoqi@0 956 if (value != null) {
attila@963 957 if (escape) {
attila@963 958 buf.append('"');
attila@963 959 }
aoqi@0 960 buf.append(value);
attila@963 961 if (escape) {
attila@963 962 buf.append('"');
attila@963 963 }
aoqi@0 964 }
aoqi@0 965 }
aoqi@0 966
aoqi@0 967 private void property(final String key, final String value) {
aoqi@0 968 property(key, value, true);
aoqi@0 969 }
aoqi@0 970
aoqi@0 971 private void property(final String key, final boolean value) {
aoqi@0 972 property(key, Boolean.toString(value), false);
aoqi@0 973 }
aoqi@0 974
aoqi@0 975 private void property(final String key, final int value) {
aoqi@0 976 property(key, Integer.toString(value), false);
aoqi@0 977 }
aoqi@0 978
aoqi@0 979 private void property(final String key) {
aoqi@0 980 property(key, null);
aoqi@0 981 }
aoqi@0 982
aoqi@0 983 private void type(final String value) {
aoqi@0 984 property("type", value);
aoqi@0 985 }
aoqi@0 986
aoqi@0 987 private void objectStart(final String name) {
aoqi@0 988 buf.append('"');
aoqi@0 989 buf.append(name);
aoqi@0 990 buf.append("\":{");
aoqi@0 991 }
aoqi@0 992
aoqi@0 993 private void objectStart() {
aoqi@0 994 buf.append('{');
aoqi@0 995 }
aoqi@0 996
aoqi@0 997 private void objectEnd() {
aoqi@0 998 buf.append('}');
aoqi@0 999 }
aoqi@0 1000
aoqi@0 1001 private void array(final String name, final List<? extends Node> nodes) {
aoqi@0 1002 // The size, idx comparison is just to avoid trailing comma..
aoqi@0 1003 final int size = nodes.size();
aoqi@0 1004 int idx = 0;
aoqi@0 1005 arrayStart(name);
aoqi@0 1006 for (final Node node : nodes) {
aoqi@0 1007 if (node != null) {
aoqi@0 1008 node.accept(this);
aoqi@0 1009 } else {
aoqi@0 1010 nullValue();
aoqi@0 1011 }
aoqi@0 1012 if (idx != (size - 1)) {
aoqi@0 1013 comma();
aoqi@0 1014 }
aoqi@0 1015 idx++;
aoqi@0 1016 }
aoqi@0 1017 arrayEnd();
aoqi@0 1018 }
aoqi@0 1019
aoqi@0 1020 private void arrayStart(final String name) {
aoqi@0 1021 buf.append('"');
aoqi@0 1022 buf.append(name);
aoqi@0 1023 buf.append('"');
aoqi@0 1024 buf.append(':');
aoqi@0 1025 buf.append('[');
aoqi@0 1026 }
aoqi@0 1027
aoqi@0 1028 private void arrayEnd() {
aoqi@0 1029 buf.append(']');
aoqi@0 1030 }
aoqi@0 1031
aoqi@0 1032 private void comma() {
aoqi@0 1033 buf.append(',');
aoqi@0 1034 }
aoqi@0 1035
aoqi@0 1036 private void nullValue() {
aoqi@0 1037 buf.append("null");
aoqi@0 1038 }
aoqi@0 1039
aoqi@0 1040 private void location(final Node node) {
aoqi@0 1041 if (includeLocation) {
aoqi@0 1042 objectStart("loc");
aoqi@0 1043
aoqi@0 1044 // source name
aoqi@0 1045 final Source src = lc.getCurrentFunction().getSource();
aoqi@0 1046 property("source", src.getName());
aoqi@0 1047 comma();
aoqi@0 1048
aoqi@0 1049 // start position
aoqi@0 1050 objectStart("start");
aoqi@0 1051 final int start = node.getStart();
aoqi@0 1052 property("line", src.getLine(start));
aoqi@0 1053 comma();
aoqi@0 1054 property("column", src.getColumn(start));
aoqi@0 1055 objectEnd();
aoqi@0 1056 comma();
aoqi@0 1057
aoqi@0 1058 // end position
aoqi@0 1059 objectStart("end");
aoqi@0 1060 final int end = node.getFinish();
aoqi@0 1061 property("line", src.getLine(end));
aoqi@0 1062 comma();
aoqi@0 1063 property("column", src.getColumn(end));
aoqi@0 1064 objectEnd();
aoqi@0 1065
aoqi@0 1066 // end 'loc'
aoqi@0 1067 objectEnd();
aoqi@0 1068
aoqi@0 1069 comma();
aoqi@0 1070 }
aoqi@0 1071 }
aoqi@0 1072
aoqi@0 1073 private static String quote(final String str) {
aoqi@0 1074 return JSONParser.quote(str);
aoqi@0 1075 }
aoqi@0 1076 }

mercurial