diff --git a/common/content/autocommands.js b/common/content/autocommands.js index 97232f9b..8acf6c36 100644 --- a/common/content/autocommands.js +++ b/common/content/autocommands.js @@ -174,7 +174,7 @@ const AutoCommands = Module("autocommands", { if (args.bang) autocommands.remove(event, regex); if (args["-javascript"]) - cmd = eval("(function (args) { with(args) {" + cmd + "} })"); + cmd = dactyl.userfunc("args", "with(args) {" + cmd + "}"); autocommands.add(events, regex, cmd); } else { diff --git a/common/content/commandline.js b/common/content/commandline.js index 6d5b920d..c7552aaf 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -1352,7 +1352,7 @@ const CommandLine = Module("commandline", { }), /** - * eval() a JavaScript expression and return a string suitable + * Evaluate a JavaScript expression and return a string suitable * to be echoed. * * @param {string} arg @@ -1364,7 +1364,7 @@ const CommandLine = Module("commandline", { return ""; try { - arg = dactyl.eval(arg); + arg = dactyl.usereval(arg); } catch (e) { dactyl.echoerr(e); diff --git a/common/content/commands.js b/common/content/commands.js index 8a25bebb..b19653f3 100644 --- a/common/content/commands.js +++ b/common/content/commands.js @@ -877,45 +877,15 @@ const Commands = Module("commands", { while (str.length && !/^\s/.test(str)) { let res; - - switch (Commands.QUOTE_STYLE) { - case "vim-sucks": - if (res = str.match = str.match(/^()((?:[^\\\s]|\\.)+)((?:\\$)?)/)) - arg += res[2].replace(/\\(.)/g, "$1"); + if ((res = str.match = str.match(/^()((?:[^\\\s"']|\\.)+)((?:\\$)?)/))) + arg += res[2].replace(/\\(.)/g, "$1"); + else if ((res = str.match(/^(")((?:[^\\"]|\\.)*)("?)/))) + arg += eval(res[0] + (res[3] ? "" : '"')); + else if ((res = str.match(/^(')((?:[^']|'')*)('?)/))) + arg += res[2].replace("''", "'", "g"); + else break; - case "pentadactyl": - if ((res = str.match(/^()((?:[^\\\s"']|\\.)+)((?:\\$)?)/))) - arg += res[2].replace(/\\(.)/g, "$1"); - else if ((res = str.match(/^(")((?:[^\\"]|\\.)*)("?)/))) - arg += eval(res[0] + (res[3] ? "" : '"')); - else if ((res = str.match(/^(')((?:[^\\']|\\.)*)('?)/))) - arg += res[2].replace(/\\(.)/g, function (n0, n1) /[\\']/.test(n1) ? n1 : n0); - break; - - case "rc-ish": - if ((res = str.match = str.match(/^()((?:[^\\\s"']|\\.)+)((?:\\$)?)/))) - arg += res[2].replace(/\\(.)/g, "$1"); - else if ((res = str.match(/^(")((?:[^\\"]|\\.)*)("?)/))) - arg += eval(res[0] + (res[3] ? "" : '"')); - else if ((res = str.match(/^(')((?:[^']|'')*)('?)/))) - arg += res[2].replace("''", "'", "g"); - break; - - case "pythonesque": - if ((res = str.match = str.match(/^()((?:[^\\\s"']|\\.)+)((?:\\$)?)/))) - arg += res[2].replace(/\\(.)/g, "$1"); - else if ((res = str.match(/^(""")((?:.?.?[^"])*)((?:""")?)/))) - arg += res[2]; - else if ((res = str.match(/^(")((?:[^\\"]|\\.)*)("?)/))) - arg += eval(res[0] + (res[3] ? "" : '"')); - else if ((res = str.match(/^(')((?:[^\\']|\\.)*)('?)/))) - arg += res[2].replace(/\\(.)/g, function (n0, n1) /[\\']/.test(n1) ? n1 : n0); - break; - } - - if (!res) - break; if (!res[3]) quote = res[1]; if (!res[1]) @@ -1036,7 +1006,7 @@ const Commands = Module("commands", { completeOpt = completeOpt.substr(7); completeFunc = function () { try { - var completer = dactyl.eval(completeOpt); + var completer = dactyl.usereval(completeOpt); if (!callable(completer)) throw new TypeError("User-defined custom completer " + completeOpt.quote() + " is not a function"); diff --git a/common/content/completion.js b/common/content/completion.js index 03e1be23..be2a5ab6 100644 --- a/common/content/completion.js +++ b/common/content/completion.js @@ -296,6 +296,8 @@ const CompletionContext = Class("CompletionContext", { for (let i in Iterator(this.keys)) { let [k, v] = i; if (typeof v == "string" && /^[.[]/.test(v)) + // This is only allowed to be a simple accessor, and shouldn't + // reference any variables. Don't bother with eval context. v = Function("i", "return i" + v); if (typeof v == "function") res.__defineGetter__(k, function () Class.replaceProperty(this, k, v(this.item))); diff --git a/common/content/dactyl.js b/common/content/dactyl.js index 509c3725..93608912 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -298,7 +298,7 @@ const Dactyl = Module("dactyl", { services.get("subscriptLoader").loadSubScript(uri, context); }, - eval: function (str, context) { + usereval: function (str, context) { try { if (!context) context = userContext; @@ -323,10 +323,21 @@ const Dactyl = Module("dactyl", { } }, + /** + * Acts like the Function builtin, but the code executes in the + * userContext global. + */ + userfunc: function () { + return this.userEval( + "(function (" + + Array.slice(arguments, 0, -1).join(", ") + + ") { " + arguments[arguments.length - 1] + " })") + }, + // partial sixth level expression evaluation // TODO: what is that really needed for, and where could it be used? // Or should it be removed? (c) Viktor - // Better name? See other dactyl.eval() + // Better name? See other dactyl.usereval() // I agree, the name is confusing, and so is the // description --Kris evalExpression: function (string) { @@ -1360,14 +1371,9 @@ const Dactyl = Module("dactyl", { commands.add(["exe[cute]"], "Execute the argument as an Ex command", - // FIXME: this should evaluate each arg separately then join - // with " " before executing. - // E.g. :execute "source" io.getRCFile().path - // Need to fix commands.parseArgs which currently strips the quotes - // from quoted args function (args) { try { - let cmd = dactyl.eval(args.string); + let cmd = dactyl.usereval(args.string); dactyl.execute(cmd, null, true); } catch (e) { @@ -1598,7 +1604,7 @@ const Dactyl = Module("dactyl", { }); commands.add(["javas[cript]", "js"], - "Run a JavaScript command through eval()", + "Evaluate a JavaScript string", function (args) { if (args.bang) { // open JavaScript console dactyl.open("chrome://global/content/console.xul", @@ -1606,7 +1612,7 @@ const Dactyl = Module("dactyl", { } else { try { - dactyl.eval(args.string); + dactyl.usereval(args.string); } catch (e) { dactyl.echoerr(e); @@ -1699,7 +1705,7 @@ const Dactyl = Module("dactyl", { if (args[0] == ":") var method = function () dactyl.execute(args, null, true); else - method = dactyl.eval("(function () {" + args + "})"); + method = dactyl.userfunction(args); try { if (count > 1) { diff --git a/common/content/javascript.js b/common/content/javascript.js index 40295627..18fa2719 100644 --- a/common/content/javascript.js +++ b/common/content/javascript.js @@ -59,8 +59,8 @@ const JavaScript = Module("javascript", { return completions; }, - eval: function evalstr(arg, key, tmp) { - let cache = this.context.cache.eval; + evalled: function evalled(arg, key, tmp) { + let cache = this.context.cache.evalled; let context = this.context.cache.evalContext; if (!key) @@ -70,7 +70,7 @@ const JavaScript = Module("javascript", { context[JavaScript.EVAL_TMP] = tmp; try { - return cache[key] = dactyl.eval(arg, context); + return cache[key] = dactyl.usereval(arg, context); } catch (e) { this.context.message = "Error: " + e; @@ -157,7 +157,7 @@ const JavaScript = Module("javascript", { let length = this._str.length; for (; this._i < length; this._lastChar = this._c, this._i++) { this._c = this._str[this._i]; - if (this._last == '"' || this._last == "'" || this._last == "/") { + if (/['"\/]/.test(this._last)) { if (this._lastChar == "\\") { // Escape. Skip the next char, whatever it may be. this._c = ""; this._i++; @@ -223,7 +223,7 @@ const JavaScript = Module("javascript", { // Don't eval any function calls unless the user presses tab. _checkFunction: function (start, end, key) { let res = this._functions.some(function (idx) idx >= start && idx < end); - if (!res || this.context.tabPressed || key in this.cache.eval) + if (!res || this.context.tabPressed || key in this.cache.evalled) return false; this.context.waitingForTab = true; return true; @@ -260,7 +260,7 @@ const JavaScript = Module("javascript", { } prev = dot + 1; - obj = this.eval(s, cacheKey, obj); + obj = this.evalled(s, cacheKey, obj); } return [[obj, cacheKey]]; }, @@ -378,7 +378,7 @@ const JavaScript = Module("javascript", { // After the opening [ upto the opening ", plus '' to take care of any operators before it let key = this._str.substring(this._get(-2, 0, "statements"), this._get(-1, null, "offset")) + "''"; // Now eval the key, to process any referenced variables. - return this.eval(key); + return this.evalled(key); }, get cache() this.context.cache, @@ -397,7 +397,7 @@ const JavaScript = Module("javascript", { return null; } - this.context.getCache("eval", Object); + this.context.getCache("evalled", Object); this.context.getCache("evalContext", function () ({ __proto__: userContext }));; // Okay, have parse stack. Figure out what we're completing. @@ -410,7 +410,7 @@ const JavaScript = Module("javascript", { let key = this._str.substring(prev, v + 1); if (this._checkFunction(prev, v, key)) return null; - this.eval(key); + this.evalled(key); prev = v + 1; } @@ -425,6 +425,8 @@ const JavaScript = Module("javascript", { // The top of the stack is the sting we're completing. // Wrap it in its delimiters and eval it to process escape sequences. let string = this._str.substring(this._get(-1).offset + 1, this._lastIdx); + // This is definitely a properly quoted string. + // Just eval it normally. string = eval(this._last + string + this._last); // Is this an object accessor? @@ -476,7 +478,7 @@ const JavaScript = Module("javascript", { for (let [i, idx] in Iterator(this._get(-2).comma)) { let arg = this._str.substring(prev + 1, idx); prev = idx; - util.memoize(args, i, function () self.eval(arg)); + util.memoize(args, i, function () self.evalled(arg)); } let key = this._getKey(); args.push(key + string);