1499 lines
51 KiB
Java
1499 lines
51 KiB
Java
|
/*
|
||
|
* Copyright 2004 The Closure Compiler Authors.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package com.google.javascript.jscomp;
|
||
|
|
||
|
import com.google.common.base.Joiner;
|
||
|
import com.google.common.collect.Lists;
|
||
|
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
|
||
|
import com.google.javascript.rhino.InputId;
|
||
|
import com.google.javascript.rhino.Node;
|
||
|
import com.google.javascript.rhino.Token;
|
||
|
|
||
|
import junit.framework.TestCase;
|
||
|
|
||
|
import java.util.List;
|
||
|
|
||
|
public class CodePrinterTest extends TestCase {
|
||
|
boolean trustedStrings = true;
|
||
|
|
||
|
@Override public void setUp() {
|
||
|
trustedStrings = true;
|
||
|
}
|
||
|
|
||
|
Node parse(String js) {
|
||
|
return parse(js, false);
|
||
|
}
|
||
|
|
||
|
Node parse(String js, boolean checkTypes) {
|
||
|
Compiler compiler = new Compiler();
|
||
|
CompilerOptions options = new CompilerOptions();
|
||
|
options.setTrustedStrings(trustedStrings);
|
||
|
|
||
|
// Allow getters and setters.
|
||
|
options.setLanguageIn(LanguageMode.ECMASCRIPT5);
|
||
|
compiler.initOptions(options);
|
||
|
Node n = compiler.parseTestCode(js);
|
||
|
|
||
|
if (checkTypes) {
|
||
|
DefaultPassConfig passConfig = new DefaultPassConfig(null);
|
||
|
CompilerPass typeResolver = passConfig.resolveTypes.create(compiler);
|
||
|
Node externs = new Node(Token.SCRIPT);
|
||
|
externs.setInputId(new InputId("externs"));
|
||
|
Node externAndJsRoot = new Node(Token.BLOCK, externs, n);
|
||
|
externAndJsRoot.setIsSyntheticBlock(true);
|
||
|
typeResolver.process(externs, n);
|
||
|
CompilerPass inferTypes = passConfig.inferTypes.create(compiler);
|
||
|
inferTypes.process(externs, n);
|
||
|
}
|
||
|
|
||
|
checkUnexpectedErrorsOrWarnings(compiler, 0);
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
private static void checkUnexpectedErrorsOrWarnings(
|
||
|
Compiler compiler, int expected) {
|
||
|
int actual = compiler.getErrors().length + compiler.getWarnings().length;
|
||
|
if (actual != expected) {
|
||
|
String msg = "";
|
||
|
for (JSError err : compiler.getErrors()) {
|
||
|
msg += "Error:" + err.toString() + "\n";
|
||
|
}
|
||
|
for (JSError err : compiler.getWarnings()) {
|
||
|
msg += "Warning:" + err.toString() + "\n";
|
||
|
}
|
||
|
assertEquals("Unexpected warnings or errors.\n " + msg, expected, actual);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
String parsePrint(String js, boolean prettyprint, int lineThreshold) {
|
||
|
CompilerOptions options = new CompilerOptions();
|
||
|
options.setTrustedStrings(trustedStrings);
|
||
|
options.setPrettyPrint(prettyprint);
|
||
|
options.setLineLengthThreshold(lineThreshold);
|
||
|
return new CodePrinter.Builder(parse(js)).setCompilerOptions(options)
|
||
|
.build();
|
||
|
}
|
||
|
|
||
|
String parsePrint(String js, boolean prettyprint, boolean lineBreak,
|
||
|
int lineThreshold) {
|
||
|
CompilerOptions options = new CompilerOptions();
|
||
|
options.setTrustedStrings(trustedStrings);
|
||
|
options.setPrettyPrint(prettyprint);
|
||
|
options.setLineLengthThreshold(lineThreshold);
|
||
|
options.setLineBreak(lineBreak);
|
||
|
return new CodePrinter.Builder(parse(js)).setCompilerOptions(options)
|
||
|
.build();
|
||
|
}
|
||
|
|
||
|
String parsePrint(String js, boolean prettyprint, boolean lineBreak,
|
||
|
boolean preferLineBreakAtEof, int lineThreshold) {
|
||
|
CompilerOptions options = new CompilerOptions();
|
||
|
options.setTrustedStrings(trustedStrings);
|
||
|
options.setPrettyPrint(prettyprint);
|
||
|
options.setLineLengthThreshold(lineThreshold);
|
||
|
options.setPreferLineBreakAtEndOfFile(preferLineBreakAtEof);
|
||
|
options.setLineBreak(lineBreak);
|
||
|
return new CodePrinter.Builder(parse(js)).setCompilerOptions(options)
|
||
|
.build();
|
||
|
}
|
||
|
|
||
|
String parsePrint(String js, boolean prettyprint, boolean lineBreak,
|
||
|
int lineThreshold, boolean outputTypes) {
|
||
|
CompilerOptions options = new CompilerOptions();
|
||
|
options.setTrustedStrings(trustedStrings);
|
||
|
options.setPrettyPrint(prettyprint);
|
||
|
options.setLineLengthThreshold(lineThreshold);
|
||
|
options.setLineBreak(lineBreak);
|
||
|
return new CodePrinter.Builder(parse(js, true)).setCompilerOptions(options)
|
||
|
.setOutputTypes(outputTypes)
|
||
|
.build();
|
||
|
}
|
||
|
|
||
|
String parsePrint(String js, boolean prettyprint, boolean lineBreak,
|
||
|
int lineThreshold, boolean outputTypes,
|
||
|
boolean tagAsStrict) {
|
||
|
CompilerOptions options = new CompilerOptions();
|
||
|
options.setTrustedStrings(trustedStrings);
|
||
|
options.setPrettyPrint(prettyprint);
|
||
|
options.setLineLengthThreshold(lineThreshold);
|
||
|
options.setLineBreak(lineBreak);
|
||
|
return new CodePrinter.Builder(parse(js, true)).setCompilerOptions(options)
|
||
|
.setOutputTypes(outputTypes)
|
||
|
.setTagAsStrict(tagAsStrict)
|
||
|
.build();
|
||
|
}
|
||
|
|
||
|
|
||
|
String printNode(Node n) {
|
||
|
CompilerOptions options = new CompilerOptions();
|
||
|
options.setLineLengthThreshold(CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD);
|
||
|
return new CodePrinter.Builder(n).setCompilerOptions(options).build();
|
||
|
}
|
||
|
|
||
|
void assertPrintNode(String expectedJs, Node ast) {
|
||
|
assertEquals(expectedJs, printNode(ast));
|
||
|
}
|
||
|
|
||
|
public void testPrint() {
|
||
|
assertPrint("10 + a + b", "10+a+b");
|
||
|
assertPrint("10 + (30*50)", "10+30*50");
|
||
|
assertPrint("with(x) { x + 3; }", "with(x)x+3");
|
||
|
assertPrint("\"aa'a\"", "\"aa'a\"");
|
||
|
assertPrint("\"aa\\\"a\"", "'aa\"a'");
|
||
|
assertPrint("function foo()\n{return 10;}", "function foo(){return 10}");
|
||
|
assertPrint("a instanceof b", "a instanceof b");
|
||
|
assertPrint("typeof(a)", "typeof a");
|
||
|
assertPrint(
|
||
|
"var foo = x ? { a : 1 } : {a: 3, b:4, \"default\": 5, \"foo-bar\": 6}",
|
||
|
"var foo=x?{a:1}:{a:3,b:4,\"default\":5,\"foo-bar\":6}");
|
||
|
|
||
|
// Safari: needs ';' at the end of a throw statement
|
||
|
assertPrint("function foo(){throw 'error';}",
|
||
|
"function foo(){throw\"error\";}");
|
||
|
// Safari 3 needs a "{" around a single function
|
||
|
assertPrint("if (true) function foo(){return}",
|
||
|
"if(true){function foo(){return}}");
|
||
|
|
||
|
assertPrint("var x = 10; { var y = 20; }", "var x=10;var y=20");
|
||
|
|
||
|
assertPrint("while (x-- > 0);", "while(x-- >0);");
|
||
|
assertPrint("x-- >> 1", "x-- >>1");
|
||
|
|
||
|
assertPrint("(function () {})(); ",
|
||
|
"(function(){})()");
|
||
|
|
||
|
// Associativity
|
||
|
assertPrint("var a,b,c,d;a || (b&& c) && (a || d)",
|
||
|
"var a,b,c,d;a||b&&c&&(a||d)");
|
||
|
assertPrint("var a,b,c; a || (b || c); a * (b * c); a | (b | c)",
|
||
|
"var a,b,c;a||b||c;a*b*c;a|b|c");
|
||
|
assertPrint("var a,b,c; a / b / c;a / (b / c); a - (b - c);",
|
||
|
"var a,b,c;a/b/c;a/(b/c);a-(b-c)");
|
||
|
assertPrint("var a,b; a = b = 3;",
|
||
|
"var a,b;a=b=3");
|
||
|
assertPrint("var a,b,c,d; a = (b = c = (d = 3));",
|
||
|
"var a,b,c,d;a=b=c=d=3");
|
||
|
assertPrint("var a,b,c; a += (b = c += 3);",
|
||
|
"var a,b,c;a+=b=c+=3");
|
||
|
assertPrint("var a,b,c; a *= (b -= c);",
|
||
|
"var a,b,c;a*=b-=c");
|
||
|
|
||
|
// Precedence
|
||
|
assertPrint("a ? delete b[0] : 3", "a?delete b[0]:3");
|
||
|
assertPrint("(delete a[0])/10", "delete a[0]/10");
|
||
|
|
||
|
// optional '()' for new
|
||
|
|
||
|
// simple new
|
||
|
assertPrint("new A", "new A");
|
||
|
assertPrint("new A()", "new A");
|
||
|
assertPrint("new A('x')", "new A(\"x\")");
|
||
|
|
||
|
// calling instance method directly after new
|
||
|
assertPrint("new A().a()", "(new A).a()");
|
||
|
assertPrint("(new A).a()", "(new A).a()");
|
||
|
|
||
|
// this case should be fixed
|
||
|
assertPrint("new A('y').a()", "(new A(\"y\")).a()");
|
||
|
|
||
|
// internal class
|
||
|
assertPrint("new A.B", "new A.B");
|
||
|
assertPrint("new A.B()", "new A.B");
|
||
|
assertPrint("new A.B('z')", "new A.B(\"z\")");
|
||
|
|
||
|
// calling instance method directly after new internal class
|
||
|
assertPrint("(new A.B).a()", "(new A.B).a()");
|
||
|
assertPrint("new A.B().a()", "(new A.B).a()");
|
||
|
// this case should be fixed
|
||
|
assertPrint("new A.B('w').a()", "(new A.B(\"w\")).a()");
|
||
|
|
||
|
// Operators: make sure we don't convert binary + and unary + into ++
|
||
|
assertPrint("x + +y", "x+ +y");
|
||
|
assertPrint("x - (-y)", "x- -y");
|
||
|
assertPrint("x++ +y", "x++ +y");
|
||
|
assertPrint("x-- -y", "x-- -y");
|
||
|
assertPrint("x++ -y", "x++-y");
|
||
|
|
||
|
// Label
|
||
|
assertPrint("foo:for(;;){break foo;}", "foo:for(;;)break foo");
|
||
|
assertPrint("foo:while(1){continue foo;}", "foo:while(1)continue foo");
|
||
|
|
||
|
// Object literals.
|
||
|
assertPrint("({})", "({})");
|
||
|
assertPrint("var x = {};", "var x={}");
|
||
|
assertPrint("({}).x", "({}).x");
|
||
|
assertPrint("({})['x']", "({})[\"x\"]");
|
||
|
assertPrint("({}) instanceof Object", "({})instanceof Object");
|
||
|
assertPrint("({}) || 1", "({})||1");
|
||
|
assertPrint("1 || ({})", "1||{}");
|
||
|
assertPrint("({}) ? 1 : 2", "({})?1:2");
|
||
|
assertPrint("0 ? ({}) : 2", "0?{}:2");
|
||
|
assertPrint("0 ? 1 : ({})", "0?1:{}");
|
||
|
assertPrint("typeof ({})", "typeof{}");
|
||
|
assertPrint("f({})", "f({})");
|
||
|
|
||
|
// Anonymous function expressions.
|
||
|
assertPrint("(function(){})", "(function(){})");
|
||
|
assertPrint("(function(){})()", "(function(){})()");
|
||
|
assertPrint("(function(){})instanceof Object",
|
||
|
"(function(){})instanceof Object");
|
||
|
assertPrint("(function(){}).bind().call()",
|
||
|
"(function(){}).bind().call()");
|
||
|
assertPrint("var x = function() { };", "var x=function(){}");
|
||
|
assertPrint("var x = function() { }();", "var x=function(){}()");
|
||
|
assertPrint("(function() {}), 2", "(function(){}),2");
|
||
|
|
||
|
// Name functions expression.
|
||
|
assertPrint("(function f(){})", "(function f(){})");
|
||
|
|
||
|
// Function declaration.
|
||
|
assertPrint("function f(){}", "function f(){}");
|
||
|
|
||
|
// Make sure we don't treat non-Latin character escapes as raw strings.
|
||
|
assertPrint("({ 'a': 4, '\\u0100': 4 })", "({\"a\":4,\"\\u0100\":4})");
|
||
|
assertPrint("({ a: 4, '\\u0100': 4 })", "({a:4,\"\\u0100\":4})");
|
||
|
|
||
|
// Test if statement and for statements with single statements in body.
|
||
|
assertPrint("if (true) { alert();}", "if(true)alert()");
|
||
|
assertPrint("if (false) {} else {alert(\"a\");}",
|
||
|
"if(false);else alert(\"a\")");
|
||
|
assertPrint("for(;;) { alert();};", "for(;;)alert()");
|
||
|
|
||
|
assertPrint("do { alert(); } while(true);",
|
||
|
"do alert();while(true)");
|
||
|
assertPrint("myLabel: { alert();}",
|
||
|
"myLabel:alert()");
|
||
|
assertPrint("myLabel: for(;;) continue myLabel;",
|
||
|
"myLabel:for(;;)continue myLabel");
|
||
|
|
||
|
// Test nested var statement
|
||
|
assertPrint("if (true) var x; x = 4;", "if(true)var x;x=4");
|
||
|
|
||
|
// Non-latin identifier. Make sure we keep them escaped.
|
||
|
assertPrint("\\u00fb", "\\u00fb");
|
||
|
assertPrint("\\u00fa=1", "\\u00fa=1");
|
||
|
assertPrint("function \\u00f9(){}", "function \\u00f9(){}");
|
||
|
assertPrint("x.\\u00f8", "x.\\u00f8");
|
||
|
assertPrint("x.\\u00f8", "x.\\u00f8");
|
||
|
assertPrint("abc\\u4e00\\u4e01jkl", "abc\\u4e00\\u4e01jkl");
|
||
|
|
||
|
// Test the right-associative unary operators for spurious parens
|
||
|
assertPrint("! ! true", "!!true");
|
||
|
assertPrint("!(!(true))", "!!true");
|
||
|
assertPrint("typeof(void(0))", "typeof void 0");
|
||
|
assertPrint("typeof(void(!0))", "typeof void!0");
|
||
|
assertPrint("+ - + + - + 3", "+-+ +-+3"); // chained unary plus/minus
|
||
|
assertPrint("+(--x)", "+--x");
|
||
|
assertPrint("-(++x)", "-++x");
|
||
|
|
||
|
// needs a space to prevent an ambiguous parse
|
||
|
assertPrint("-(--x)", "- --x");
|
||
|
assertPrint("!(~~5)", "!~~5");
|
||
|
assertPrint("~(a/b)", "~(a/b)");
|
||
|
|
||
|
// Preserve parens to overcome greedy binding of NEW
|
||
|
assertPrint("new (foo.bar()).factory(baz)", "new (foo.bar().factory)(baz)");
|
||
|
assertPrint("new (bar()).factory(baz)", "new (bar().factory)(baz)");
|
||
|
assertPrint("new (new foobar(x)).factory(baz)",
|
||
|
"new (new foobar(x)).factory(baz)");
|
||
|
|
||
|
// Make sure that HOOK is right associative
|
||
|
assertPrint("a ? b : (c ? d : e)", "a?b:c?d:e");
|
||
|
assertPrint("a ? (b ? c : d) : e", "a?b?c:d:e");
|
||
|
assertPrint("(a ? b : c) ? d : e", "(a?b:c)?d:e");
|
||
|
|
||
|
// Test nested ifs
|
||
|
assertPrint("if (x) if (y); else;", "if(x)if(y);else;");
|
||
|
|
||
|
// Test comma.
|
||
|
assertPrint("a,b,c", "a,b,c");
|
||
|
assertPrint("(a,b),c", "a,b,c");
|
||
|
assertPrint("a,(b,c)", "a,b,c");
|
||
|
assertPrint("x=a,b,c", "x=a,b,c");
|
||
|
assertPrint("x=(a,b),c", "x=(a,b),c");
|
||
|
assertPrint("x=a,(b,c)", "x=a,b,c");
|
||
|
assertPrint("x=a,y=b,z=c", "x=a,y=b,z=c");
|
||
|
assertPrint("x=(a,y=b,z=c)", "x=(a,y=b,z=c)");
|
||
|
assertPrint("x=[a,b,c,d]", "x=[a,b,c,d]");
|
||
|
assertPrint("x=[(a,b,c),d]", "x=[(a,b,c),d]");
|
||
|
assertPrint("x=[(a,(b,c)),d]", "x=[(a,b,c),d]");
|
||
|
assertPrint("x=[a,(b,c,d)]", "x=[a,(b,c,d)]");
|
||
|
assertPrint("var x=(a,b)", "var x=(a,b)");
|
||
|
assertPrint("var x=a,b,c", "var x=a,b,c");
|
||
|
assertPrint("var x=(a,b),c", "var x=(a,b),c");
|
||
|
assertPrint("var x=a,b=(c,d)", "var x=a,b=(c,d)");
|
||
|
assertPrint("foo(a,b,c,d)", "foo(a,b,c,d)");
|
||
|
assertPrint("foo((a,b,c),d)", "foo((a,b,c),d)");
|
||
|
assertPrint("foo((a,(b,c)),d)", "foo((a,b,c),d)");
|
||
|
assertPrint("f(a+b,(c,d,(e,f,g)))", "f(a+b,(c,d,e,f,g))");
|
||
|
assertPrint("({}) , 1 , 2", "({}),1,2");
|
||
|
assertPrint("({}) , {} , {}", "({}),{},{}");
|
||
|
|
||
|
// EMPTY nodes
|
||
|
assertPrint("if (x){}", "if(x);");
|
||
|
assertPrint("if(x);", "if(x);");
|
||
|
assertPrint("if(x)if(y);", "if(x)if(y);");
|
||
|
assertPrint("if(x){if(y);}", "if(x)if(y);");
|
||
|
assertPrint("if(x){if(y){};;;}", "if(x)if(y);");
|
||
|
assertPrint("if(x){;;function y(){};;}", "if(x){function y(){}}");
|
||
|
}
|
||
|
|
||
|
public void testBreakTrustedStrings() {
|
||
|
// Break scripts
|
||
|
assertPrint("'<script>'", "\"<script>\"");
|
||
|
assertPrint("'</script>'", "\"\\x3c/script>\"");
|
||
|
assertPrint("\"</script> </SCRIPT>\"", "\"\\x3c/script> \\x3c/SCRIPT>\"");
|
||
|
|
||
|
assertPrint("'-->'", "\"--\\x3e\"");
|
||
|
assertPrint("']]>'", "\"]]\\x3e\"");
|
||
|
assertPrint("' --></script>'", "\" --\\x3e\\x3c/script>\"");
|
||
|
|
||
|
assertPrint("/--> <\\/script>/g", "/--\\x3e <\\/script>/g");
|
||
|
|
||
|
// Break HTML start comments. Certain versions of WebKit
|
||
|
// begin an HTML comment when they see this.
|
||
|
assertPrint("'<!-- I am a string -->'",
|
||
|
"\"\\x3c!-- I am a string --\\x3e\"");
|
||
|
|
||
|
assertPrint("'<=&>'", "\"<=&>\"");
|
||
|
}
|
||
|
|
||
|
public void testBreakUntrustedStrings() {
|
||
|
trustedStrings = false;
|
||
|
|
||
|
// Break scripts
|
||
|
assertPrint("'<script>'", "\"\\x3cscript\\x3e\"");
|
||
|
assertPrint("'</script>'", "\"\\x3c/script\\x3e\"");
|
||
|
assertPrint("\"</script> </SCRIPT>\"", "\"\\x3c/script\\x3e \\x3c/SCRIPT\\x3e\"");
|
||
|
|
||
|
assertPrint("'-->'", "\"--\\x3e\"");
|
||
|
assertPrint("']]>'", "\"]]\\x3e\"");
|
||
|
assertPrint("' --></script>'", "\" --\\x3e\\x3c/script\\x3e\"");
|
||
|
|
||
|
assertPrint("/--> <\\/script>/g", "/--\\x3e <\\/script>/g");
|
||
|
|
||
|
// Break HTML start comments. Certain versions of WebKit
|
||
|
// begin an HTML comment when they see this.
|
||
|
assertPrint("'<!-- I am a string -->'",
|
||
|
"\"\\x3c!-- I am a string --\\x3e\"");
|
||
|
|
||
|
assertPrint("'<=&>'", "\"\\x3c\\x3d\\x26\\x3e\"");
|
||
|
assertPrint("/(?=x)/", "/(?=x)/");
|
||
|
}
|
||
|
|
||
|
public void testPrintArray() {
|
||
|
assertPrint("[void 0, void 0]", "[void 0,void 0]");
|
||
|
assertPrint("[undefined, undefined]", "[undefined,undefined]");
|
||
|
assertPrint("[ , , , undefined]", "[,,,undefined]");
|
||
|
assertPrint("[ , , , 0]", "[,,,0]");
|
||
|
}
|
||
|
|
||
|
public void testHook() {
|
||
|
assertPrint("a ? b = 1 : c = 2", "a?b=1:c=2");
|
||
|
assertPrint("x = a ? b = 1 : c = 2", "x=a?b=1:c=2");
|
||
|
assertPrint("(x = a) ? b = 1 : c = 2", "(x=a)?b=1:c=2");
|
||
|
|
||
|
assertPrint("x, a ? b = 1 : c = 2", "x,a?b=1:c=2");
|
||
|
assertPrint("x, (a ? b = 1 : c = 2)", "x,a?b=1:c=2");
|
||
|
assertPrint("(x, a) ? b = 1 : c = 2", "(x,a)?b=1:c=2");
|
||
|
|
||
|
assertPrint("a ? (x, b) : c = 2", "a?(x,b):c=2");
|
||
|
assertPrint("a ? b = 1 : (x,c)", "a?b=1:(x,c)");
|
||
|
|
||
|
assertPrint("a ? b = 1 : c = 2 + x", "a?b=1:c=2+x");
|
||
|
assertPrint("(a ? b = 1 : c = 2) + x", "(a?b=1:c=2)+x");
|
||
|
assertPrint("a ? b = 1 : (c = 2) + x", "a?b=1:(c=2)+x");
|
||
|
|
||
|
assertPrint("a ? (b?1:2) : 3", "a?b?1:2:3");
|
||
|
}
|
||
|
|
||
|
public void testPrintInOperatorInForLoop() {
|
||
|
// Check for in expression in for's init expression.
|
||
|
// Check alone, with + (higher precedence), with ?: (lower precedence),
|
||
|
// and with conditional.
|
||
|
assertPrint("var a={}; for (var i = (\"length\" in a); i;) {}",
|
||
|
"var a={};for(var i=(\"length\"in a);i;);");
|
||
|
assertPrint("var a={}; for (var i = (\"length\" in a) ? 0 : 1; i;) {}",
|
||
|
"var a={};for(var i=(\"length\"in a)?0:1;i;);");
|
||
|
assertPrint("var a={}; for (var i = (\"length\" in a) + 1; i;) {}",
|
||
|
"var a={};for(var i=(\"length\"in a)+1;i;);");
|
||
|
assertPrint("var a={};for (var i = (\"length\" in a|| \"size\" in a);;);",
|
||
|
"var a={};for(var i=(\"length\"in a)||(\"size\"in a);;);");
|
||
|
assertPrint("var a={};for (var i = a || a || (\"size\" in a);;);",
|
||
|
"var a={};for(var i=a||a||(\"size\"in a);;);");
|
||
|
|
||
|
// Test works with unary operators and calls.
|
||
|
assertPrint("var a={}; for (var i = -(\"length\" in a); i;) {}",
|
||
|
"var a={};for(var i=-(\"length\"in a);i;);");
|
||
|
assertPrint("var a={};function b_(p){ return p;};" +
|
||
|
"for(var i=1,j=b_(\"length\" in a);;) {}",
|
||
|
"var a={};function b_(p){return p}" +
|
||
|
"for(var i=1,j=b_(\"length\"in a);;);");
|
||
|
|
||
|
// Test we correctly handle an in operator in the test clause.
|
||
|
assertPrint("var a={}; for (;(\"length\" in a);) {}",
|
||
|
"var a={};for(;\"length\"in a;);");
|
||
|
}
|
||
|
|
||
|
public void testLiteralProperty() {
|
||
|
assertPrint("(64).toString()", "(64).toString()");
|
||
|
}
|
||
|
|
||
|
private void assertPrint(String js, String expected) {
|
||
|
parse(expected); // validate the expected string is valid JS
|
||
|
assertEquals(expected,
|
||
|
parsePrint(js, false, CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD));
|
||
|
}
|
||
|
|
||
|
private void assertPrintSame(String js) {
|
||
|
assertPrint(js, js);
|
||
|
}
|
||
|
|
||
|
// Make sure that the code generator doesn't associate an
|
||
|
// else clause with the wrong if clause.
|
||
|
public void testAmbiguousElseClauses() {
|
||
|
assertPrintNode("if(x)if(y);else;",
|
||
|
new Node(Token.IF,
|
||
|
Node.newString(Token.NAME, "x"),
|
||
|
new Node(Token.BLOCK,
|
||
|
new Node(Token.IF,
|
||
|
Node.newString(Token.NAME, "y"),
|
||
|
new Node(Token.BLOCK),
|
||
|
|
||
|
// ELSE clause for the inner if
|
||
|
new Node(Token.BLOCK)))));
|
||
|
|
||
|
assertPrintNode("if(x){if(y);}else;",
|
||
|
new Node(Token.IF,
|
||
|
Node.newString(Token.NAME, "x"),
|
||
|
new Node(Token.BLOCK,
|
||
|
new Node(Token.IF,
|
||
|
Node.newString(Token.NAME, "y"),
|
||
|
new Node(Token.BLOCK))),
|
||
|
|
||
|
// ELSE clause for the outer if
|
||
|
new Node(Token.BLOCK)));
|
||
|
|
||
|
assertPrintNode("if(x)if(y);else{if(z);}else;",
|
||
|
new Node(Token.IF,
|
||
|
Node.newString(Token.NAME, "x"),
|
||
|
new Node(Token.BLOCK,
|
||
|
new Node(Token.IF,
|
||
|
Node.newString(Token.NAME, "y"),
|
||
|
new Node(Token.BLOCK),
|
||
|
new Node(Token.BLOCK,
|
||
|
new Node(Token.IF,
|
||
|
Node.newString(Token.NAME, "z"),
|
||
|
new Node(Token.BLOCK))))),
|
||
|
|
||
|
// ELSE clause for the outermost if
|
||
|
new Node(Token.BLOCK)));
|
||
|
}
|
||
|
|
||
|
public void testLineBreak() {
|
||
|
// line break after function if in a statement context
|
||
|
assertLineBreak("function a() {}\n" +
|
||
|
"function b() {}",
|
||
|
"function a(){}\n" +
|
||
|
"function b(){}\n");
|
||
|
|
||
|
// line break after ; after a function
|
||
|
assertLineBreak("var a = {};\n" +
|
||
|
"a.foo = function () {}\n" +
|
||
|
"function b() {}",
|
||
|
"var a={};a.foo=function(){};\n" +
|
||
|
"function b(){}\n");
|
||
|
|
||
|
// break after comma after a function
|
||
|
assertLineBreak("var a = {\n" +
|
||
|
" b: function() {},\n" +
|
||
|
" c: function() {}\n" +
|
||
|
"};\n" +
|
||
|
"alert(a);",
|
||
|
|
||
|
"var a={b:function(){},\n" +
|
||
|
"c:function(){}};\n" +
|
||
|
"alert(a)");
|
||
|
}
|
||
|
|
||
|
private void assertLineBreak(String js, String expected) {
|
||
|
assertEquals(expected,
|
||
|
parsePrint(js, false, true,
|
||
|
CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD));
|
||
|
}
|
||
|
|
||
|
public void testPreferLineBreakAtEndOfFile() {
|
||
|
// short final line, no previous break, do nothing
|
||
|
assertLineBreakAtEndOfFile(
|
||
|
"\"1234567890\";",
|
||
|
"\"1234567890\"",
|
||
|
"\"1234567890\"");
|
||
|
|
||
|
// short final line, shift previous break to end
|
||
|
assertLineBreakAtEndOfFile(
|
||
|
"\"123456789012345678901234567890\";\"1234567890\"",
|
||
|
"\"123456789012345678901234567890\";\n\"1234567890\"",
|
||
|
"\"123456789012345678901234567890\"; \"1234567890\";\n");
|
||
|
assertLineBreakAtEndOfFile(
|
||
|
"var12345678901234567890123456 instanceof Object;",
|
||
|
"var12345678901234567890123456 instanceof\nObject",
|
||
|
"var12345678901234567890123456 instanceof Object;\n");
|
||
|
|
||
|
// long final line, no previous break, add a break at end
|
||
|
assertLineBreakAtEndOfFile(
|
||
|
"\"1234567890\";\"12345678901234567890\";",
|
||
|
"\"1234567890\";\"12345678901234567890\"",
|
||
|
"\"1234567890\";\"12345678901234567890\";\n");
|
||
|
|
||
|
// long final line, previous break, add a break at end
|
||
|
assertLineBreakAtEndOfFile(
|
||
|
"\"123456789012345678901234567890\";\"12345678901234567890\";",
|
||
|
"\"123456789012345678901234567890\";\n\"12345678901234567890\"",
|
||
|
"\"123456789012345678901234567890\";\n\"12345678901234567890\";\n");
|
||
|
}
|
||
|
|
||
|
private void assertLineBreakAtEndOfFile(String js,
|
||
|
String expectedWithoutBreakAtEnd, String expectedWithBreakAtEnd) {
|
||
|
assertEquals(expectedWithoutBreakAtEnd,
|
||
|
parsePrint(js, false, false, false, 30));
|
||
|
assertEquals(expectedWithBreakAtEnd,
|
||
|
parsePrint(js, false, false, true, 30));
|
||
|
}
|
||
|
|
||
|
public void testPrettyPrinter() {
|
||
|
// Ensure that the pretty printer inserts line breaks at appropriate
|
||
|
// places.
|
||
|
assertPrettyPrint("(function(){})();","(function() {\n})();\n");
|
||
|
assertPrettyPrint("var a = (function() {});alert(a);",
|
||
|
"var a = function() {\n};\nalert(a);\n");
|
||
|
|
||
|
// Check we correctly handle putting brackets around all if clauses so
|
||
|
// we can put breakpoints inside statements.
|
||
|
assertPrettyPrint("if (1) {}",
|
||
|
"if(1) {\n" +
|
||
|
"}\n");
|
||
|
assertPrettyPrint("if (1) {alert(\"\");}",
|
||
|
"if(1) {\n" +
|
||
|
" alert(\"\")\n" +
|
||
|
"}\n");
|
||
|
assertPrettyPrint("if (1)alert(\"\");",
|
||
|
"if(1) {\n" +
|
||
|
" alert(\"\")\n" +
|
||
|
"}\n");
|
||
|
assertPrettyPrint("if (1) {alert();alert();}",
|
||
|
"if(1) {\n" +
|
||
|
" alert();\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
// Don't add blocks if they weren't there already.
|
||
|
assertPrettyPrint("label: alert();",
|
||
|
"label:alert();\n");
|
||
|
|
||
|
// But if statements and loops get blocks automagically.
|
||
|
assertPrettyPrint("if (1) alert();",
|
||
|
"if(1) {\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
assertPrettyPrint("for (;;) alert();",
|
||
|
"for(;;) {\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
assertPrettyPrint("while (1) alert();",
|
||
|
"while(1) {\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
// Do we put else clauses in blocks?
|
||
|
assertPrettyPrint("if (1) {} else {alert(a);}",
|
||
|
"if(1) {\n" +
|
||
|
"}else {\n alert(a)\n}\n");
|
||
|
|
||
|
// Do we add blocks to else clauses?
|
||
|
assertPrettyPrint("if (1) alert(a); else alert(b);",
|
||
|
"if(1) {\n" +
|
||
|
" alert(a)\n" +
|
||
|
"}else {\n" +
|
||
|
" alert(b)\n" +
|
||
|
"}\n");
|
||
|
|
||
|
// Do we put for bodies in blocks?
|
||
|
assertPrettyPrint("for(;;) { alert();}",
|
||
|
"for(;;) {\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
assertPrettyPrint("for(;;) {}",
|
||
|
"for(;;) {\n" +
|
||
|
"}\n");
|
||
|
assertPrettyPrint("for(;;) { alert(); alert(); }",
|
||
|
"for(;;) {\n" +
|
||
|
" alert();\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
// How about do loops?
|
||
|
assertPrettyPrint("do { alert(); } while(true);",
|
||
|
"do {\n" +
|
||
|
" alert()\n" +
|
||
|
"}while(true);\n");
|
||
|
|
||
|
// label?
|
||
|
assertPrettyPrint("myLabel: { alert();}",
|
||
|
"myLabel: {\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
// Don't move the label on a loop, because then break {label} and
|
||
|
// continue {label} won't work.
|
||
|
assertPrettyPrint("myLabel: for(;;) continue myLabel;",
|
||
|
"myLabel:for(;;) {\n" +
|
||
|
" continue myLabel\n" +
|
||
|
"}\n");
|
||
|
|
||
|
assertPrettyPrint("var a;", "var a;\n");
|
||
|
}
|
||
|
|
||
|
public void testPrettyPrinter2() {
|
||
|
assertPrettyPrint(
|
||
|
"if(true) f();",
|
||
|
"if(true) {\n" +
|
||
|
" f()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
assertPrettyPrint(
|
||
|
"if (true) { f() } else { g() }",
|
||
|
"if(true) {\n" +
|
||
|
" f()\n" +
|
||
|
"}else {\n" +
|
||
|
" g()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
assertPrettyPrint(
|
||
|
"if(true) f(); for(;;) g();",
|
||
|
"if(true) {\n" +
|
||
|
" f()\n" +
|
||
|
"}\n" +
|
||
|
"for(;;) {\n" +
|
||
|
" g()\n" +
|
||
|
"}\n");
|
||
|
}
|
||
|
|
||
|
public void testPrettyPrinter3() {
|
||
|
assertPrettyPrint(
|
||
|
"try {} catch(e) {}if (1) {alert();alert();}",
|
||
|
"try {\n" +
|
||
|
"}catch(e) {\n" +
|
||
|
"}\n" +
|
||
|
"if(1) {\n" +
|
||
|
" alert();\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
assertPrettyPrint(
|
||
|
"try {} finally {}if (1) {alert();alert();}",
|
||
|
"try {\n" +
|
||
|
"}finally {\n" +
|
||
|
"}\n" +
|
||
|
"if(1) {\n" +
|
||
|
" alert();\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
assertPrettyPrint(
|
||
|
"try {} catch(e) {} finally {} if (1) {alert();alert();}",
|
||
|
"try {\n" +
|
||
|
"}catch(e) {\n" +
|
||
|
"}finally {\n" +
|
||
|
"}\n" +
|
||
|
"if(1) {\n" +
|
||
|
" alert();\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
}
|
||
|
|
||
|
public void testPrettyPrinter4() {
|
||
|
assertPrettyPrint(
|
||
|
"function f() {}if (1) {alert();}",
|
||
|
"function f() {\n" +
|
||
|
"}\n" +
|
||
|
"if(1) {\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
assertPrettyPrint(
|
||
|
"var f = function() {};if (1) {alert();}",
|
||
|
"var f = function() {\n" +
|
||
|
"};\n" +
|
||
|
"if(1) {\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
assertPrettyPrint(
|
||
|
"(function() {})();if (1) {alert();}",
|
||
|
"(function() {\n" +
|
||
|
"})();\n" +
|
||
|
"if(1) {\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
|
||
|
assertPrettyPrint(
|
||
|
"(function() {alert();alert();})();if (1) {alert();}",
|
||
|
"(function() {\n" +
|
||
|
" alert();\n" +
|
||
|
" alert()\n" +
|
||
|
"})();\n" +
|
||
|
"if(1) {\n" +
|
||
|
" alert()\n" +
|
||
|
"}\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotations() {
|
||
|
assertTypeAnnotations(
|
||
|
"/** @constructor */ function Foo(){}",
|
||
|
"/**\n * @return {undefined}\n * @constructor\n */\n"
|
||
|
+ "function Foo() {\n}\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsTypeDef() {
|
||
|
// TODO(johnlenz): It would be nice if there were some way to preserve
|
||
|
// typedefs but currently they are resolved into the basic types in the
|
||
|
// type registry.
|
||
|
assertTypeAnnotations(
|
||
|
"/** @typedef {Array.<number>} */ goog.java.Long;\n"
|
||
|
+ "/** @param {!goog.java.Long} a*/\n"
|
||
|
+ "function f(a){};\n",
|
||
|
"goog.java.Long;\n"
|
||
|
+ "/**\n"
|
||
|
+ " * @param {(Array.<number>|null)} a\n"
|
||
|
+ " * @return {undefined}\n"
|
||
|
+ " */\n"
|
||
|
+ "function f(a) {\n}\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsAssign() {
|
||
|
assertTypeAnnotations("/** @constructor */ var Foo = function(){}",
|
||
|
"/**\n * @return {undefined}\n * @constructor\n */\n"
|
||
|
+ "var Foo = function() {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsNamespace() {
|
||
|
assertTypeAnnotations("var a = {};"
|
||
|
+ "/** @constructor */ a.Foo = function(){}",
|
||
|
"var a = {};\n"
|
||
|
+ "/**\n * @return {undefined}\n * @constructor\n */\n"
|
||
|
+ "a.Foo = function() {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsMemberSubclass() {
|
||
|
assertTypeAnnotations("var a = {};"
|
||
|
+ "/** @constructor */ a.Foo = function(){};"
|
||
|
+ "/** @constructor \n @extends {a.Foo} */ a.Bar = function(){}",
|
||
|
"var a = {};\n"
|
||
|
+ "/**\n * @return {undefined}\n * @constructor\n */\n"
|
||
|
+ "a.Foo = function() {\n};\n"
|
||
|
+ "/**\n * @return {undefined}\n * @extends {a.Foo}\n"
|
||
|
+ " * @constructor\n */\n"
|
||
|
+ "a.Bar = function() {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsInterface() {
|
||
|
assertTypeAnnotations("var a = {};"
|
||
|
+ "/** @interface */ a.Foo = function(){};"
|
||
|
+ "/** @interface \n @extends {a.Foo} */ a.Bar = function(){}",
|
||
|
"var a = {};\n"
|
||
|
+ "/**\n * @interface\n */\n"
|
||
|
+ "a.Foo = function() {\n};\n"
|
||
|
+ "/**\n * @extends {a.Foo}\n"
|
||
|
+ " * @interface\n */\n"
|
||
|
+ "a.Bar = function() {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsMultipleInterface() {
|
||
|
assertTypeAnnotations("var a = {};"
|
||
|
+ "/** @interface */ a.Foo1 = function(){};"
|
||
|
+ "/** @interface */ a.Foo2 = function(){};"
|
||
|
+ "/** @interface \n @extends {a.Foo1} \n @extends {a.Foo2} */"
|
||
|
+ "a.Bar = function(){}",
|
||
|
"var a = {};\n"
|
||
|
+ "/**\n * @interface\n */\n"
|
||
|
+ "a.Foo1 = function() {\n};\n"
|
||
|
+ "/**\n * @interface\n */\n"
|
||
|
+ "a.Foo2 = function() {\n};\n"
|
||
|
+ "/**\n * @extends {a.Foo1}\n"
|
||
|
+ " * @extends {a.Foo2}\n"
|
||
|
+ " * @interface\n */\n"
|
||
|
+ "a.Bar = function() {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsMember() {
|
||
|
assertTypeAnnotations("var a = {};"
|
||
|
+ "/** @constructor */ a.Foo = function(){}"
|
||
|
+ "/** @param {string} foo\n"
|
||
|
+ " * @return {number} */\n"
|
||
|
+ "a.Foo.prototype.foo = function(foo) { return 3; };"
|
||
|
+ "/** @type {string|undefined} */"
|
||
|
+ "a.Foo.prototype.bar = '';",
|
||
|
"var a = {};\n"
|
||
|
+ "/**\n * @return {undefined}\n * @constructor\n */\n"
|
||
|
+ "a.Foo = function() {\n};\n"
|
||
|
+ "/**\n"
|
||
|
+ " * @param {string} foo\n"
|
||
|
+ " * @return {number}\n"
|
||
|
+ " */\n"
|
||
|
+ "a.Foo.prototype.foo = function(foo) {\n return 3\n};\n"
|
||
|
+ "/** @type {string} */\n"
|
||
|
+ "a.Foo.prototype.bar = \"\";\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsImplements() {
|
||
|
assertTypeAnnotations("var a = {};"
|
||
|
+ "/** @constructor */ a.Foo = function(){};\n"
|
||
|
+ "/** @interface */ a.I = function(){};\n"
|
||
|
+ "/** @interface */ a.I2 = function(){};\n"
|
||
|
+ "/** @constructor \n @extends {a.Foo}\n"
|
||
|
+ " * @implements {a.I} \n @implements {a.I2}\n"
|
||
|
+ "*/ a.Bar = function(){}",
|
||
|
"var a = {};\n"
|
||
|
+ "/**\n * @return {undefined}\n * @constructor\n */\n"
|
||
|
+ "a.Foo = function() {\n};\n"
|
||
|
+ "/**\n * @interface\n */\n"
|
||
|
+ "a.I = function() {\n};\n"
|
||
|
+ "/**\n * @interface\n */\n"
|
||
|
+ "a.I2 = function() {\n};\n"
|
||
|
+ "/**\n * @return {undefined}\n * @extends {a.Foo}\n"
|
||
|
+ " * @implements {a.I}\n"
|
||
|
+ " * @implements {a.I2}\n * @constructor\n */\n"
|
||
|
+ "a.Bar = function() {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsDispatcher1() {
|
||
|
assertTypeAnnotations(
|
||
|
"var a = {};\n" +
|
||
|
"/** \n" +
|
||
|
" * @constructor \n" +
|
||
|
" * @javadispatch \n" +
|
||
|
" */\n" +
|
||
|
"a.Foo = function(){}",
|
||
|
"var a = {};\n" +
|
||
|
"/**\n" +
|
||
|
" * @return {undefined}\n" +
|
||
|
" * @constructor\n" +
|
||
|
" * @javadispatch\n" +
|
||
|
" */\n" +
|
||
|
"a.Foo = function() {\n" +
|
||
|
"};\n");
|
||
|
}
|
||
|
|
||
|
public void testTypeAnnotationsDispatcher2() {
|
||
|
assertTypeAnnotations(
|
||
|
"var a = {};\n" +
|
||
|
"/** \n" +
|
||
|
" * @constructor \n" +
|
||
|
" */\n" +
|
||
|
"a.Foo = function(){}\n" +
|
||
|
"/**\n" +
|
||
|
" * @javadispatch\n" +
|
||
|
" */\n" +
|
||
|
"a.Foo.prototype.foo = function() {};",
|
||
|
|
||
|
"var a = {};\n" +
|
||
|
"/**\n" +
|
||
|
" * @return {undefined}\n" +
|
||
|
" * @constructor\n" +
|
||
|
" */\n" +
|
||
|
"a.Foo = function() {\n" +
|
||
|
"};\n" +
|
||
|
"/**\n" +
|
||
|
" * @return {undefined}\n" +
|
||
|
" * @javadispatch\n" +
|
||
|
" */\n" +
|
||
|
"a.Foo.prototype.foo = function() {\n" +
|
||
|
"};\n");
|
||
|
}
|
||
|
|
||
|
public void testU2UFunctionTypeAnnotation() {
|
||
|
assertTypeAnnotations(
|
||
|
"/** @type {!Function} */ var x = function() {}",
|
||
|
"/**\n * @constructor\n */\nvar x = function() {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testEmitUnknownParamTypesAsAllType() {
|
||
|
assertTypeAnnotations(
|
||
|
"var a = function(x) {}",
|
||
|
"/**\n" +
|
||
|
" * @param {*} x\n" +
|
||
|
" * @return {undefined}\n" +
|
||
|
" */\n" +
|
||
|
"var a = function(x) {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testOptionalTypesAnnotation() {
|
||
|
assertTypeAnnotations(
|
||
|
"/**\n" +
|
||
|
" * @param {string=} x \n" +
|
||
|
" */\n" +
|
||
|
"var a = function(x) {}",
|
||
|
"/**\n" +
|
||
|
" * @param {string=} x\n" +
|
||
|
" * @return {undefined}\n" +
|
||
|
" */\n" +
|
||
|
"var a = function(x) {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testVariableArgumentsTypesAnnotation() {
|
||
|
assertTypeAnnotations(
|
||
|
"/**\n" +
|
||
|
" * @param {...string} x \n" +
|
||
|
" */\n" +
|
||
|
"var a = function(x) {}",
|
||
|
"/**\n" +
|
||
|
" * @param {...string} x\n" +
|
||
|
" * @return {undefined}\n" +
|
||
|
" */\n" +
|
||
|
"var a = function(x) {\n};\n");
|
||
|
}
|
||
|
|
||
|
public void testTempConstructor() {
|
||
|
assertTypeAnnotations(
|
||
|
"var x = function() {\n/**\n * @constructor\n */\nfunction t1() {}\n" +
|
||
|
" /**\n * @constructor\n */\nfunction t2() {}\n" +
|
||
|
" t1.prototype = t2.prototype}",
|
||
|
"/**\n * @return {undefined}\n */\nvar x = function() {\n" +
|
||
|
" /**\n * @return {undefined}\n * @constructor\n */\n" +
|
||
|
"function t1() {\n }\n" +
|
||
|
" /**\n * @return {undefined}\n * @constructor\n */\n" +
|
||
|
"function t2() {\n }\n" +
|
||
|
" t1.prototype = t2.prototype\n};\n"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
public void testEnumAnnotation1() {
|
||
|
assertTypeAnnotations(
|
||
|
"/** @enum {string} */ var Enum = {FOO: 'x', BAR: 'y'};",
|
||
|
"/** @enum {string} */\nvar Enum = {FOO:\"x\", BAR:\"y\"};\n");
|
||
|
}
|
||
|
|
||
|
public void testEnumAnnotation2() {
|
||
|
assertTypeAnnotations(
|
||
|
"var goog = goog || {};" +
|
||
|
"/** @enum {string} */ goog.Enum = {FOO: 'x', BAR: 'y'};" +
|
||
|
"/** @const */ goog.Enum2 = goog.x ? {} : goog.Enum;",
|
||
|
"var goog = goog || {};\n" +
|
||
|
"/** @enum {string} */\ngoog.Enum = {FOO:\"x\", BAR:\"y\"};\n" +
|
||
|
"/** @type {(Object|{})} */\ngoog.Enum2 = goog.x ? {} : goog.Enum;\n");
|
||
|
}
|
||
|
|
||
|
private void assertPrettyPrint(String js, String expected) {
|
||
|
assertEquals(expected,
|
||
|
parsePrint(js, true, false,
|
||
|
CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD));
|
||
|
}
|
||
|
|
||
|
private void assertTypeAnnotations(String js, String expected) {
|
||
|
assertEquals(expected,
|
||
|
parsePrint(js, true, false,
|
||
|
CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD, true));
|
||
|
}
|
||
|
|
||
|
public void testSubtraction() {
|
||
|
Compiler compiler = new Compiler();
|
||
|
Node n = compiler.parseTestCode("x - -4");
|
||
|
assertEquals(0, compiler.getErrorCount());
|
||
|
|
||
|
assertEquals(
|
||
|
"x- -4",
|
||
|
printNode(n));
|
||
|
}
|
||
|
|
||
|
public void testFunctionWithCall() {
|
||
|
assertPrint(
|
||
|
"var user = new function() {"
|
||
|
+ "alert(\"foo\")}",
|
||
|
"var user=new function(){"
|
||
|
+ "alert(\"foo\")}");
|
||
|
assertPrint(
|
||
|
"var user = new function() {"
|
||
|
+ "this.name = \"foo\";"
|
||
|
+ "this.local = function(){alert(this.name)};}",
|
||
|
"var user=new function(){"
|
||
|
+ "this.name=\"foo\";"
|
||
|
+ "this.local=function(){alert(this.name)}}");
|
||
|
}
|
||
|
|
||
|
public void testLineLength() {
|
||
|
// list
|
||
|
assertLineLength("var aba,bcb,cdc",
|
||
|
"var aba,bcb," +
|
||
|
"\ncdc");
|
||
|
|
||
|
// operators, and two breaks
|
||
|
assertLineLength(
|
||
|
"\"foo\"+\"bar,baz,bomb\"+\"whee\"+\";long-string\"\n+\"aaa\"",
|
||
|
"\"foo\"+\"bar,baz,bomb\"+" +
|
||
|
"\n\"whee\"+\";long-string\"+" +
|
||
|
"\n\"aaa\"");
|
||
|
|
||
|
// assignment
|
||
|
assertLineLength("var abazaba=1234",
|
||
|
"var abazaba=" +
|
||
|
"\n1234");
|
||
|
|
||
|
// statements
|
||
|
assertLineLength("var abab=1;var bab=2",
|
||
|
"var abab=1;" +
|
||
|
"\nvar bab=2");
|
||
|
|
||
|
// don't break regexes
|
||
|
assertLineLength("var a=/some[reg](ex),with.*we?rd|chars/i;var b=a",
|
||
|
"var a=/some[reg](ex),with.*we?rd|chars/i;" +
|
||
|
"\nvar b=a");
|
||
|
|
||
|
// don't break strings
|
||
|
assertLineLength("var a=\"foo,{bar};baz\";var b=a",
|
||
|
"var a=\"foo,{bar};baz\";" +
|
||
|
"\nvar b=a");
|
||
|
|
||
|
// don't break before post inc/dec
|
||
|
assertLineLength("var a=\"a\";a++;var b=\"bbb\";",
|
||
|
"var a=\"a\";a++;\n" +
|
||
|
"var b=\"bbb\"");
|
||
|
}
|
||
|
|
||
|
private void assertLineLength(String js, String expected) {
|
||
|
assertEquals(expected,
|
||
|
parsePrint(js, false, true, 10));
|
||
|
}
|
||
|
|
||
|
public void testParsePrintParse() {
|
||
|
testReparse("3;");
|
||
|
testReparse("var a = b;");
|
||
|
testReparse("var x, y, z;");
|
||
|
testReparse("try { foo() } catch(e) { bar() }");
|
||
|
testReparse("try { foo() } catch(e) { bar() } finally { stuff() }");
|
||
|
testReparse("try { foo() } finally { stuff() }");
|
||
|
testReparse("throw 'me'");
|
||
|
testReparse("function foo(a) { return a + 4; }");
|
||
|
testReparse("function foo() { return; }");
|
||
|
testReparse("var a = function(a, b) { foo(); return a + b; }");
|
||
|
testReparse("b = [3, 4, 'paul', \"Buchhe it\",,5];");
|
||
|
testReparse("v = (5, 6, 7, 8)");
|
||
|
testReparse("d = 34.0; x = 0; y = .3; z = -22");
|
||
|
testReparse("d = -x; t = !x + ~y;");
|
||
|
testReparse("'hi'; /* just a test */ stuff(a,b) \n" +
|
||
|
" foo(); // and another \n" +
|
||
|
" bar();");
|
||
|
testReparse("a = b++ + ++c; a = b++-++c; a = - --b; a = - ++b;");
|
||
|
testReparse("a++; b= a++; b = ++a; b = a--; b = --a; a+=2; b-=5");
|
||
|
testReparse("a = (2 + 3) * 4;");
|
||
|
testReparse("a = 1 + (2 + 3) + 4;");
|
||
|
testReparse("x = a ? b : c; x = a ? (b,3,5) : (foo(),bar());");
|
||
|
testReparse("a = b | c || d ^ e " +
|
||
|
"&& f & !g != h << i <= j < k >>> l > m * n % !o");
|
||
|
testReparse("a == b; a != b; a === b; a == b == a;" +
|
||
|
" (a == b) == a; a == (b == a);");
|
||
|
testReparse("if (a > b) a = b; if (b < 3) a = 3; else c = 4;");
|
||
|
testReparse("if (a == b) { a++; } if (a == 0) { a++; } else { a --; }");
|
||
|
testReparse("for (var i in a) b += i;");
|
||
|
testReparse("for (var i = 0; i < 10; i++){ b /= 2;" +
|
||
|
" if (b == 2)break;else continue;}");
|
||
|
testReparse("for (x = 0; x < 10; x++) a /= 2;");
|
||
|
testReparse("for (;;) a++;");
|
||
|
testReparse("while(true) { blah(); }while(true) blah();");
|
||
|
testReparse("do stuff(); while(a>b);");
|
||
|
testReparse("[0, null, , true, false, this];");
|
||
|
testReparse("s.replace(/absc/, 'X').replace(/ab/gi, 'Y');");
|
||
|
testReparse("new Foo; new Bar(a, b,c);");
|
||
|
testReparse("with(foo()) { x = z; y = t; } with(bar()) a = z;");
|
||
|
testReparse("delete foo['bar']; delete foo;");
|
||
|
testReparse("var x = { 'a':'paul', 1:'3', 2:(3,4) };");
|
||
|
testReparse("switch(a) { case 2: case 3: stuff(); break;" +
|
||
|
"case 4: morestuff(); break; default: done();}");
|
||
|
testReparse("x = foo['bar'] + foo['my stuff'] + foo[bar] + f.stuff;");
|
||
|
testReparse("a.v = b.v; x['foo'] = y['zoo'];");
|
||
|
testReparse("'test' in x; 3 in x; a in x;");
|
||
|
testReparse("'foo\"bar' + \"foo'c\" + 'stuff\\n and \\\\more'");
|
||
|
testReparse("x.__proto__;");
|
||
|
}
|
||
|
|
||
|
private void testReparse(String code) {
|
||
|
Compiler compiler = new Compiler();
|
||
|
Node parse1 = parse(code);
|
||
|
Node parse2 = parse(new CodePrinter.Builder(parse1).build());
|
||
|
String explanation = parse1.checkTreeEquals(parse2);
|
||
|
assertNull("\nExpected: " + compiler.toSource(parse1) +
|
||
|
"\nResult: " + compiler.toSource(parse2) +
|
||
|
"\n" + explanation, explanation);
|
||
|
}
|
||
|
|
||
|
public void testDoLoopIECompatiblity() {
|
||
|
// Do loops within IFs cause syntax errors in IE6 and IE7.
|
||
|
assertPrint("function f(){if(e1){do foo();while(e2)}else foo()}",
|
||
|
"function f(){if(e1){do foo();while(e2)}else foo()}");
|
||
|
|
||
|
assertPrint("function f(){if(e1)do foo();while(e2)else foo()}",
|
||
|
"function f(){if(e1){do foo();while(e2)}else foo()}");
|
||
|
|
||
|
assertPrint("if(x){do{foo()}while(y)}else bar()",
|
||
|
"if(x){do foo();while(y)}else bar()");
|
||
|
|
||
|
assertPrint("if(x)do{foo()}while(y);else bar()",
|
||
|
"if(x){do foo();while(y)}else bar()");
|
||
|
|
||
|
assertPrint("if(x){do{foo()}while(y)}",
|
||
|
"if(x){do foo();while(y)}");
|
||
|
|
||
|
assertPrint("if(x)do{foo()}while(y);",
|
||
|
"if(x){do foo();while(y)}");
|
||
|
|
||
|
assertPrint("if(x)A:do{foo()}while(y);",
|
||
|
"if(x){A:do foo();while(y)}");
|
||
|
|
||
|
assertPrint("var i = 0;a: do{b: do{i++;break b;} while(0);} while(0);",
|
||
|
"var i=0;a:do{b:do{i++;break b}while(0)}while(0)");
|
||
|
}
|
||
|
|
||
|
public void testFunctionSafariCompatiblity() {
|
||
|
// Functions within IFs cause syntax errors on Safari.
|
||
|
assertPrint("function f(){if(e1){function goo(){return true}}else foo()}",
|
||
|
"function f(){if(e1){function goo(){return true}}else foo()}");
|
||
|
|
||
|
assertPrint("function f(){if(e1)function goo(){return true}else foo()}",
|
||
|
"function f(){if(e1){function goo(){return true}}else foo()}");
|
||
|
|
||
|
assertPrint("if(e1){function goo(){return true}}",
|
||
|
"if(e1){function goo(){return true}}");
|
||
|
|
||
|
assertPrint("if(e1)function goo(){return true}",
|
||
|
"if(e1){function goo(){return true}}");
|
||
|
|
||
|
assertPrint("if(e1)A:function goo(){return true}",
|
||
|
"if(e1){A:function goo(){return true}}");
|
||
|
}
|
||
|
|
||
|
public void testExponents() {
|
||
|
assertPrintNumber("1", 1);
|
||
|
assertPrintNumber("10", 10);
|
||
|
assertPrintNumber("100", 100);
|
||
|
assertPrintNumber("1E3", 1000);
|
||
|
assertPrintNumber("1E4", 10000);
|
||
|
assertPrintNumber("1E5", 100000);
|
||
|
assertPrintNumber("-1", -1);
|
||
|
assertPrintNumber("-10", -10);
|
||
|
assertPrintNumber("-100", -100);
|
||
|
assertPrintNumber("-1E3", -1000);
|
||
|
assertPrintNumber("-12341234E4", -123412340000L);
|
||
|
assertPrintNumber("1E18", 1000000000000000000L);
|
||
|
assertPrintNumber("1E5", 100000.0);
|
||
|
assertPrintNumber("100000.1", 100000.1);
|
||
|
|
||
|
assertPrintNumber("1E-6", 0.000001);
|
||
|
assertPrintNumber("-0x38d7ea4c68001", -0x38d7ea4c68001L);
|
||
|
assertPrintNumber("0x38d7ea4c68001", 0x38d7ea4c68001L);
|
||
|
}
|
||
|
|
||
|
// Make sure to test as both a String and a Node, because
|
||
|
// negative numbers do not parse consistently from strings.
|
||
|
private void assertPrintNumber(String expected, double number) {
|
||
|
assertPrint(String.valueOf(number), expected);
|
||
|
assertPrintNode(expected, Node.newNumber(number));
|
||
|
}
|
||
|
|
||
|
private void assertPrintNumber(String expected, int number) {
|
||
|
assertPrint(String.valueOf(number), expected);
|
||
|
assertPrintNode(expected, Node.newNumber(number));
|
||
|
}
|
||
|
|
||
|
public void testDirectEval() {
|
||
|
assertPrint("eval('1');", "eval(\"1\")");
|
||
|
}
|
||
|
|
||
|
public void testIndirectEval() {
|
||
|
Node n = parse("eval('1');");
|
||
|
assertPrintNode("eval(\"1\")", n);
|
||
|
n.getFirstChild().getFirstChild().getFirstChild().putBooleanProp(
|
||
|
Node.DIRECT_EVAL, false);
|
||
|
assertPrintNode("(0,eval)(\"1\")", n);
|
||
|
}
|
||
|
|
||
|
public void testFreeCall1() {
|
||
|
assertPrint("foo(a);", "foo(a)");
|
||
|
assertPrint("x.foo(a);", "x.foo(a)");
|
||
|
}
|
||
|
|
||
|
public void testFreeCall2() {
|
||
|
Node n = parse("foo(a);");
|
||
|
assertPrintNode("foo(a)", n);
|
||
|
Node call = n.getFirstChild().getFirstChild();
|
||
|
assertTrue(call.isCall());
|
||
|
call.putBooleanProp(Node.FREE_CALL, true);
|
||
|
assertPrintNode("foo(a)", n);
|
||
|
}
|
||
|
|
||
|
public void testFreeCall3() {
|
||
|
Node n = parse("x.foo(a);");
|
||
|
assertPrintNode("x.foo(a)", n);
|
||
|
Node call = n.getFirstChild().getFirstChild();
|
||
|
assertTrue(call.isCall());
|
||
|
call.putBooleanProp(Node.FREE_CALL, true);
|
||
|
assertPrintNode("(0,x.foo)(a)", n);
|
||
|
}
|
||
|
|
||
|
public void testPrintScript() {
|
||
|
// Verify that SCRIPT nodes not marked as synthetic are printed as
|
||
|
// blocks.
|
||
|
Node ast = new Node(Token.SCRIPT,
|
||
|
new Node(Token.EXPR_RESULT, Node.newString("f")),
|
||
|
new Node(Token.EXPR_RESULT, Node.newString("g")));
|
||
|
String result = new CodePrinter.Builder(ast).setPrettyPrint(true).build();
|
||
|
assertEquals("\"f\";\n\"g\";\n", result);
|
||
|
}
|
||
|
|
||
|
public void testObjectLit() {
|
||
|
assertPrint("({x:1})", "({x:1})");
|
||
|
assertPrint("var x=({x:1})", "var x={x:1}");
|
||
|
assertPrint("var x={'x':1}", "var x={\"x\":1}");
|
||
|
assertPrint("var x={1:1}", "var x={1:1}");
|
||
|
assertPrint("({},42)+0", "({},42)+0");
|
||
|
}
|
||
|
|
||
|
public void testObjectLit2() {
|
||
|
assertPrint("var x={1:1}", "var x={1:1}");
|
||
|
assertPrint("var x={'1':1}", "var x={1:1}");
|
||
|
assertPrint("var x={'1.0':1}", "var x={\"1.0\":1}");
|
||
|
assertPrint("var x={1.5:1}", "var x={\"1.5\":1}");
|
||
|
|
||
|
}
|
||
|
|
||
|
public void testObjectLit3() {
|
||
|
assertPrint("var x={3E9:1}",
|
||
|
"var x={3E9:1}");
|
||
|
assertPrint("var x={'3000000000':1}", // More than 31 bits
|
||
|
"var x={3E9:1}");
|
||
|
assertPrint("var x={'3000000001':1}",
|
||
|
"var x={3000000001:1}");
|
||
|
assertPrint("var x={'6000000001':1}", // More than 32 bits
|
||
|
"var x={6000000001:1}");
|
||
|
assertPrint("var x={\"12345678901234567\":1}", // More than 53 bits
|
||
|
"var x={\"12345678901234567\":1}");
|
||
|
}
|
||
|
|
||
|
public void testObjectLit4() {
|
||
|
// More than 128 bits.
|
||
|
assertPrint(
|
||
|
"var x={\"123456789012345671234567890123456712345678901234567\":1}",
|
||
|
"var x={\"123456789012345671234567890123456712345678901234567\":1}");
|
||
|
}
|
||
|
|
||
|
public void testGetter() {
|
||
|
assertPrint("var x = {}", "var x={}");
|
||
|
assertPrint("var x = {get a() {return 1}}", "var x={get a(){return 1}}");
|
||
|
assertPrint(
|
||
|
"var x = {get a() {}, get b(){}}",
|
||
|
"var x={get a(){},get b(){}}");
|
||
|
|
||
|
assertPrint(
|
||
|
"var x = {get 'a'() {return 1}}",
|
||
|
"var x={get \"a\"(){return 1}}");
|
||
|
|
||
|
assertPrint(
|
||
|
"var x = {get 1() {return 1}}",
|
||
|
"var x={get 1(){return 1}}");
|
||
|
|
||
|
assertPrint(
|
||
|
"var x = {get \"()\"() {return 1}}",
|
||
|
"var x={get \"()\"(){return 1}}");
|
||
|
}
|
||
|
|
||
|
public void testSetter() {
|
||
|
assertPrint("var x = {}", "var x={}");
|
||
|
assertPrint(
|
||
|
"var x = {set a(y) {return 1}}",
|
||
|
"var x={set a(y){return 1}}");
|
||
|
|
||
|
assertPrint(
|
||
|
"var x = {get 'a'() {return 1}}",
|
||
|
"var x={get \"a\"(){return 1}}");
|
||
|
|
||
|
assertPrint(
|
||
|
"var x = {set 1(y) {return 1}}",
|
||
|
"var x={set 1(y){return 1}}");
|
||
|
|
||
|
assertPrint(
|
||
|
"var x = {set \"(x)\"(y) {return 1}}",
|
||
|
"var x={set \"(x)\"(y){return 1}}");
|
||
|
}
|
||
|
|
||
|
public void testNegCollapse() {
|
||
|
// Collapse the negative symbol on numbers at generation time,
|
||
|
// to match the Rhino behavior.
|
||
|
assertPrint("var x = - - 2;", "var x=2");
|
||
|
assertPrint("var x = - (2);", "var x=-2");
|
||
|
}
|
||
|
|
||
|
public void testStrict() {
|
||
|
String result = parsePrint("var x", false, false, 0, false, true);
|
||
|
assertEquals("'use strict';var x", result);
|
||
|
}
|
||
|
|
||
|
public void testArrayLiteral() {
|
||
|
assertPrint("var x = [,];","var x=[,]");
|
||
|
assertPrint("var x = [,,];","var x=[,,]");
|
||
|
assertPrint("var x = [,s,,];","var x=[,s,,]");
|
||
|
assertPrint("var x = [,s];","var x=[,s]");
|
||
|
assertPrint("var x = [s,];","var x=[s]");
|
||
|
}
|
||
|
|
||
|
public void testZero() {
|
||
|
assertPrint("var x ='\\0';", "var x=\"\\x00\"");
|
||
|
assertPrint("var x ='\\x00';", "var x=\"\\x00\"");
|
||
|
assertPrint("var x ='\\u0000';", "var x=\"\\x00\"");
|
||
|
assertPrint("var x ='\\u00003';", "var x=\"\\x003\"");
|
||
|
}
|
||
|
|
||
|
public void testUnicode() {
|
||
|
assertPrint("var x ='\\x0f';", "var x=\"\\u000f\"");
|
||
|
assertPrint("var x ='\\x68';", "var x=\"h\"");
|
||
|
assertPrint("var x ='\\x7f';", "var x=\"\\u007f\"");
|
||
|
}
|
||
|
|
||
|
public void testUnicodeKeyword() {
|
||
|
// keyword "if"
|
||
|
assertPrint("var \\u0069\\u0066 = 1;", "var i\\u0066=1");
|
||
|
// keyword "var"
|
||
|
assertPrint("var v\\u0061\\u0072 = 1;", "var va\\u0072=1");
|
||
|
// all are keyword "while"
|
||
|
assertPrint("var w\\u0068\\u0069\\u006C\\u0065 = 1;"
|
||
|
+ "\\u0077\\u0068il\\u0065 = 2;"
|
||
|
+ "\\u0077h\\u0069le = 3;",
|
||
|
"var whil\\u0065=1;whil\\u0065=2;whil\\u0065=3");
|
||
|
}
|
||
|
|
||
|
public void testNumericKeys() {
|
||
|
assertPrint("var x = {010: 1};", "var x={8:1}");
|
||
|
assertPrint("var x = {'010': 1};", "var x={\"010\":1}");
|
||
|
|
||
|
assertPrint("var x = {0x10: 1};", "var x={16:1}");
|
||
|
assertPrint("var x = {'0x10': 1};", "var x={\"0x10\":1}");
|
||
|
|
||
|
// I was surprised at this result too.
|
||
|
assertPrint("var x = {.2: 1};", "var x={\"0.2\":1}");
|
||
|
assertPrint("var x = {'.2': 1};", "var x={\".2\":1}");
|
||
|
|
||
|
assertPrint("var x = {0.2: 1};", "var x={\"0.2\":1}");
|
||
|
assertPrint("var x = {'0.2': 1};", "var x={\"0.2\":1}");
|
||
|
}
|
||
|
|
||
|
public void testIssue582() {
|
||
|
assertPrint("var x = -0.0;", "var x=-0");
|
||
|
}
|
||
|
|
||
|
public void testIssue601() {
|
||
|
assertPrint("'\\v' == 'v'", "\"\\v\"==\"v\"");
|
||
|
assertPrint("'\\u000B' == '\\v'", "\"\\x0B\"==\"\\v\"");
|
||
|
assertPrint("'\\x0B' == '\\v'", "\"\\x0B\"==\"\\v\"");
|
||
|
}
|
||
|
|
||
|
public void testIssue620() {
|
||
|
assertPrint("alert(/ / / / /);", "alert(/ // / /)");
|
||
|
assertPrint("alert(/ // / /);", "alert(/ // / /)");
|
||
|
}
|
||
|
|
||
|
public void testIssue5746867() {
|
||
|
assertPrint("var a = { '$\\\\' : 5 };", "var a={\"$\\\\\":5}");
|
||
|
}
|
||
|
|
||
|
public void testCommaSpacing() {
|
||
|
assertPrint("var a = (b = 5, c = 5);",
|
||
|
"var a=(b=5,c=5)");
|
||
|
assertPrettyPrint("var a = (b = 5, c = 5);",
|
||
|
"var a = (b = 5, c = 5);\n");
|
||
|
}
|
||
|
|
||
|
public void testManyCommas() {
|
||
|
int numCommas = 10000;
|
||
|
List<String> numbers = Lists.newArrayList("0", "1");
|
||
|
Node current = new Node(Token.COMMA, Node.newNumber(0), Node.newNumber(1));
|
||
|
for (int i = 2; i < numCommas; i++) {
|
||
|
current = new Node(Token.COMMA, current);
|
||
|
|
||
|
// 1000 is printed as 1E3, and screws up our test.
|
||
|
int num = i % 1000;
|
||
|
numbers.add(String.valueOf(num));
|
||
|
current.addChildToBack(Node.newNumber(num));
|
||
|
}
|
||
|
|
||
|
String expected = Joiner.on(",").join(numbers);
|
||
|
String actual = printNode(current).replace("\n", "");
|
||
|
assertEquals(expected, actual);
|
||
|
}
|
||
|
|
||
|
public void testManyAdds() {
|
||
|
int numAdds = 10000;
|
||
|
List<String> numbers = Lists.newArrayList("0", "1");
|
||
|
Node current = new Node(Token.ADD, Node.newNumber(0), Node.newNumber(1));
|
||
|
for (int i = 2; i < numAdds; i++) {
|
||
|
current = new Node(Token.ADD, current);
|
||
|
|
||
|
// 1000 is printed as 1E3, and screws up our test.
|
||
|
int num = i % 1000;
|
||
|
numbers.add(String.valueOf(num));
|
||
|
current.addChildToBack(Node.newNumber(num));
|
||
|
}
|
||
|
|
||
|
String expected = Joiner.on("+").join(numbers);
|
||
|
String actual = printNode(current).replace("\n", "");
|
||
|
assertEquals(expected, actual);
|
||
|
}
|
||
|
|
||
|
public void testMinusNegativeZero() {
|
||
|
// Negative zero is weird, because we have to be able to distinguish
|
||
|
// it from positive zero (there are some subtle differences in behavior).
|
||
|
assertPrint("x- -0", "x- -0");
|
||
|
}
|
||
|
|
||
|
public void testStringEscapeSequences() {
|
||
|
// From the SingleEscapeCharacter grammar production.
|
||
|
assertPrintSame("var x=\"\\b\"");
|
||
|
assertPrintSame("var x=\"\\f\"");
|
||
|
assertPrintSame("var x=\"\\n\"");
|
||
|
assertPrintSame("var x=\"\\r\"");
|
||
|
assertPrintSame("var x=\"\\t\"");
|
||
|
assertPrintSame("var x=\"\\v\"");
|
||
|
assertPrint("var x=\"\\\"\"", "var x='\"'");
|
||
|
assertPrint("var x=\"\\\'\"", "var x=\"'\"");
|
||
|
|
||
|
// From the LineTerminator grammar
|
||
|
assertPrint("var x=\"\\u000A\"", "var x=\"\\n\"");
|
||
|
assertPrint("var x=\"\\u000D\"", "var x=\"\\r\"");
|
||
|
assertPrintSame("var x=\"\\u2028\"");
|
||
|
assertPrintSame("var x=\"\\u2029\"");
|
||
|
|
||
|
// Now with regular expressions.
|
||
|
assertPrintSame("var x=/\\b/");
|
||
|
assertPrintSame("var x=/\\f/");
|
||
|
assertPrintSame("var x=/\\n/");
|
||
|
assertPrintSame("var x=/\\r/");
|
||
|
assertPrintSame("var x=/\\t/");
|
||
|
assertPrintSame("var x=/\\v/");
|
||
|
assertPrintSame("var x=/\\u000A/");
|
||
|
assertPrintSame("var x=/\\u000D/");
|
||
|
assertPrintSame("var x=/\\u2028/");
|
||
|
assertPrintSame("var x=/\\u2029/");
|
||
|
}
|
||
|
}
|