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

Thu, 11 Jul 2013 18:33:33 +0200

author
attila
date
Thu, 11 Jul 2013 18:33:33 +0200
changeset 430
2c007a8bb0e7
parent 415
edca88d3a03e
child 551
917b16e509bd
permissions
-rw-r--r--

8013925: Remove symbol fields from nodes that don't need them
Reviewed-by: jlaskey, lagergren

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package jdk.nashorn.internal.ir.debug;
    28 import java.util.Arrays;
    29 import java.util.List;
    30 import jdk.nashorn.internal.codegen.CompilerConstants;
    31 import jdk.nashorn.internal.ir.AccessNode;
    32 import jdk.nashorn.internal.ir.BinaryNode;
    33 import jdk.nashorn.internal.ir.Block;
    34 import jdk.nashorn.internal.ir.BlockStatement;
    35 import jdk.nashorn.internal.ir.BreakNode;
    36 import jdk.nashorn.internal.ir.CallNode;
    37 import jdk.nashorn.internal.ir.CaseNode;
    38 import jdk.nashorn.internal.ir.CatchNode;
    39 import jdk.nashorn.internal.ir.ContinueNode;
    40 import jdk.nashorn.internal.ir.EmptyNode;
    41 import jdk.nashorn.internal.ir.ExpressionStatement;
    42 import jdk.nashorn.internal.ir.ForNode;
    43 import jdk.nashorn.internal.ir.FunctionNode;
    44 import jdk.nashorn.internal.ir.IdentNode;
    45 import jdk.nashorn.internal.ir.IfNode;
    46 import jdk.nashorn.internal.ir.IndexNode;
    47 import jdk.nashorn.internal.ir.LabelNode;
    48 import jdk.nashorn.internal.ir.LexicalContext;
    49 import jdk.nashorn.internal.ir.LiteralNode;
    50 import jdk.nashorn.internal.ir.Node;
    51 import jdk.nashorn.internal.ir.ObjectNode;
    52 import jdk.nashorn.internal.ir.PropertyNode;
    53 import jdk.nashorn.internal.ir.ReturnNode;
    54 import jdk.nashorn.internal.ir.RuntimeNode;
    55 import jdk.nashorn.internal.ir.SplitNode;
    56 import jdk.nashorn.internal.ir.Statement;
    57 import jdk.nashorn.internal.ir.SwitchNode;
    58 import jdk.nashorn.internal.ir.TernaryNode;
    59 import jdk.nashorn.internal.ir.ThrowNode;
    60 import jdk.nashorn.internal.ir.TryNode;
    61 import jdk.nashorn.internal.ir.UnaryNode;
    62 import jdk.nashorn.internal.ir.VarNode;
    63 import jdk.nashorn.internal.ir.WhileNode;
    64 import jdk.nashorn.internal.ir.WithNode;
    65 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    66 import jdk.nashorn.internal.parser.JSONParser;
    67 import jdk.nashorn.internal.parser.Lexer.RegexToken;
    68 import jdk.nashorn.internal.parser.Parser;
    69 import jdk.nashorn.internal.parser.TokenType;
    70 import jdk.nashorn.internal.runtime.Context;
    71 import jdk.nashorn.internal.runtime.ParserException;
    72 import jdk.nashorn.internal.runtime.ScriptEnvironment;
    73 import jdk.nashorn.internal.runtime.Source;
    75 /**
    76  * This IR writer produces a JSON string that represents AST as a JSON string.
    77  */
    78 public final class JSONWriter extends NodeVisitor<LexicalContext> {
    80     /**
    81      * Returns AST as JSON compatible string.
    82      *
    83      * @param env  script environment to use
    84      * @param code code to be parsed
    85      * @param name name of the code source (used for location)
    86      * @param includeLoc tells whether to include location information for nodes or not
    87      * @return JSON string representation of AST of the supplied code
    88      */
    89     public static String parse(final ScriptEnvironment env, final String code, final String name, final boolean includeLoc) {
    90         final Parser       parser     = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
    91         final JSONWriter   jsonWriter = new JSONWriter(includeLoc);
    92         try {
    93             final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());
    94             functionNode.accept(jsonWriter);
    95             return jsonWriter.getString();
    96         } catch (final ParserException e) {
    97             e.throwAsEcmaException();
    98             return null;
    99         }
   100     }
   102     @Override
   103     protected boolean enterDefault(final Node node) {
   104         objectStart();
   105         location(node);
   107         return true;
   108     }
   110     private boolean leave() {
   111         objectEnd();
   112         return false;
   113     }
   115     @Override
   116     protected Node leaveDefault(final Node node) {
   117         objectEnd();
   118         return null;
   119     }
   121     @Override
   122     public boolean enterAccessNode(final AccessNode accessNode) {
   123         enterDefault(accessNode);
   125         type("MemberExpression");
   126         comma();
   128         property("object");
   129         accessNode.getBase().accept(this);
   130         comma();
   132         property("property");
   133         accessNode.getProperty().accept(this);
   134         comma();
   136         property("computed", false);
   138         return leave();
   139     }
   141     @Override
   142     public boolean enterBlock(final Block block) {
   143         enterDefault(block);
   145         type("BlockStatement");
   146         comma();
   148         array("body", block.getStatements());
   150         return leave();
   151     }
   153     private static boolean isLogical(final TokenType tt) {
   154         switch (tt) {
   155         case AND:
   156         case OR:
   157             return true;
   158         default:
   159             return false;
   160         }
   161     }
   163     @Override
   164     public boolean enterBinaryNode(final BinaryNode binaryNode) {
   165         enterDefault(binaryNode);
   167         final String name;
   168         if (binaryNode.isAssignment()) {
   169             name = "AssignmentExpression";
   170         } else if (isLogical(binaryNode.tokenType())) {
   171             name = "LogicalExpression";
   172         } else {
   173             name = "BinaryExpression";
   174         }
   176         type(name);
   177         comma();
   179         property("operator", binaryNode.tokenType().getName());
   180         comma();
   182         property("left");
   183         binaryNode.lhs().accept(this);
   184         comma();
   186         property("right");
   187         binaryNode.rhs().accept(this);
   189         return leave();
   190     }
   192     @Override
   193     public boolean enterBreakNode(final BreakNode breakNode) {
   194         enterDefault(breakNode);
   196         type("BreakStatement");
   197         comma();
   199         final IdentNode label = breakNode.getLabel();
   200         if (label != null) {
   201             property("label", label.getName());
   202         } else {
   203             property("label");
   204             nullValue();
   205         }
   207         return leave();
   208     }
   210     @Override
   211     public boolean enterCallNode(final CallNode callNode) {
   212         enterDefault(callNode);
   214         type("CallExpression");
   215         comma();
   217         property("callee");
   218         callNode.getFunction().accept(this);
   219         comma();
   221         array("arguments", callNode.getArgs());
   223         return leave();
   224     }
   226     @Override
   227     public boolean enterCaseNode(final CaseNode caseNode) {
   228         enterDefault(caseNode);
   230         type("SwitchCase");
   231         comma();
   233         final Node test = caseNode.getTest();
   234         property("test");
   235         if (test != null) {
   236             test.accept(this);
   237         } else {
   238             nullValue();
   239         }
   240         comma();
   242         array("consequent", caseNode.getBody().getStatements());
   244         return leave();
   245     }
   247     @Override
   248     public boolean enterCatchNode(final CatchNode catchNode) {
   249         enterDefault(catchNode);
   251         type("CatchClause");
   252         comma();
   254         property("param");
   255         catchNode.getException().accept(this);
   256         comma();
   258         final Node guard = catchNode.getExceptionCondition();
   259         property("guard");
   260         if (guard != null) {
   261             guard.accept(this);
   262         } else {
   263             nullValue();
   264         }
   265         comma();
   267         property("body");
   268         catchNode.getBody().accept(this);
   270         return leave();
   271     }
   273     @Override
   274     public boolean enterContinueNode(final ContinueNode continueNode) {
   275         enterDefault(continueNode);
   277         type("ContinueStatement");
   278         comma();
   280         final IdentNode label = continueNode.getLabel();
   281         if (label != null) {
   282             property("label", label.getName());
   283         } else {
   284             property("label");
   285             nullValue();
   286         }
   288         return leave();
   289     }
   291     @Override
   292     public boolean enterEmptyNode(final EmptyNode emptyNode) {
   293         enterDefault(emptyNode);
   295         type("EmptyStatement");
   297         return leave();
   298     }
   300     @Override
   301     public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
   302         enterDefault(expressionStatement);
   304         type("ExpressionStatement");
   305         comma();
   307         property("expression");
   308         expressionStatement.getExpression().accept(this);
   310         return leave();
   311     }
   313     @Override
   314     public boolean enterBlockStatement(BlockStatement blockStatement) {
   315         enterDefault(blockStatement);
   317         type("BlockStatement");
   318         comma();
   320         property("block");
   321         blockStatement.getBlock().accept(this);
   323         return leave();
   324     }
   326     @Override
   327     public boolean enterForNode(final ForNode forNode) {
   328         enterDefault(forNode);
   330         if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) {
   331             type("ForInStatement");
   332             comma();
   334             Node init = forNode.getInit();
   335             assert init != null;
   336             property("left");
   337             init.accept(this);
   338             comma();
   340             Node modify = forNode.getModify();
   341             assert modify != null;
   342             property("right");
   343             modify.accept(this);
   344             comma();
   346             property("body");
   347             forNode.getBody().accept(this);
   348             comma();
   350             property("each", forNode.isForEach());
   351         } else {
   352             type("ForStatement");
   353             comma();
   355             final Node init = forNode.getInit();
   356             property("init");
   357             if (init != null) {
   358                 init.accept(this);
   359             } else {
   360                 nullValue();
   361             }
   362             comma();
   364             final Node test = forNode.getTest();
   365             property("test");
   366             if (test != null) {
   367                 test.accept(this);
   368             } else {
   369                 nullValue();
   370             }
   371             comma();
   373             final Node update = forNode.getModify();
   374             property("update");
   375             if (update != null) {
   376                 update.accept(this);
   377             } else {
   378                 nullValue();
   379             }
   380             comma();
   382             property("body");
   383             forNode.getBody().accept(this);
   384         }
   386         return leave();
   387     }
   389     @Override
   390     public boolean enterFunctionNode(final FunctionNode functionNode) {
   391         enterDefault(functionNode);
   393         final boolean program = functionNode.isProgram();
   394         final String name;
   395         if (program) {
   396             name = "Program";
   397         } else if (functionNode.isDeclared()) {
   398             name = "FunctionDeclaration";
   399         } else {
   400             name = "FunctionExpression";
   401         }
   402         type(name);
   403         comma();
   405         if (! program) {
   406             property("id");
   407             if (functionNode.isAnonymous()) {
   408                 nullValue();
   409             } else {
   410                 functionNode.getIdent().accept(this);
   411             }
   412             comma();
   413         }
   415         property("rest");
   416         nullValue();
   417         comma();
   419         if (!program) {
   420             array("params", functionNode.getParameters());
   421             comma();
   422         }
   424         // body consists of nested functions and statements
   425         final List<Statement> stats = functionNode.getBody().getStatements();
   426         final int size = stats.size();
   427         int idx = 0;
   428         arrayStart("body");
   430         for (final Node stat : stats) {
   431             stat.accept(this);
   432             if (idx != (size - 1)) {
   433                 comma();
   434             }
   435             idx++;
   436         }
   437         arrayEnd();
   439         return leave();
   440     }
   442     @Override
   443     public boolean enterIdentNode(final IdentNode identNode) {
   444         enterDefault(identNode);
   446         final String name = identNode.getName();
   447         if ("this".equals(name)) {
   448             type("ThisExpression");
   449         } else {
   450             type("Identifier");
   451             comma();
   452             property("name", identNode.getName());
   453         }
   455         return leave();
   456     }
   458     @Override
   459     public boolean enterIfNode(final IfNode ifNode) {
   460         enterDefault(ifNode);
   462         type("IfStatement");
   463         comma();
   465         property("test");
   466         ifNode.getTest().accept(this);
   467         comma();
   469         property("consequent");
   470         ifNode.getPass().accept(this);
   471         final Node elsePart = ifNode.getFail();
   472         comma();
   474         property("alternate");
   475         if (elsePart != null) {
   476             elsePart.accept(this);
   477         } else {
   478             nullValue();
   479         }
   481         return leave();
   482     }
   484     @Override
   485     public boolean enterIndexNode(final IndexNode indexNode) {
   486         enterDefault(indexNode);
   488         type("MemberExpression");
   489         comma();
   491         property("object");
   492         indexNode.getBase().accept(this);
   493         comma();
   495         property("property");
   496         indexNode.getIndex().accept(this);
   497         comma();
   499         property("computed", true);
   501         return leave();
   502     }
   504     @Override
   505     public boolean enterLabelNode(final LabelNode labelNode) {
   506         enterDefault(labelNode);
   508         type("LabeledStatement");
   509         comma();
   511         property("label");
   512         labelNode.getLabel().accept(this);
   513         comma();
   515         property("body");
   516         labelNode.getBody().accept(this);
   518         return leave();
   519     }
   521     @SuppressWarnings("rawtypes")
   522     @Override
   523     public boolean enterLiteralNode(final LiteralNode literalNode) {
   524         enterDefault(literalNode);
   526         if (literalNode instanceof LiteralNode.ArrayLiteralNode) {
   527             type("ArrayExpression");
   528             comma();
   530             final Node[] value = literalNode.getArray();
   531             array("elements", Arrays.asList(value));
   532         } else {
   533             type("Literal");
   534             comma();
   536             property("value");
   537             final Object value = literalNode.getValue();
   538             if (value instanceof RegexToken) {
   539                 // encode RegExp literals as Strings of the form /.../<flags>
   540                 final RegexToken regex = (RegexToken)value;
   541                 final StringBuilder regexBuf = new StringBuilder();
   542                 regexBuf.append('/');
   543                 regexBuf.append(regex.getExpression());
   544                 regexBuf.append('/');
   545                 regexBuf.append(regex.getOptions());
   546                 buf.append(quote(regexBuf.toString()));
   547             } else {
   548                 final String str = literalNode.getString();
   549                 // encode every String literal with prefix '$' so that script
   550                 // can differentiate b/w RegExps as Strings and Strings.
   551                 buf.append(literalNode.isString()? quote("$" + str) : str);
   552             }
   553         }
   555         return leave();
   556     }
   558     @Override
   559     public boolean enterObjectNode(final ObjectNode objectNode) {
   560         enterDefault(objectNode);
   562         type("ObjectExpression");
   563         comma();
   565         array("properties", objectNode.getElements());
   567         return leave();
   568     }
   570     @Override
   571     public boolean enterPropertyNode(final PropertyNode propertyNode) {
   572         final Node key = propertyNode.getKey();
   574         final Node value = propertyNode.getValue();
   575         if (value != null) {
   576             objectStart();
   577             location(propertyNode);
   579             property("key");
   580             key.accept(this);
   581             comma();
   583             property("value");
   584             value.accept(this);
   585             comma();
   587             property("kind", "init");
   589             objectEnd();
   590         } else {
   591             // getter
   592             final Node getter = propertyNode.getGetter();
   593             if (getter != null) {
   594                 objectStart();
   595                 location(propertyNode);
   597                 property("key");
   598                 key.accept(this);
   599                 comma();
   601                 property("value");
   602                 getter.accept(this);
   603                 comma();
   605                 property("kind", "get");
   607                 objectEnd();
   608             }
   610             // setter
   611             final Node setter = propertyNode.getSetter();
   612             if (setter != null) {
   613                 if (getter != null) {
   614                     comma();
   615                 }
   616                 objectStart();
   617                 location(propertyNode);
   619                 property("key");
   620                 key.accept(this);
   621                 comma();
   623                 property("value");
   624                 setter.accept(this);
   625                 comma();
   627                 property("kind", "set");
   629                 objectEnd();
   630             }
   631         }
   633         return false;
   634     }
   636     @Override
   637     public boolean enterReturnNode(final ReturnNode returnNode) {
   638         enterDefault(returnNode);
   640         type("ReturnStatement");
   641         comma();
   643         final Node arg = returnNode.getExpression();
   644         property("argument");
   645         if (arg != null) {
   646             arg.accept(this);
   647         } else {
   648             nullValue();
   649         }
   651         return leave();
   652     }
   654     @Override
   655     public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
   656         final RuntimeNode.Request req = runtimeNode.getRequest();
   658         if (req == RuntimeNode.Request.DEBUGGER) {
   659             enterDefault(runtimeNode);
   660             type("DebuggerStatement");
   661             return leave();
   662         }
   664         return false;
   665     }
   667     @Override
   668     public boolean enterSplitNode(final SplitNode splitNode) {
   669         return false;
   670     }
   672     @Override
   673     public boolean enterSwitchNode(final SwitchNode switchNode) {
   674         enterDefault(switchNode);
   676         type("SwitchStatement");
   677         comma();
   679         property("discriminant");
   680         switchNode.getExpression().accept(this);
   681         comma();
   683         array("cases", switchNode.getCases());
   685         return leave();
   686     }
   688     @Override
   689     public boolean enterTernaryNode(final TernaryNode ternaryNode) {
   690         enterDefault(ternaryNode);
   692         type("ConditionalExpression");
   693         comma();
   695         property("test");
   696         ternaryNode.getTest().accept(this);
   697         comma();
   699         property("consequent");
   700         ternaryNode.getTrueExpression().accept(this);
   701         comma();
   703         property("alternate");
   704         ternaryNode.getFalseExpression().accept(this);
   706         return leave();
   707     }
   709     @Override
   710     public boolean enterThrowNode(final ThrowNode throwNode) {
   711         enterDefault(throwNode);
   713         type("ThrowStatement");
   714         comma();
   716         property("argument");
   717         throwNode.getExpression().accept(this);
   719         return leave();
   720     }
   722     @Override
   723     public boolean enterTryNode(final TryNode tryNode) {
   724         enterDefault(tryNode);
   726         type("TryStatement");
   727         comma();
   729         property("block");
   730         tryNode.getBody().accept(this);
   731         comma();
   733         array("handlers", tryNode.getCatches());
   734         comma();
   736         property("finalizer");
   737         final Node finallyNode = tryNode.getFinallyBody();
   738         if (finallyNode != null) {
   739             finallyNode.accept(this);
   740         } else {
   741             nullValue();
   742         }
   744         return leave();
   745     }
   747     @Override
   748     public boolean enterUnaryNode(final UnaryNode unaryNode) {
   749         enterDefault(unaryNode);
   751         final TokenType tokenType = unaryNode.tokenType();
   752         if (tokenType == TokenType.NEW) {
   753             type("NewExpression");
   754             comma();
   756             final CallNode callNode = (CallNode)unaryNode.rhs();
   757             property("callee");
   758             callNode.getFunction().accept(this);
   759             comma();
   761             array("arguments", callNode.getArgs());
   762         } else {
   763             final boolean prefix;
   764             final String operator;
   765             switch (tokenType) {
   766             case INCPOSTFIX:
   767                 prefix = false;
   768                 operator = "++";
   769                 break;
   770             case DECPOSTFIX:
   771                 prefix = false;
   772                 operator = "--";
   773                 break;
   774             case INCPREFIX:
   775                 operator = "++";
   776                 prefix = true;
   777                 break;
   778             case DECPREFIX:
   779                 operator = "--";
   780                 prefix = true;
   781                 break;
   782             default:
   783                 prefix = false;
   784                 operator = tokenType.getName();
   785             }
   787             type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression");
   788             comma();
   790             property("operator", operator);
   791             comma();
   793             property("prefix", prefix);
   794             comma();
   796             property("argument");
   797             unaryNode.rhs().accept(this);
   798         }
   800         return leave();
   801     }
   803     @Override
   804     public boolean enterVarNode(final VarNode varNode) {
   805         enterDefault(varNode);
   807         type("VariableDeclaration");
   808         comma();
   810         arrayStart("declarations");
   812         // VariableDeclarator
   813         objectStart();
   814         location(varNode.getName());
   816         type("VariableDeclarator");
   817         comma();
   819         property("id", varNode.getName().toString());
   820         comma();
   822         property("init");
   823         final Node init = varNode.getInit();
   824         if (init != null) {
   825             init.accept(this);
   826         } else {
   827             nullValue();
   828         }
   830         // VariableDeclarator
   831         objectEnd();
   833         // declarations
   834         arrayEnd();
   836         return leave();
   837     }
   839     @Override
   840     public boolean enterWhileNode(final WhileNode whileNode) {
   841         enterDefault(whileNode);
   843         type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
   844         comma();
   846         if (whileNode.isDoWhile()) {
   847             property("body");
   848             whileNode.getBody().accept(this);
   849             comma();
   851             property("test");
   852             whileNode.getTest().accept(this);
   853         } else {
   854             property("test");
   855             whileNode.getTest().accept(this);
   856             comma();
   858             property("block");
   859             whileNode.getBody().accept(this);
   860         }
   862         return leave();
   863     }
   865     @Override
   866     public boolean enterWithNode(final WithNode withNode) {
   867         enterDefault(withNode);
   869         type("WithStatement");
   870         comma();
   872         property("object");
   873         withNode.getExpression().accept(this);
   874         comma();
   876         property("body");
   877         withNode.getBody().accept(this);
   879         return leave();
   880    }
   882     // Internals below
   884     private JSONWriter(final boolean includeLocation) {
   885         super(new LexicalContext());
   886         this.buf             = new StringBuilder();
   887         this.includeLocation = includeLocation;
   888     }
   890     private final StringBuilder buf;
   891     private final boolean includeLocation;
   893     private String getString() {
   894         return buf.toString();
   895     }
   897     private void property(final String key, final String value) {
   898         buf.append('"');
   899         buf.append(key);
   900         buf.append("\":");
   901         if (value != null) {
   902             buf.append('"');
   903             buf.append(value);
   904             buf.append('"');
   905         }
   906     }
   908     private void property(final String key, final boolean value) {
   909         property(key, Boolean.toString(value));
   910     }
   912     private void property(final String key, final int value) {
   913         property(key, Integer.toString(value));
   914     }
   916     private void property(final String key) {
   917         property(key, null);
   918     }
   920     private void type(final String value) {
   921         property("type", value);
   922     }
   924     private void objectStart(final String name) {
   925         buf.append('"');
   926         buf.append(name);
   927         buf.append("\":{");
   928     }
   930     private void objectStart() {
   931         buf.append('{');
   932     }
   934     private void objectEnd() {
   935         buf.append('}');
   936     }
   938     private void array(final String name, final List<? extends Node> nodes) {
   939         // The size, idx comparison is just to avoid trailing comma..
   940         final int size = nodes.size();
   941         int idx = 0;
   942         arrayStart(name);
   943         for (final Node node : nodes) {
   944             if (node != null) {
   945                 node.accept(this);
   946             } else {
   947                 nullValue();
   948             }
   949             if (idx != (size - 1)) {
   950                 comma();
   951             }
   952             idx++;
   953         }
   954         arrayEnd();
   955     }
   957     private void arrayStart(final String name) {
   958         buf.append('"');
   959         buf.append(name);
   960         buf.append('"');
   961         buf.append(':');
   962         buf.append('[');
   963     }
   965     private void arrayEnd() {
   966         buf.append(']');
   967     }
   969     private void comma() {
   970         buf.append(',');
   971     }
   973     private void nullValue() {
   974         buf.append("null");
   975     }
   977     private void location(final Node node) {
   978         if (includeLocation) {
   979             objectStart("loc");
   981             // source name
   982             final Source src = lc.getCurrentFunction().getSource();
   983             property("source", src.getName());
   984             comma();
   986             // start position
   987             objectStart("start");
   988             final int start = node.getStart();
   989             property("line", src.getLine(start));
   990             comma();
   991             property("column", src.getColumn(start));
   992             objectEnd();
   993             comma();
   995             // end position
   996             objectStart("end");
   997             final int end = node.getFinish();
   998             property("line", src.getLine(end));
   999             comma();
  1000             property("column", src.getColumn(end));
  1001             objectEnd();
  1003             // end 'loc'
  1004             objectEnd();
  1006             comma();
  1010     private static String quote(final String str) {
  1011         return JSONParser.quote(str);

mercurial