From 4621481f0061e1e15e2f511a0296a0cef7cb35f7 Mon Sep 17 00:00:00 2001 From: Pablo Cubico Date: Thu, 25 Feb 2016 09:04:27 -0300 Subject: [PATCH 1/3] Attempt on supporting strict mode reserved words conditionally. --- lib/parse.js | 12 ++++++++---- lib/scope.js | 7 ++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/parse.js b/lib/parse.js index f1495153..8c243b43 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -46,14 +46,16 @@ var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with'; var KEYWORDS_ATOM = 'false null true'; -var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield' - + " " + KEYWORDS_ATOM + " " + KEYWORDS; +var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto import int long native short super synchronized this throws transient volatile yield' + + " " + KEYWORDS_ATOM + " " + KEYWORDS; // 'yield' is reserved in Firefox even outside strict mode. var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case'; +var RESERVED_WORDS_STRICT_ONLY = 'implements interface let package private protected public static'; KEYWORDS = makePredicate(KEYWORDS); RESERVED_WORDS = makePredicate(RESERVED_WORDS); KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION); KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM); +RESERVED_WORDS_STRICT_ONLY = makePredicate(RESERVED_WORDS_STRICT_ONLY); var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^")); @@ -152,8 +154,10 @@ function is_unicode_connector_punctuation(ch) { return UNICODE.connector_punctuation.test(ch); }; -function is_identifier(name) { - return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name); +function is_identifier(name, strict_mode) { + return !RESERVED_WORDS(name) + && /^[a-z_$][a-z0-9_$]*$/i.test(name) + && (!strict_mode || strict_mode && !RESERVED_WORDS_STRICT_ONLY(name)); }; function is_identifier_start(code) { diff --git a/lib/scope.js b/lib/scope.js index ace25894..13eed6ad 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -134,6 +134,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ s.uses_with = true; return; } + if (node instanceof AST_Directive) { + scope.directives.push(node.value); + return; + } if (node instanceof AST_Symbol) { node.scope = scope; } @@ -237,6 +241,7 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes this.cname = -1; // the current index for mangling functions/variables this.nesting = nesting; // the nesting level of this scope (0 means toplevel) + this.directives = []; // a list of directives like "use strict" present in this scope }); AST_Lambda.DEFMETHOD("init_scope_vars", function(){ @@ -287,7 +292,7 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){ var ext = this.enclosed; out: while (true) { var m = base54(++this.cname); - if (!is_identifier(m)) continue; // skip over "do" + if (!is_identifier(m, this.directives.indexOf('use strict') >= 0)) continue; // skip over "do" // https://github.com/mishoo/UglifyJS2/issues/242 -- do not // shadow a name excepted from mangling. From 0d1cff40ec1202e62c134c88edf32b269bb564e5 Mon Sep 17 00:00:00 2001 From: Pablo Cubico Date: Thu, 25 Feb 2016 13:36:06 -0300 Subject: [PATCH 2/3] Removing redundant condition. --- lib/parse.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/parse.js b/lib/parse.js index 8c243b43..6e51ae70 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -157,7 +157,7 @@ function is_unicode_connector_punctuation(ch) { function is_identifier(name, strict_mode) { return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name) - && (!strict_mode || strict_mode && !RESERVED_WORDS_STRICT_ONLY(name)); + && (!strict_mode || !RESERVED_WORDS_STRICT_ONLY(name)); }; function is_identifier_start(code) { From 1aa26cdef153fe16ff71b4e8ff4c380f0500835e Mon Sep 17 00:00:00 2001 From: Pablo Cubico Date: Sun, 28 Feb 2016 09:05:57 -0300 Subject: [PATCH 3/3] A few basic tests. --- test/compress/strict-mode.js | 98 ++++++++++++++++++++++++++++++++++++ test/strict-mode-mangler.js | 77 ++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 test/compress/strict-mode.js create mode 100644 test/strict-mode-mangler.js diff --git a/test/compress/strict-mode.js b/test/compress/strict-mode.js new file mode 100644 index 00000000..c292a896 --- /dev/null +++ b/test/compress/strict-mode.js @@ -0,0 +1,98 @@ +keep_non_reserved_in_non_strict_mode: { + input: { + function outer() { + var implements = 1; + var interface = 1; + var let = 1; + var package = 1; + var private = 1; + var protected = 1; + var public = 1; + var static = 1; + + function inner() { + implements++; + interface++; + let++; + package++; + private++; + protected++; + public++; + static++; + } + } + } + expect: { + function outer() { + var implements = 1; + var interface = 1; + var let = 1; + var package = 1; + var private = 1; + var protected = 1; + var public = 1; + var static = 1; + + function inner() { + implements++; + interface++; + let++; + package++; + private++; + protected++; + public++; + static++; + } + } + } +} + +mangle_non_reserved_in_non_strict_mode: { + mangle = {} + input: { + function outer() { + var implements = 1; + var interface = 1; + var let = 1; + var package = 1; + var private = 1; + var protected = 1; + var public = 1; + var static = 1; + + function inner() { + implements++; + interface++; + let++; + package++; + private++; + protected++; + public++; + static++; + } + } + } + expect: { + function outer() { + var r=1; + var a=1; + var v=1; + var n=1; + var o=1; + var t=1; + var u=1; + var c=1; + function f() { + r++; + a++; + v++; + n++; + o++; + t++; + u++; + c++ + } + } + } +} + diff --git a/test/strict-mode-mangler.js b/test/strict-mode-mangler.js new file mode 100644 index 00000000..bb377481 --- /dev/null +++ b/test/strict-mode-mangler.js @@ -0,0 +1,77 @@ +var UglifyJS = require(".."); +var assert = require("assert"); + +var alphabet = "abcdefghijklmnop".split(""); +var perms = []; +var linesOfCode = []; +var startTime = new Date().getTime(); + +console.log("--- Strict mode mangling tests"); + +function permute(text) { + perms.push(text); + if(text.length < 4) { + for(var i = 0; i < alphabet.length; i++) { + permute(text + alphabet[i]); + } + } +} +permute(""); + +var permuteTime = new Date().getTime() - startTime; + +console.log(" - Generated perms in " + permuteTime + "ms") +console.log(" - Testing with " + perms.length + " identifiers"); +console.log(" - Mangling (this will take some time)..."); + +function getCode(withStrictMode) { + + linesOfCode = " function outer() {\n"; + if (!!withStrictMode) { + linesOfCode += " \"use strict\";\n"; + } + for (var i in perms) { + linesOfCode += " var _" + perms[i] + " = 1;\n" + } + linesOfCode += " function inner() {\n"; + for (var i in perms) { + linesOfCode += " _" + perms[i] + "++;\n"; + } + linesOfCode += " }\n"; + linesOfCode += " }\n"; + + return linesOfCode; + +} + +module.exports = function () { + + var startManglingTime = new Date().getTime(); + var mangled_code = mangle(getCode(false)); + + console.log(" - Found 'var let' at char: " + mangled_code.indexOf('var let')); + console.log(" - Mangled code with no strict mode in " + Math.floor((new Date().getTime() - startManglingTime)/ 1000) + "s"); + assert(mangled_code.indexOf('var let') >= 0, "No 'var let' produced in the mangled code outside strict mode."); + + startManglingTime = new Date().getTime(); + var mangled_strict_code = mangle(getCode(true)); + console.log(" - This should be -1: " + mangled_strict_code.indexOf('var let')); + console.log(" - Mangled code with strict mode in " + Math.floor((new Date().getTime() - startManglingTime)/ 1000) + "s"); + assert(mangled_strict_code.indexOf('var let') === -1, "A 'var let' statement was produced inside strict mode."); + +} + +function mangle(js) { + var stream = UglifyJS.OutputStream(); + var parsed = UglifyJS.parse(js); + parsed.figure_out_scope(); + parsed.mangle_names(); + parsed.print(stream); + return stream.toString(); +} + +// Run standalone +if (module.parent === null) { + module.exports(); +} +