improve parser under "use strict"
- `const` without value - `delete` of expression - redefining `arguments` or `eval` fixes #1810
This commit is contained in:
parent
45ce369480
commit
379365d391
74
lib/parse.js
74
lib/parse.js
|
|
@ -629,8 +629,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||
}
|
||||
|
||||
next_token.has_directive = function(directive) {
|
||||
return S.directives[directive] !== undefined &&
|
||||
S.directives[directive] > 0;
|
||||
return S.directives[directive] > 0;
|
||||
}
|
||||
|
||||
return next_token;
|
||||
|
|
@ -1033,29 +1032,32 @@ function parse($TEXT, options) {
|
|||
if (in_statement && !name)
|
||||
unexpected();
|
||||
expect("(");
|
||||
var argnames = [];
|
||||
for (var first = true; !is("punc", ")");) {
|
||||
if (first) first = false; else expect(",");
|
||||
argnames.push(as_symbol(AST_SymbolFunarg));
|
||||
}
|
||||
next();
|
||||
var loop = S.in_loop;
|
||||
var labels = S.labels;
|
||||
++S.in_function;
|
||||
S.in_directives = true;
|
||||
S.input.push_directives_stack();
|
||||
S.in_loop = 0;
|
||||
S.labels = [];
|
||||
var body = block_();
|
||||
if (S.input.has_directive("use strict")) {
|
||||
if (name) strict_verify_symbol(name);
|
||||
argnames.forEach(strict_verify_symbol);
|
||||
}
|
||||
S.input.pop_directives_stack();
|
||||
--S.in_function;
|
||||
S.in_loop = loop;
|
||||
S.labels = labels;
|
||||
return new ctor({
|
||||
name: name,
|
||||
argnames: (function(first, a){
|
||||
while (!is("punc", ")")) {
|
||||
if (first) first = false; else expect(",");
|
||||
a.push(as_symbol(AST_SymbolFunarg));
|
||||
}
|
||||
next();
|
||||
return a;
|
||||
})(true, []),
|
||||
body: (function(loop, labels){
|
||||
++S.in_function;
|
||||
S.in_directives = true;
|
||||
S.input.push_directives_stack();
|
||||
S.in_loop = 0;
|
||||
S.labels = [];
|
||||
var a = block_();
|
||||
S.input.pop_directives_stack();
|
||||
--S.in_function;
|
||||
S.in_loop = loop;
|
||||
S.labels = labels;
|
||||
return a;
|
||||
})(S.in_loop, S.labels)
|
||||
argnames: argnames,
|
||||
body: body
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -1157,7 +1159,10 @@ function parse($TEXT, options) {
|
|||
a.push(new AST_VarDef({
|
||||
start : S.token,
|
||||
name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
|
||||
value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
|
||||
value : is("operator", "=")
|
||||
? (next(), expression(false, no_in))
|
||||
: in_const && S.input.has_directive("use strict")
|
||||
? croak("Missing initializer in const declaration") : null,
|
||||
end : prev()
|
||||
}));
|
||||
if (!is("punc", ","))
|
||||
|
|
@ -1384,12 +1389,20 @@ function parse($TEXT, options) {
|
|||
});
|
||||
};
|
||||
|
||||
function strict_verify_symbol(sym) {
|
||||
if (sym.name == "arguments" || sym.name == "eval")
|
||||
croak("Unexpected " + sym.name + " in strict mode", sym.start.line, sym.start.col, sym.start.pos);
|
||||
}
|
||||
|
||||
function as_symbol(type, noerror) {
|
||||
if (!is("name")) {
|
||||
if (!noerror) croak("Name expected");
|
||||
return null;
|
||||
}
|
||||
var sym = _make_symbol(type);
|
||||
if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) {
|
||||
strict_verify_symbol(sym);
|
||||
}
|
||||
next();
|
||||
return sym;
|
||||
};
|
||||
|
|
@ -1450,8 +1463,17 @@ function parse($TEXT, options) {
|
|||
|
||||
function make_unary(ctor, token, expr) {
|
||||
var op = token.value;
|
||||
if ((op == "++" || op == "--") && !is_assignable(expr))
|
||||
croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
|
||||
switch (op) {
|
||||
case "++":
|
||||
case "--":
|
||||
if (!is_assignable(expr))
|
||||
croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
|
||||
break;
|
||||
case "delete":
|
||||
if (!(expr instanceof AST_PropAccess) && S.input.has_directive("use strict"))
|
||||
croak("Calling delete on expression not allowed in strict mode", expr.start.line, expr.start.col, expr.start.pos);
|
||||
break;
|
||||
}
|
||||
return new ctor({ operator: op, expression: expr });
|
||||
};
|
||||
|
||||
|
|
|
|||
8
test/input/invalid/const.js
Normal file
8
test/input/invalid/const.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
function f() {
|
||||
const a;
|
||||
}
|
||||
|
||||
function g() {
|
||||
"use strict";
|
||||
const a;
|
||||
}
|
||||
8
test/input/invalid/delete.js
Normal file
8
test/input/invalid/delete.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
function f(x) {
|
||||
delete x;
|
||||
}
|
||||
|
||||
function g(x) {
|
||||
"use strict";
|
||||
delete x;
|
||||
}
|
||||
6
test/input/invalid/function_1.js
Normal file
6
test/input/invalid/function_1.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
function f(arguments) {
|
||||
}
|
||||
|
||||
function g(arguments) {
|
||||
"use strict";
|
||||
}
|
||||
6
test/input/invalid/function_2.js
Normal file
6
test/input/invalid/function_2.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
function arguments() {
|
||||
}
|
||||
|
||||
function eval() {
|
||||
"use strict";
|
||||
}
|
||||
6
test/input/invalid/function_3.js
Normal file
6
test/input/invalid/function_3.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
!function eval() {
|
||||
}();
|
||||
|
||||
!function arguments() {
|
||||
"use strict";
|
||||
}();
|
||||
8
test/input/invalid/try.js
Normal file
8
test/input/invalid/try.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
function f() {
|
||||
try {} catch (eval) {}
|
||||
}
|
||||
|
||||
function g() {
|
||||
"use strict";
|
||||
try {} catch (eval) {}
|
||||
}
|
||||
8
test/input/invalid/var.js
Normal file
8
test/input/invalid/var.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
function f() {
|
||||
var eval;
|
||||
}
|
||||
|
||||
function g() {
|
||||
"use strict";
|
||||
var eval;
|
||||
}
|
||||
|
|
@ -379,6 +379,111 @@ describe("bin/uglifyjs", function () {
|
|||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (const a)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/const.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/const.js:7,11",
|
||||
" const a;",
|
||||
" ^",
|
||||
"ERROR: Missing initializer in const declaration"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (delete x)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/delete.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/delete.js:7,11",
|
||||
" delete x;",
|
||||
" ^",
|
||||
"ERROR: Calling delete on expression not allowed in strict mode"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (function g(arguments))", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/function_1.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/function_1.js:4,11",
|
||||
"function g(arguments) {",
|
||||
" ^",
|
||||
"ERROR: Unexpected arguments in strict mode"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (function eval())", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/function_2.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/function_2.js:4,9",
|
||||
"function eval() {",
|
||||
" ^",
|
||||
"ERROR: Unexpected eval in strict mode"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (iife arguments())", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/function_3.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/function_3.js:4,10",
|
||||
"!function arguments() {",
|
||||
" ^",
|
||||
"ERROR: Unexpected arguments in strict mode"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (catch(eval))", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/try.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/try.js:7,18",
|
||||
" try {} catch (eval) {}",
|
||||
" ^",
|
||||
"ERROR: Unexpected eval in strict mode"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (var eval)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/var.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/var.js:7,8",
|
||||
" var eval;",
|
||||
" ^",
|
||||
"ERROR: Unexpected eval in strict mode"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should handle literal string as source map input", function(done) {
|
||||
var command = [
|
||||
uglifyjscmd,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user