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

Print this page




  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  */
  25 
  26 package jdk.nashorn.internal.ir.debug;
  27 
  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;


 180         comma();
 181 
 182         property("left");
 183         binaryNode.lhs().accept(this);
 184         comma();
 185 
 186         property("right");
 187         binaryNode.rhs().accept(this);
 188 
 189         return leave();
 190     }
 191 
 192     @Override
 193     public boolean enterBreakNode(final BreakNode breakNode) {
 194         enterDefault(breakNode);
 195 
 196         type("BreakStatement");
 197         comma();
 198 
 199         final IdentNode label = breakNode.getLabel();

 200         if (label != null) {
 201             property("label", label.getName());
 202         } else {
 203             property("label");
 204             nullValue();
 205         }
 206 
 207         return leave();
 208     }
 209 
 210     @Override
 211     public boolean enterCallNode(final CallNode callNode) {
 212         enterDefault(callNode);
 213 
 214         type("CallExpression");
 215         comma();
 216 
 217         property("callee");
 218         callNode.getFunction().accept(this);
 219         comma();
 220 
 221         array("arguments", callNode.getArgs());
 222 
 223         return leave();


 239         }
 240         comma();
 241 
 242         array("consequent", caseNode.getBody().getStatements());
 243 
 244         return leave();
 245     }
 246 
 247     @Override
 248     public boolean enterCatchNode(final CatchNode catchNode) {
 249         enterDefault(catchNode);
 250 
 251         type("CatchClause");
 252         comma();
 253 
 254         property("param");
 255         catchNode.getException().accept(this);
 256         comma();
 257 
 258         final Node guard = catchNode.getExceptionCondition();
 259         property("guard");
 260         if (guard != null) {

 261             guard.accept(this);
 262         } else {
 263             nullValue();
 264         }
 265         comma();

 266 
 267         property("body");
 268         catchNode.getBody().accept(this);
 269 
 270         return leave();
 271     }
 272 
 273     @Override
 274     public boolean enterContinueNode(final ContinueNode continueNode) {
 275         enterDefault(continueNode);
 276 
 277         type("ContinueStatement");
 278         comma();
 279 
 280         final IdentNode label = continueNode.getLabel();

 281         if (label != null) {
 282             property("label", label.getName());
 283         } else {
 284             property("label");
 285             nullValue();
 286         }
 287 
 288         return leave();
 289     }
 290 
 291     @Override
 292     public boolean enterEmptyNode(final EmptyNode emptyNode) {
 293         enterDefault(emptyNode);
 294 
 295         type("EmptyStatement");
 296 
 297         return leave();
 298     }
 299 
 300     @Override
 301     public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {







 302         enterDefault(expressionStatement);
 303 
 304         type("ExpressionStatement");
 305         comma();
 306 
 307         property("expression");
 308         expressionStatement.getExpression().accept(this);
 309 
 310         return leave();
 311     }
 312 
 313     @Override
 314     public boolean enterBlockStatement(BlockStatement blockStatement) {
 315         enterDefault(blockStatement);
 316 
 317         type("BlockStatement");
 318         comma();
 319 
 320         property("block");
 321         blockStatement.getBlock().accept(this);
 322 
 323         return leave();
 324     }
 325 
 326     @Override
 327     public boolean enterForNode(final ForNode forNode) {
 328         enterDefault(forNode);


 371             comma();
 372 
 373             final Node update = forNode.getModify();
 374             property("update");
 375             if (update != null) {
 376                 update.accept(this);
 377             } else {
 378                 nullValue();
 379             }
 380             comma();
 381 
 382             property("body");
 383             forNode.getBody().accept(this);
 384         }
 385 
 386         return leave();
 387     }
 388 
 389     @Override
 390     public boolean enterFunctionNode(final FunctionNode functionNode) {
 391         enterDefault(functionNode);
 392 
 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();
 404 
 405         if (! program) {
 406             property("id");
 407             if (functionNode.isAnonymous()) {
 408                 nullValue();
 409             } else {
 410                 functionNode.getIdent().accept(this);
 411             }
 412             comma();
 413         }






 414 
 415         property("rest");
 416         nullValue();
 417         comma();
 418 
 419         if (!program) {
 420             array("params", functionNode.getParameters());



 421             comma();




 422         }
 423 





 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");
 429 
 430         for (final Node stat : stats) {
 431             stat.accept(this);
 432             if (idx != (size - 1)) {
 433                 comma();
 434             }
 435             idx++;
 436         }
 437         arrayEnd();
 438 
 439         return leave();
 440     }
 441 
 442     @Override
 443     public boolean enterIdentNode(final IdentNode identNode) {


 713         type("ThrowStatement");
 714         comma();
 715 
 716         property("argument");
 717         throwNode.getExpression().accept(this);
 718 
 719         return leave();
 720     }
 721 
 722     @Override
 723     public boolean enterTryNode(final TryNode tryNode) {
 724         enterDefault(tryNode);
 725 
 726         type("TryStatement");
 727         comma();
 728 
 729         property("block");
 730         tryNode.getBody().accept(this);
 731         comma();
 732 
 733         array("handlers", tryNode.getCatches());
























 734         comma();
 735 
 736         property("finalizer");
 737         final Node finallyNode = tryNode.getFinallyBody();
 738         if (finallyNode != null) {
 739             finallyNode.accept(this);
 740         } else {
 741             nullValue();
 742         }
 743 
 744         return leave();
 745     }
 746 
 747     @Override
 748     public boolean enterUnaryNode(final UnaryNode unaryNode) {
 749         enterDefault(unaryNode);
 750 
 751         final TokenType tokenType = unaryNode.tokenType();
 752         if (tokenType == TokenType.NEW) {
 753             type("NewExpression");
 754             comma();
 755 
 756             final CallNode callNode = (CallNode)unaryNode.rhs();
 757             property("callee");
 758             callNode.getFunction().accept(this);
 759             comma();
 760 
 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             }
 786 
 787             type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression");
 788             comma();
 789 
 790             property("operator", operator);
 791             comma();
 792 
 793             property("prefix", prefix);
 794             comma();
 795 
 796             property("argument");
 797             unaryNode.rhs().accept(this);
 798         }
 799 
 800         return leave();
 801     }
 802 
 803     @Override
 804     public boolean enterVarNode(final VarNode varNode) {








 805         enterDefault(varNode);
 806 
 807         type("VariableDeclaration");
 808         comma();
 809 
 810         arrayStart("declarations");
 811 
 812         // VariableDeclarator
 813         objectStart();
 814         location(varNode.getName());
 815 
 816         type("VariableDeclarator");
 817         comma();
 818 
 819         property("id", varNode.getName().toString());

 820         comma();
 821 
 822         property("init");
 823         final Node init = varNode.getInit();
 824         if (init != null) {
 825             init.accept(this);
 826         } else {
 827             nullValue();
 828         }
 829 
 830         // VariableDeclarator
 831         objectEnd();
 832 
 833         // declarations
 834         arrayEnd();
 835 
 836         return leave();
 837     }
 838 
 839     @Override
 840     public boolean enterWhileNode(final WhileNode whileNode) {
 841         enterDefault(whileNode);
 842 
 843         type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
 844         comma();
 845 
 846         if (whileNode.isDoWhile()) {
 847             property("body");
 848             whileNode.getBody().accept(this);
 849             comma();
 850 
 851             property("test");
 852             whileNode.getTest().accept(this);
 853         } else {
 854             property("test");
 855             whileNode.getTest().accept(this);
 856             comma();
 857 
 858             property("block");
 859             whileNode.getBody().accept(this);
 860         }
 861 
 862         return leave();
 863     }
 864 
 865     @Override
 866     public boolean enterWithNode(final WithNode withNode) {
 867         enterDefault(withNode);
 868 
 869         type("WithStatement");
 870         comma();
 871 
 872         property("object");
 873         withNode.getExpression().accept(this);
 874         comma();
 875 
 876         property("body");
 877         withNode.getBody().accept(this);
 878 
 879         return leave();
 880    }
 881 
 882     // Internals below
 883 
 884     private JSONWriter(final boolean includeLocation) {
 885         super(new LexicalContext());
 886         this.buf             = new StringBuilder();
 887         this.includeLocation = includeLocation;
 888     }
 889 
 890     private final StringBuilder buf;
 891     private final boolean includeLocation;
 892 
 893     private String getString() {
 894         return buf.toString();
 895     }
 896 
 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     }
 907 
 908     private void property(final String key, final boolean value) {
 909         property(key, Boolean.toString(value));
 910     }
 911 
 912     private void property(final String key, final int value) {
 913         property(key, Integer.toString(value));
 914     }
 915 
 916     private void property(final String key) {
 917         property(key, null);
 918     }
 919 
 920     private void type(final String value) {
 921         property("type", value);
 922     }
 923 
 924     private void objectStart(final String name) {
 925         buf.append('"');
 926         buf.append(name);
 927         buf.append("\":{");
 928     }
 929 
 930     private void objectStart() {
 931         buf.append('{');
 932     }
 933 




  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  */
  25 
  26 package jdk.nashorn.internal.ir.debug;
  27 
  28 import java.util.Arrays;
  29 import java.util.List;
  30 import java.util.ArrayList;
  31 import jdk.nashorn.internal.codegen.CompilerConstants;
  32 import jdk.nashorn.internal.ir.AccessNode;
  33 import jdk.nashorn.internal.ir.BinaryNode;
  34 import jdk.nashorn.internal.ir.Block;
  35 import jdk.nashorn.internal.ir.BlockStatement;
  36 import jdk.nashorn.internal.ir.BreakNode;
  37 import jdk.nashorn.internal.ir.CallNode;
  38 import jdk.nashorn.internal.ir.CaseNode;
  39 import jdk.nashorn.internal.ir.CatchNode;
  40 import jdk.nashorn.internal.ir.ContinueNode;
  41 import jdk.nashorn.internal.ir.EmptyNode;
  42 import jdk.nashorn.internal.ir.ExpressionStatement;
  43 import jdk.nashorn.internal.ir.ForNode;
  44 import jdk.nashorn.internal.ir.FunctionNode;
  45 import jdk.nashorn.internal.ir.IdentNode;
  46 import jdk.nashorn.internal.ir.IfNode;
  47 import jdk.nashorn.internal.ir.IndexNode;
  48 import jdk.nashorn.internal.ir.LabelNode;
  49 import jdk.nashorn.internal.ir.LexicalContext;
  50 import jdk.nashorn.internal.ir.LiteralNode;


 181         comma();
 182 
 183         property("left");
 184         binaryNode.lhs().accept(this);
 185         comma();
 186 
 187         property("right");
 188         binaryNode.rhs().accept(this);
 189 
 190         return leave();
 191     }
 192 
 193     @Override
 194     public boolean enterBreakNode(final BreakNode breakNode) {
 195         enterDefault(breakNode);
 196 
 197         type("BreakStatement");
 198         comma();
 199 
 200         final IdentNode label = breakNode.getLabel();
 201         property("label");
 202         if (label != null) {
 203             label.accept(this);
 204         } else {

 205             nullValue();
 206         }
 207 
 208         return leave();
 209     }
 210 
 211     @Override
 212     public boolean enterCallNode(final CallNode callNode) {
 213         enterDefault(callNode);
 214 
 215         type("CallExpression");
 216         comma();
 217 
 218         property("callee");
 219         callNode.getFunction().accept(this);
 220         comma();
 221 
 222         array("arguments", callNode.getArgs());
 223 
 224         return leave();


 240         }
 241         comma();
 242 
 243         array("consequent", caseNode.getBody().getStatements());
 244 
 245         return leave();
 246     }
 247 
 248     @Override
 249     public boolean enterCatchNode(final CatchNode catchNode) {
 250         enterDefault(catchNode);
 251 
 252         type("CatchClause");
 253         comma();
 254 
 255         property("param");
 256         catchNode.getException().accept(this);
 257         comma();
 258 
 259         final Node guard = catchNode.getExceptionCondition();

 260         if (guard != null) {
 261             property("guard");
 262             guard.accept(this);



 263             comma();
 264         }
 265 
 266         property("body");
 267         catchNode.getBody().accept(this);
 268 
 269         return leave();
 270     }
 271 
 272     @Override
 273     public boolean enterContinueNode(final ContinueNode continueNode) {
 274         enterDefault(continueNode);
 275 
 276         type("ContinueStatement");
 277         comma();
 278 
 279         final IdentNode label = continueNode.getLabel();
 280         property("label");
 281         if (label != null) {
 282             label.accept(this);
 283         } else {

 284             nullValue();
 285         }
 286 
 287         return leave();
 288     }
 289 
 290     @Override
 291     public boolean enterEmptyNode(final EmptyNode emptyNode) {
 292         enterDefault(emptyNode);
 293 
 294         type("EmptyStatement");
 295 
 296         return leave();
 297     }
 298 
 299     @Override
 300     public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
 301         // handle debugger statement
 302         final Node expression = expressionStatement.getExpression();
 303         if (expression instanceof RuntimeNode) {
 304             expression.accept(this);
 305             return false;
 306         }
 307 
 308         enterDefault(expressionStatement);
 309 
 310         type("ExpressionStatement");
 311         comma();
 312 
 313         property("expression");
 314         expression.accept(this);
 315 
 316         return leave();
 317     }
 318 
 319     @Override
 320     public boolean enterBlockStatement(BlockStatement blockStatement) {
 321         enterDefault(blockStatement);
 322 
 323         type("BlockStatement");
 324         comma();
 325 
 326         property("block");
 327         blockStatement.getBlock().accept(this);
 328 
 329         return leave();
 330     }
 331 
 332     @Override
 333     public boolean enterForNode(final ForNode forNode) {
 334         enterDefault(forNode);


 377             comma();
 378 
 379             final Node update = forNode.getModify();
 380             property("update");
 381             if (update != null) {
 382                 update.accept(this);
 383             } else {
 384                 nullValue();
 385             }
 386             comma();
 387 
 388             property("body");
 389             forNode.getBody().accept(this);
 390         }
 391 
 392         return leave();
 393     }
 394 
 395     @Override
 396     public boolean enterFunctionNode(final FunctionNode functionNode) {


 397         final boolean program = functionNode.isProgram();

 398         if (program) {
 399             return emitProgram(functionNode);
 400         }
 401 
 402         enterDefault(functionNode);
 403         final String name;
 404         if (functionNode.isDeclared()) {
 405             name = "FunctionDeclaration";
 406         } else {
 407             name = "FunctionExpression";
 408         }
 409         type(name);
 410         comma();
 411 

 412         property("id");
 413         if (functionNode.isAnonymous()) {
 414             nullValue();
 415         } else {
 416             functionNode.getIdent().accept(this);
 417         }
 418         comma();
 419 
 420         array("params", functionNode.getParameters());
 421         comma();
 422 
 423         arrayStart("defaults");
 424         arrayEnd();
 425         comma();
 426 
 427         property("rest");
 428         nullValue();
 429         comma();
 430 
 431         property("body");
 432         functionNode.getBody().accept(this);
 433         comma();
 434 
 435         property("generator", false);
 436         comma();
 437 
 438         property("expression", false);
 439 
 440         return leave();
 441     }
 442 
 443     private boolean emitProgram(final FunctionNode functionNode) {
 444         enterDefault(functionNode);
 445         type("Program");
 446         comma();
 447 
 448         // body consists of nested functions and statements
 449         final List<Statement> stats = functionNode.getBody().getStatements();
 450         final int size = stats.size();
 451         int idx = 0;
 452         arrayStart("body");
 453 
 454         for (final Node stat : stats) {
 455             stat.accept(this);
 456             if (idx != (size - 1)) {
 457                 comma();
 458             }
 459             idx++;
 460         }
 461         arrayEnd();
 462 
 463         return leave();
 464     }
 465 
 466     @Override
 467     public boolean enterIdentNode(final IdentNode identNode) {


 737         type("ThrowStatement");
 738         comma();
 739 
 740         property("argument");
 741         throwNode.getExpression().accept(this);
 742 
 743         return leave();
 744     }
 745 
 746     @Override
 747     public boolean enterTryNode(final TryNode tryNode) {
 748         enterDefault(tryNode);
 749 
 750         type("TryStatement");
 751         comma();
 752 
 753         property("block");
 754         tryNode.getBody().accept(this);
 755         comma();
 756 
 757 
 758         final List<? extends Node> catches = tryNode.getCatches();
 759         final List<CatchNode> guarded = new ArrayList<>();
 760         CatchNode unguarded = null;
 761         if (catches != null) {
 762             for (Node n : catches) {
 763                 CatchNode cn = (CatchNode)n;
 764                 if (cn.getExceptionCondition() != null) {
 765                     guarded.add(cn);
 766                 } else {
 767                     assert unguarded == null: "too many unguarded?";
 768                     unguarded = cn;
 769                 }
 770             }
 771         }
 772 
 773         array("guardedHandlers", guarded);
 774         comma();
 775 
 776         property("handler");
 777         if (unguarded != null) {
 778             unguarded.accept(this);
 779         } else {
 780             nullValue();
 781         }
 782         comma();
 783 
 784         property("finalizer");
 785         final Node finallyNode = tryNode.getFinallyBody();
 786         if (finallyNode != null) {
 787             finallyNode.accept(this);
 788         } else {
 789             nullValue();
 790         }
 791 
 792         return leave();
 793     }
 794 
 795     @Override
 796     public boolean enterUnaryNode(final UnaryNode unaryNode) {
 797         enterDefault(unaryNode);
 798 
 799         final TokenType tokenType = unaryNode.tokenType();
 800         if (tokenType == TokenType.NEW) {
 801             type("NewExpression");
 802             comma();
 803 
 804             final CallNode callNode = (CallNode)unaryNode.rhs();
 805             property("callee");
 806             callNode.getFunction().accept(this);
 807             comma();
 808 
 809             array("arguments", callNode.getArgs());
 810         } else {

 811             final String operator;
 812             final boolean prefix;
 813             switch (tokenType) {
 814             case INCPOSTFIX:
 815                 prefix = false;
 816                 operator = "++";
 817                 break;
 818             case DECPOSTFIX:
 819                 prefix = false;
 820                 operator = "--";
 821                 break;
 822             case INCPREFIX:
 823                 operator = "++";
 824                 prefix = true;
 825                 break;
 826             case DECPREFIX:
 827                 operator = "--";
 828                 prefix = true;
 829                 break;
 830             default:
 831                 prefix = true;
 832                 operator = tokenType.getName();
 833                 break;
 834             }
 835 
 836             type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression");
 837             comma();
 838 
 839             property("operator", operator);
 840             comma();
 841 
 842             property("prefix", prefix);
 843             comma();
 844 
 845             property("argument");
 846             unaryNode.rhs().accept(this);
 847         }
 848 
 849         return leave();
 850     }
 851 
 852     @Override
 853     public boolean enterVarNode(final VarNode varNode) {
 854         final Node init = varNode.getInit();
 855         if (init instanceof FunctionNode && ((FunctionNode)init).isDeclared()) {
 856             // function declaration - don't emit VariableDeclaration instead
 857             // just emit FunctionDeclaration using 'init' Node.
 858             init.accept(this);
 859             return false;
 860         }
 861 
 862         enterDefault(varNode);
 863 
 864         type("VariableDeclaration");
 865         comma();
 866 
 867         arrayStart("declarations");
 868 
 869         // VariableDeclarator
 870         objectStart();
 871         location(varNode.getName());
 872 
 873         type("VariableDeclarator");
 874         comma();
 875 
 876         property("id");
 877         varNode.getName().accept(this);
 878         comma();
 879 
 880         property("init");

 881         if (init != null) {
 882             init.accept(this);
 883         } else {
 884             nullValue();
 885         }
 886 
 887         // VariableDeclarator
 888         objectEnd();
 889 
 890         // declarations
 891         arrayEnd();
 892 
 893         return leave();
 894     }
 895 
 896     @Override
 897     public boolean enterWhileNode(final WhileNode whileNode) {
 898         enterDefault(whileNode);
 899 
 900         type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
 901         comma();
 902 
 903         if (whileNode.isDoWhile()) {
 904             property("body");
 905             whileNode.getBody().accept(this);
 906             comma();
 907 
 908             property("test");
 909             whileNode.getTest().accept(this);
 910         } else {
 911             property("test");
 912             whileNode.getTest().accept(this);
 913             comma();
 914 
 915             property("body");
 916             whileNode.getBody().accept(this);
 917         }
 918 
 919         return leave();
 920     }
 921 
 922     @Override
 923     public boolean enterWithNode(final WithNode withNode) {
 924         enterDefault(withNode);
 925 
 926         type("WithStatement");
 927         comma();
 928 
 929         property("object");
 930         withNode.getExpression().accept(this);
 931         comma();
 932 
 933         property("body");
 934         withNode.getBody().accept(this);
 935 
 936         return leave();
 937    }
 938 
 939     // Internals below
 940 
 941     private JSONWriter(final boolean includeLocation) {
 942         super(new LexicalContext());
 943         this.buf             = new StringBuilder();
 944         this.includeLocation = includeLocation;
 945     }
 946 
 947     private final StringBuilder buf;
 948     private final boolean includeLocation;
 949 
 950     private String getString() {
 951         return buf.toString();
 952     }
 953 
 954     private void property(final String key, final String value, final boolean escape) {
 955         buf.append('"');
 956         buf.append(key);
 957         buf.append("\":");
 958         if (value != null) {
 959             if (escape) buf.append('"');
 960             buf.append(value);
 961             if (escape) buf.append('"');
 962         }
 963     }
 964 
 965     private void property(final String key, final String value) {
 966         property(key, value, true);
 967     }
 968 
 969     private void property(final String key, final boolean value) {
 970         property(key, Boolean.toString(value), false);
 971     }
 972 
 973     private void property(final String key, final int value) {
 974         property(key, Integer.toString(value), false);
 975     }
 976 
 977     private void property(final String key) {
 978         property(key, null);
 979     }
 980 
 981     private void type(final String value) {
 982         property("type", value);
 983     }
 984 
 985     private void objectStart(final String name) {
 986         buf.append('"');
 987         buf.append(name);
 988         buf.append("\":{");
 989     }
 990 
 991     private void objectStart() {
 992         buf.append('{');
 993     }
 994