From cdf8407f3161c09fa5294e9b98caa18c330cd320 Mon Sep 17 00:00:00 2001 From: Nick Evans Date: Sun, 3 Jan 2016 04:59:58 +0000 Subject: [PATCH] New: ecmaFeatures.impliedStrict (fixes: #227) --- README.md | 3 + espree.js | 15 + lib/features.js | 3 + .../global-strict-violation.result.js | 132 +++++++ .../global-strict-violation.src.js | 1 + .../inner-strict-violation.result.js | 332 ++++++++++++++++++ .../inner-strict-violation.src.js | 3 + tests/lib/ecma-features.js | 18 +- 8 files changed, 503 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/ecma-features/impliedStrict/global-strict-violation.result.js create mode 100644 tests/fixtures/ecma-features/impliedStrict/global-strict-violation.src.js create mode 100644 tests/fixtures/ecma-features/impliedStrict/inner-strict-violation.result.js create mode 100644 tests/fixtures/ecma-features/impliedStrict/inner-strict-violation.src.js diff --git a/README.md b/README.md index 7c4e961f..d1902c22 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,9 @@ var ast = espree.parse(code, { // enable return in global scope globalReturn: true, + // enable implied strict mode (if ecmaVersion >= 5) + impliedStrict: true, + // allow experimental object rest/spread experimentalObjectRestSpread: true } diff --git a/espree.js b/espree.js index 17b1f345..b3bd55b5 100644 --- a/espree.js +++ b/espree.js @@ -266,6 +266,15 @@ pp.extend("checkLVal", function(checkLVal) { }; }); +pp.extend("parseTopLevel", function(parseTopLevel) { + return /** @this acorn.Parser */ function(node) { + if (extra.ecmaFeatures.impliedStrict && this.options.ecmaVersion >= 5) { + this.strict = true; + } + return parseTopLevel.call(this, node); + }; +}); + /** * Method to parse an object rest or object spread. * @returns {ASTNode} The node representing object rest or object spread. @@ -422,6 +431,7 @@ pp.extend("jsx_readString", function(jsxReadString) { function tokenize(code, options) { var toString, tokens, + impliedStrict, translator = new TokenTranslator(tt, code); toString = String; @@ -478,6 +488,8 @@ function tokenize(code, options) { // apply parsing flags if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") { extra.ecmaFeatures = options.ecmaFeatures; + impliedStrict = extra.ecmaFeatures.impliedStrict; + extra.ecmaFeatures.impliedStrict = typeof impliedStrict === "boolean" && impliedStrict; } try { @@ -554,6 +566,7 @@ function parse(code, options) { var program, toString = String, translator, + impliedStrict, acornOptions = { ecmaVersion: 5 }; @@ -620,6 +633,8 @@ function parse(code, options) { // apply parsing flags after sourceType to allow overriding if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") { extra.ecmaFeatures = options.ecmaFeatures; + impliedStrict = extra.ecmaFeatures.impliedStrict; + extra.ecmaFeatures.impliedStrict = typeof impliedStrict === "boolean" && impliedStrict; if (options.ecmaFeatures.globalReturn) { acornOptions.allowReturnOutsideFunction = true; } diff --git a/lib/features.js b/lib/features.js index a432ebcb..aa2a6e48 100644 --- a/lib/features.js +++ b/lib/features.js @@ -46,6 +46,9 @@ module.exports = { // allow return statement in global scope globalReturn: false, + // allow implied strict mode + impliedStrict: false, + // allow experimental object rest/spread experimentalObjectRestSpread: false }; diff --git a/tests/fixtures/ecma-features/impliedStrict/global-strict-violation.result.js b/tests/fixtures/ecma-features/impliedStrict/global-strict-violation.result.js new file mode 100644 index 00000000..f181d4aa --- /dev/null +++ b/tests/fixtures/ecma-features/impliedStrict/global-strict-violation.result.js @@ -0,0 +1,132 @@ +module.exports = { + "type": "Program", + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "private", + "range": [ + 4, + 11 + ], + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 11 + } + } + }, + "init": null, + "range": [ + 4, + 11 + ], + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 11 + } + } + } + ], + "kind": "var", + "range": [ + 0, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 12 + } + } + } + ], + "range": [ + 0, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "tokens": [ + { + "type": "Keyword", + "value": "var", + "range": [ + 0, + 3 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 3 + } + } + }, + { + "type": "Identifier", + "value": "private", + "range": [ + 4, + 11 + ], + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 11 + } + } + }, + { + "type": "Punctuator", + "value": ";", + "range": [ + 11, + 12 + ], + "loc": { + "start": { + "line": 1, + "column": 11 + }, + "end": { + "line": 1, + "column": 12 + } + } + } + ] +}; diff --git a/tests/fixtures/ecma-features/impliedStrict/global-strict-violation.src.js b/tests/fixtures/ecma-features/impliedStrict/global-strict-violation.src.js new file mode 100644 index 00000000..0dd8baaf --- /dev/null +++ b/tests/fixtures/ecma-features/impliedStrict/global-strict-violation.src.js @@ -0,0 +1 @@ +var private; \ No newline at end of file diff --git a/tests/fixtures/ecma-features/impliedStrict/inner-strict-violation.result.js b/tests/fixtures/ecma-features/impliedStrict/inner-strict-violation.result.js new file mode 100644 index 00000000..b32a48d0 --- /dev/null +++ b/tests/fixtures/ecma-features/impliedStrict/inner-strict-violation.result.js @@ -0,0 +1,332 @@ +module.exports = { + "type": "Program", + "sourceType": "script", + "body": [ + { + "type": "FunctionDeclaration", + "id": { + "type": "Identifier", + "name": "inner", + "range": [ + 9, + 14 + ], + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 14 + } + } + }, + "params": [], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "private", + "range": [ + 27, + 34 + ], + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 15 + } + } + }, + "init": null, + "range": [ + 27, + 34 + ], + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 15 + } + } + } + ], + "kind": "var", + "range": [ + 23, + 35 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 16 + } + } + } + ], + "range": [ + 17, + 37 + ], + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 3, + "column": 1 + } + } + }, + "generator": false, + "expression": false, + "range": [ + 0, + 37 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + } + } + }, + { + "type": "EmptyStatement", + "range": [ + 37, + 38 + ], + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 3, + "column": 2 + } + } + } + ], + "range": [ + 0, + 38 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "tokens": [ + { + "type": "Keyword", + "value": "function", + "range": [ + 0, + 8 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 8 + } + } + }, + { + "type": "Identifier", + "value": "inner", + "range": [ + 9, + 14 + ], + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 14 + } + } + }, + { + "type": "Punctuator", + "value": "(", + "range": [ + 14, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + { + "type": "Punctuator", + "value": ")", + "range": [ + 15, + 16 + ], + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 16 + } + } + }, + { + "type": "Punctuator", + "value": "{", + "range": [ + 17, + 18 + ], + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 1, + "column": 18 + } + } + }, + { + "type": "Keyword", + "value": "var", + "range": [ + 23, + 26 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 7 + } + } + }, + { + "type": "Identifier", + "value": "private", + "range": [ + 27, + 34 + ], + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 15 + } + } + }, + { + "type": "Punctuator", + "value": ";", + "range": [ + 34, + 35 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 16 + } + } + }, + { + "type": "Punctuator", + "value": "}", + "range": [ + 36, + 37 + ], + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + } + } + }, + { + "type": "Punctuator", + "value": ";", + "range": [ + 37, + 38 + ], + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 3, + "column": 2 + } + } + } + ] +}; diff --git a/tests/fixtures/ecma-features/impliedStrict/inner-strict-violation.src.js b/tests/fixtures/ecma-features/impliedStrict/inner-strict-violation.src.js new file mode 100644 index 00000000..9a90a7a3 --- /dev/null +++ b/tests/fixtures/ecma-features/impliedStrict/inner-strict-violation.src.js @@ -0,0 +1,3 @@ +function inner() { + var private; +}; \ No newline at end of file diff --git a/tests/lib/ecma-features.js b/tests/lib/ecma-features.js index 5e947c96..47931581 100644 --- a/tests/lib/ecma-features.js +++ b/tests/lib/ecma-features.js @@ -57,6 +57,15 @@ var testFiles = shelljs.find(FIXTURES_DIR).filter(function(filename) { // Tests //------------------------------------------------------------------------------ +/** + * Returns whether a feature should throw in its tests when it is enabled. + * @param {string} feature The name of the feature. + * @returns {boolean} Whether it should throw in its tests when it is enabled. + */ +function shouldThrowInTestsWhenEnabled(feature) { + return (feature === "impliedStrict"); +} + describe("ecmaFeatures", function() { var config; @@ -75,17 +84,18 @@ describe("ecmaFeatures", function() { // Uncomment and fill in filename to focus on a single file // var filename = "jsx/invalid-matching-placeholder-in-closing-tag"; var feature = path.dirname(filename), + isPermissive = !shouldThrowInTestsWhenEnabled(feature), code = shelljs.cat(path.resolve(FIXTURES_DIR, filename) + ".src.js"); - it("should parse correctly when " + feature + " is true", function() { - config.ecmaFeatures[feature] = true; + it("should parse correctly when " + feature + " is " + isPermissive, function() { + config.ecmaFeatures[feature] = isPermissive; var expected = require(path.resolve(__dirname, "../../", FIXTURES_DIR, filename) + ".result.js"); tester.assertMatches(code, config, expected); }); - it("should throw an error when " + feature + " is false", function() { - config.ecmaFeatures[feature] = false; + it("should throw an error when " + feature + " is " + !isPermissive, function() { + config.ecmaFeatures[feature] = !isPermissive; assert.throws(function() { espree.parse(code, config);