fix corner case in directives (#3645)
This commit is contained in:
@@ -43,8 +43,6 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/;
|
|
||||||
|
|
||||||
function is_some_comments(comment) {
|
function is_some_comments(comment) {
|
||||||
// multiline comment
|
// multiline comment
|
||||||
return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value);
|
return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value);
|
||||||
@@ -378,7 +376,7 @@ function OutputStream(options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function force_semicolon() {
|
function force_semicolon() {
|
||||||
might_need_semicolon = false;
|
if (might_need_semicolon) print(";");
|
||||||
print(";");
|
print(";");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,17 +583,7 @@ function OutputStream(options) {
|
|||||||
force_semicolon : force_semicolon,
|
force_semicolon : force_semicolon,
|
||||||
to_utf8 : to_utf8,
|
to_utf8 : to_utf8,
|
||||||
print_name : function(name) { print(make_name(name)) },
|
print_name : function(name) { print(make_name(name)) },
|
||||||
print_string : function(str, quote, escape_directive) {
|
print_string : function(str, quote) { print(encode_string(str, quote)) },
|
||||||
var encoded = encode_string(str, quote);
|
|
||||||
if (escape_directive === true && encoded.indexOf("\\") === -1) {
|
|
||||||
// Insert semicolons to break directive prologue
|
|
||||||
if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
|
|
||||||
force_semicolon();
|
|
||||||
}
|
|
||||||
force_semicolon();
|
|
||||||
}
|
|
||||||
print(encoded);
|
|
||||||
},
|
|
||||||
next_indent : next_indent,
|
next_indent : next_indent,
|
||||||
with_indent : with_indent,
|
with_indent : with_indent,
|
||||||
with_block : with_block,
|
with_block : with_block,
|
||||||
@@ -633,17 +621,10 @@ function OutputStream(options) {
|
|||||||
nodetype.DEFMETHOD("_codegen", generator);
|
nodetype.DEFMETHOD("_codegen", generator);
|
||||||
}
|
}
|
||||||
|
|
||||||
var in_directive = false;
|
var use_asm = false;
|
||||||
var active_scope = null;
|
|
||||||
var use_asm = null;
|
|
||||||
|
|
||||||
AST_Node.DEFMETHOD("print", function(stream, force_parens) {
|
AST_Node.DEFMETHOD("print", function(stream, force_parens) {
|
||||||
var self = this, generator = self._codegen;
|
var self = this, generator = self._codegen;
|
||||||
if (self instanceof AST_Scope) {
|
|
||||||
active_scope = self;
|
|
||||||
} else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
|
|
||||||
use_asm = active_scope;
|
|
||||||
}
|
|
||||||
function doit() {
|
function doit() {
|
||||||
stream.prepend_comments(self);
|
stream.prepend_comments(self);
|
||||||
self.add_source_map(stream);
|
self.add_source_map(stream);
|
||||||
@@ -657,9 +638,6 @@ function OutputStream(options) {
|
|||||||
doit();
|
doit();
|
||||||
}
|
}
|
||||||
stream.pop_node();
|
stream.pop_node();
|
||||||
if (self === use_asm) {
|
|
||||||
use_asm = null;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
|
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
|
||||||
|
|
||||||
@@ -828,7 +806,18 @@ function OutputStream(options) {
|
|||||||
/* -----[ PRINTERS ]----- */
|
/* -----[ PRINTERS ]----- */
|
||||||
|
|
||||||
DEFPRINT(AST_Directive, function(self, output) {
|
DEFPRINT(AST_Directive, function(self, output) {
|
||||||
output.print_string(self.value, self.quote);
|
var quote = self.quote;
|
||||||
|
var value = self.value;
|
||||||
|
switch (output.option("quote_style")) {
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
if (value.indexOf('"') == -1) quote = '"';
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (value.indexOf("'") == -1) quote = "'";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
output.print(quote + value + quote);
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Debugger, function(self, output) {
|
DEFPRINT(AST_Debugger, function(self, output) {
|
||||||
@@ -840,30 +829,27 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
function display_body(body, is_toplevel, output, allow_directives) {
|
function display_body(body, is_toplevel, output, allow_directives) {
|
||||||
var last = body.length - 1;
|
var last = body.length - 1;
|
||||||
in_directive = allow_directives;
|
var in_directive = allow_directives;
|
||||||
|
var was_asm = use_asm;
|
||||||
body.forEach(function(stmt, i) {
|
body.forEach(function(stmt, i) {
|
||||||
if (in_directive === true && !(stmt instanceof AST_Directive ||
|
if (in_directive) {
|
||||||
stmt instanceof AST_EmptyStatement ||
|
if (stmt instanceof AST_Directive) {
|
||||||
(stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String)
|
if (stmt.value == "use asm") use_asm = true;
|
||||||
)) {
|
} else if (!(stmt instanceof AST_EmptyStatement)) {
|
||||||
in_directive = false;
|
if (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) {
|
||||||
}
|
output.force_semicolon();
|
||||||
if (!(stmt instanceof AST_EmptyStatement)) {
|
}
|
||||||
output.indent();
|
in_directive = false;
|
||||||
stmt.print(output);
|
|
||||||
if (!(i == last && is_toplevel)) {
|
|
||||||
output.newline();
|
|
||||||
if (is_toplevel) output.newline();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (in_directive === true &&
|
if (stmt instanceof AST_EmptyStatement) return;
|
||||||
stmt instanceof AST_SimpleStatement &&
|
output.indent();
|
||||||
stmt.body instanceof AST_String
|
stmt.print(output);
|
||||||
) {
|
if (i == last && is_toplevel) return;
|
||||||
in_directive = false;
|
output.newline();
|
||||||
}
|
if (is_toplevel) output.newline();
|
||||||
});
|
});
|
||||||
in_directive = false;
|
use_asm = was_asm;
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) {
|
AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) {
|
||||||
@@ -1348,7 +1334,7 @@ function OutputStream(options) {
|
|||||||
output.print(self.getValue());
|
output.print(self.getValue());
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_String, function(self, output) {
|
DEFPRINT(AST_String, function(self, output) {
|
||||||
output.print_string(self.getValue(), self.quote, in_directive);
|
output.print_string(self.getValue(), self.quote);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Number, function(self, output) {
|
DEFPRINT(AST_Number, function(self, output) {
|
||||||
if (use_asm && self.start && self.start.raw != null) {
|
if (use_asm && self.start && self.start.raw != null) {
|
||||||
|
|||||||
@@ -790,9 +790,10 @@ function parse($TEXT, options) {
|
|||||||
var dir = S.in_directives;
|
var dir = S.in_directives;
|
||||||
var body = expression(true);
|
var body = expression(true);
|
||||||
if (dir) {
|
if (dir) {
|
||||||
var token = body.start;
|
if (body instanceof AST_String) {
|
||||||
if (body instanceof AST_String && token.raw.indexOf("\\") == -1) {
|
var value = body.start.raw.slice(1, -1);
|
||||||
S.input.add_directive(token.value);
|
S.input.add_directive(value);
|
||||||
|
body.value = value;
|
||||||
} else {
|
} else {
|
||||||
S.in_directives = dir = false;
|
S.in_directives = dir = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,3 +93,41 @@ issue_3166: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
valid_after_invalid_1: {
|
||||||
|
input: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
"use\x20strict";
|
||||||
|
"use strict";
|
||||||
|
return this;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
"use\x20strict";
|
||||||
|
"use strict";
|
||||||
|
return this;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_after_invalid_2: {
|
||||||
|
options = {
|
||||||
|
directives: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
"use\x20strict";
|
||||||
|
"use strict";
|
||||||
|
return this;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
"use strict";
|
||||||
|
return this;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,13 +69,13 @@ describe("Directives", function() {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'"use \\\nstrict";"use strict";',
|
'"use \\\nstrict";"use strict";',
|
||||||
[],
|
[ "use strict" ],
|
||||||
[ "use strict", "use\nstrict", "use \nstrict", "use asm" ]
|
[ "use\nstrict", "use \nstrict", "use asm" ]
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'"\\76";',
|
'"\\76";',
|
||||||
[],
|
[ "\\76" ],
|
||||||
[ ">", "\\76" ]
|
[ ">" ]
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
// no ; or newline
|
// no ; or newline
|
||||||
@@ -106,13 +106,13 @@ describe("Directives", function() {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'function foo() {"use \\\nstrict";"use strict";',
|
'function foo() {"use \\\nstrict";"use strict";',
|
||||||
[],
|
[ "use strict" ],
|
||||||
[ "use strict", "use\nstrict", "use \nstrict", "use asm" ]
|
[ "use\nstrict", "use \nstrict", "use asm" ]
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'var foo = function() {"\\76";',
|
'var foo = function() {"\\76";',
|
||||||
[],
|
[ "\\76" ],
|
||||||
[ ">", "\\76" ]
|
[ ">" ]
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'var foo = function() {"use strict"', // no ; or newline
|
'var foo = function() {"use strict"', // no ; or newline
|
||||||
@@ -156,21 +156,24 @@ describe("Directives", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("Should test EXPECT_DIRECTIVE RegExp", function() {
|
it("Should print semicolon to separate strings from directives", function() {
|
||||||
[
|
[
|
||||||
[ "", true ],
|
[ "", ';"";' ],
|
||||||
[ "'test';", true ],
|
[ '"test";', '"test";;"";' ],
|
||||||
[ "'test';;", true ],
|
[ '"test";;', '"test";;"";' ],
|
||||||
[ "'tests';\n", true ],
|
[ '"tests";\n', '"tests";;"";' ],
|
||||||
[ "'tests'", false ],
|
[ '"tests"', '"tests";;"";' ],
|
||||||
[ "'tests'; \n\t", true ],
|
[ '"tests"; \n\t', '"tests";;"";' ],
|
||||||
[ "'tests';\n\n", true ],
|
[ '"tests";\n\n', '"tests";;"";' ],
|
||||||
[ "\n\n\"use strict\";\n\n", true ],
|
[ '\n\n"use strict";\n\n', '"use strict";;"";' ],
|
||||||
].forEach(function(test) {
|
].forEach(function(test) {
|
||||||
|
var ast = UglifyJS.parse(test[0]);
|
||||||
|
ast.body.push(new UglifyJS.AST_SimpleStatement({
|
||||||
|
body: new UglifyJS.AST_String({ value: "" })
|
||||||
|
}));
|
||||||
var out = UglifyJS.OutputStream();
|
var out = UglifyJS.OutputStream();
|
||||||
out.print(test[0]);
|
ast.print(out);
|
||||||
out.print_string("", null, true);
|
assert.strictEqual(out.get(), test[1], test[0]);
|
||||||
assert.strictEqual(out.get() === test[0] + ';""', test[1], test[0]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("Should only print 2 semicolons spread over 2 lines in beautify mode", function() {
|
it("Should only print 2 semicolons spread over 2 lines in beautify mode", function() {
|
||||||
@@ -178,8 +181,8 @@ describe("Directives", function() {
|
|||||||
'"use strict";',
|
'"use strict";',
|
||||||
"'use strict';",
|
"'use strict';",
|
||||||
'"use strict";',
|
'"use strict";',
|
||||||
'"use strict";;',
|
'"use strict";',
|
||||||
"'use strict';",
|
";'use strict';",
|
||||||
"console.log('use strict');"
|
"console.log('use strict');"
|
||||||
].join(""), {
|
].join(""), {
|
||||||
compress: false,
|
compress: false,
|
||||||
@@ -201,19 +204,23 @@ describe("Directives", function() {
|
|||||||
it("Should not add double semicolons in non-scoped block statements to avoid strings becoming directives", function() {
|
it("Should not add double semicolons in non-scoped block statements to avoid strings becoming directives", function() {
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
'{"use\x20strict"}',
|
'"use strict";"use\\x20strict";',
|
||||||
|
'"use strict";"use\\x20strict";'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'{"use\\x20strict"}',
|
||||||
'{"use strict"}'
|
'{"use strict"}'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'function foo(){"use\x20strict";}', // Valid place for directives
|
'function foo(){"use\\x20strict";}', // Valid place for directives
|
||||||
'function foo(){"use strict"}'
|
'function foo(){"use\\x20strict"}'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'try{"use\x20strict"}catch(e){}finally{"use\x20strict"}',
|
'try{"use\\x20strict"}catch(e){}finally{"use\\x20strict"}',
|
||||||
'try{"use strict"}catch(e){}finally{"use strict"}'
|
'try{"use strict"}catch(e){}finally{"use strict"}'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'if(1){"use\x20strict"} else {"use strict"}',
|
'if(1){"use\\x20strict"} else {"use strict"}',
|
||||||
'if(1){"use strict"}else{"use strict"}'
|
'if(1){"use strict"}else{"use strict"}'
|
||||||
]
|
]
|
||||||
].forEach(function(test) {
|
].forEach(function(test) {
|
||||||
@@ -225,16 +232,6 @@ describe("Directives", function() {
|
|||||||
assert.strictEqual(result.code, test[1], test[0]);
|
assert.strictEqual(result.code, test[1], test[0]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("Should add double semicolon when relying on automatic semicolon insertion", function() {
|
|
||||||
var result = UglifyJS.minify('"use strict";"use\\x20strict";', {
|
|
||||||
compress: false,
|
|
||||||
output: {
|
|
||||||
semicolons: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (result.error) throw result.error;
|
|
||||||
assert.strictEqual(result.code, '"use strict";;"use strict"\n');
|
|
||||||
});
|
|
||||||
it("Should check quote style of directives", function() {
|
it("Should check quote style of directives", function() {
|
||||||
[
|
[
|
||||||
// 0. Prefer double quotes, unless string contains more double quotes than single quotes
|
// 0. Prefer double quotes, unless string contains more double quotes than single quotes
|
||||||
@@ -249,9 +246,9 @@ describe("Directives", function() {
|
|||||||
'"use strict";'
|
'"use strict";'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'"\\\'use strict\\\'";', // Not a directive as it contains quotes
|
'"\\\'use strict\\\'";',
|
||||||
0,
|
0,
|
||||||
';"\'use strict\'";',
|
'"\\\'use strict\\\'";',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"'\"use strict\"';",
|
"'\"use strict\"';",
|
||||||
@@ -273,7 +270,7 @@ describe("Directives", function() {
|
|||||||
'"\'use strict\'";',
|
'"\'use strict\'";',
|
||||||
1,
|
1,
|
||||||
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
|
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
|
||||||
"'\\'use strict\\'';",
|
'"\'use strict\'";',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"'\\'use strict\\'';", // Not a valid directive
|
"'\\'use strict\\'';", // Not a valid directive
|
||||||
@@ -305,7 +302,7 @@ describe("Directives", function() {
|
|||||||
"'\"use strict\"';",
|
"'\"use strict\"';",
|
||||||
2,
|
2,
|
||||||
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
|
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
|
||||||
'"\\\"use strict\\\"";',
|
"'\"use strict\"';",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'"\\"use strict\\"";', // Not a valid directive
|
'"\\"use strict\\"";', // Not a valid directive
|
||||||
@@ -353,8 +350,7 @@ describe("Directives", function() {
|
|||||||
[
|
[
|
||||||
// Nothing gets optimised in the compressor because "use asm" is the first statement
|
// Nothing gets optimised in the compressor because "use asm" is the first statement
|
||||||
'"use asm";"use\\x20strict";1+1;',
|
'"use asm";"use\\x20strict";1+1;',
|
||||||
// Yet, the parser noticed that "use strict" wasn't a directive
|
'"use asm";"use\\x20strict";1+1;'
|
||||||
'"use asm";;"use strict";1+1;',
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'function f(){ "use strict" }',
|
'function f(){ "use strict" }',
|
||||||
|
|||||||
@@ -59,13 +59,13 @@ describe("String literals", function() {
|
|||||||
|
|
||||||
it("Should not throw error outside strict mode if string contains escaped octalIntegerLiteral", function() {
|
it("Should not throw error outside strict mode if string contains escaped octalIntegerLiteral", function() {
|
||||||
var tests = [
|
var tests = [
|
||||||
['"\\76";', ';">";'],
|
[ ';"\\76";', ';">";' ],
|
||||||
['"\\0"', '"\\0";'],
|
[ ';"\\0";', ';"\\0";' ],
|
||||||
['"\\08"', '"\\x008";'],
|
[ ';"\\08"', ';"\\x008";' ],
|
||||||
['"\\008"', '"\\x008";'],
|
[ ';"\\008"', ';"\\x008";' ],
|
||||||
['"\\0008"', '"\\x008";'],
|
[ ';"\\0008"', ';"\\x008";' ],
|
||||||
['"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";'],
|
[ ';"use\\\n strict";\n"\\07";', ';"use strict";"\07";' ],
|
||||||
['"use\\\n strict";\n"\\07";', ';"use strict";"\07";']
|
[ '"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";' ],
|
||||||
];
|
];
|
||||||
|
|
||||||
for (var test in tests) {
|
for (var test in tests) {
|
||||||
@@ -75,8 +75,8 @@ describe("String literals", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Should not throw error when digit is 8 or 9", function() {
|
it("Should not throw error when digit is 8 or 9", function() {
|
||||||
assert.equal(UglifyJS.parse('"use strict";"\\08"').print_to_string(), '"use strict";"\\x008";');
|
assert.equal(UglifyJS.parse('"use strict";;"\\08"').print_to_string(), '"use strict";;"\\x008";');
|
||||||
assert.equal(UglifyJS.parse('"use strict";"\\09"').print_to_string(), '"use strict";"\\x009";');
|
assert.equal(UglifyJS.parse('"use strict";;"\\09"').print_to_string(), '"use strict";;"\\x009";');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should not unescape unpaired surrogates", function() {
|
it("Should not unescape unpaired surrogates", function() {
|
||||||
@@ -93,7 +93,7 @@ describe("String literals", function() {
|
|||||||
for (; i <= 0xFFFF; i++) {
|
for (; i <= 0xFFFF; i++) {
|
||||||
code.push("\\u" + i.toString(16));
|
code.push("\\u" + i.toString(16));
|
||||||
}
|
}
|
||||||
code = '"' + code.join() + '"';
|
code = ';"' + code.join() + '"';
|
||||||
var normal = UglifyJS.minify(code, {
|
var normal = UglifyJS.minify(code, {
|
||||||
compress: false,
|
compress: false,
|
||||||
mangle: false,
|
mangle: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user