diff --git a/content/completion.js b/content/completion.js index 9678ad10..1511d5a5 100644 --- a/content/completion.js +++ b/content/completion.js @@ -26,12 +26,6 @@ the provisions above, a recipient may use your version of this file under the terms of any one of the MPL, the GPL or the LGPL. }}} ***** END LICENSE BLOCK *****/ -// An eval with a cleaner lexical scope. -modules._cleanEval = function _cleanEval(__liberator_eval_arg, __liberator_eval_tmp) -{ - return window.eval(__liberator_eval_arg); -} - function CompletionContext(editor, name, offset) { if (!(this instanceof arguments.callee)) @@ -359,6 +353,13 @@ CompletionContext.prototype = { this._filter = this._filter.substr(count); }, + getCache: function (key, defVal) + { + if (!(key in this.cache)) + this.cache[key] = defVal(); + return this.cache[key]; + }, + getItems: function getItems(start, end) { let self = this; @@ -493,8 +494,6 @@ function Completion() //{{{ catch (e) {} const EVAL_TMP = "__liberator_eval_tmp"; - Javascript.cleanEval = _cleanEval; - delete modules._cleanEval; function Javascript() { @@ -571,11 +570,19 @@ function Completion() //{{{ // the wrappedJSObject instead, and return any keys // available in the object itself. let orig = obj; - if (obj.wrappedJSObject) - obj = obj.wrappedJSObject; + // v[0] in orig and orig[v[0]] catch different cases. XPCOM // objects are problematic, to say the least. - compl = [v for (v in this.iter(obj)) if (v[0] in orig || orig[v[0]] !== undefined)]; + if (obj.__proto__ == modules || obj.__proto__ == plugins) // Special case. + compl = [v for (v in Iterator(obj))]; + else + { + if (obj.wrappedJSObject) + obj = obj.wrappedJSObject; + compl = [v for (v in this.iter(obj)) + if ((typeof orig == "object" && v[0] in orig) || orig[v[0]] !== undefined)]; + } + // And if wrappedJSObject happens to be available, // return that, too. if (orig.wrappedJSObject) @@ -599,19 +606,27 @@ function Completion() //{{{ this.eval = function eval(arg, key, tmp) { let cache = this.context.cache.eval; + let context = this.context.cache.evalContext; + if (!key) key = arg; if (key in cache) return cache[key]; + context[EVAL_TMP] = tmp; try { - return cache[key] = Javascript.cleanEval(arg, tmp); + let res = liberator.eval(arg, context); + return res; } catch (e) { return null; } + finally + { + delete context[EVAL_TMP]; + } } /* Get an element from the stack. If @n is negative, @@ -773,8 +788,9 @@ function Completion() //{{{ return; } - if (!this.context.cache.eval) - this.context.cache.eval = {}; + let cache = this.context.cache; + this.context.getCache("eval", Object); + this.context.getCache("evalContext", function () ({ __proto__: modules })); /* Okay, have parse stack. Figure out what we're completing. */ @@ -794,7 +810,7 @@ function Completion() //{{{ function checkFunction(start, end, key) { let res = functions.some(function (idx) idx >= start && idx < end); - if (!res || self.context.tabPressed || key in self.context.cache.eval) + if (!res || self.context.tabPressed || key in cache.eval) return false; self.context.waitingForTab = true; return true; @@ -817,12 +833,13 @@ function Completion() //{{{ { if (dot < statement) continue; - if (dot > stop) + if (dot > stop || dot <= prev) break; let s = str.substring(prev, dot); if (prev != statement) s = EVAL_TMP + "." + s; + liberator.dump("s : " + s); cacheKey = str.substring(statement, dot); if (checkFunction(prev, dot, cacheKey)) @@ -841,7 +858,7 @@ function Completion() //{{{ let end = (frame == -1 ? lastIdx : get(frame + 1)[OFFSET]); cacheKey = null; - let obj = [[modules, "modules"], [window, "window"]]; // Default objects; + let obj = [[cache.evalContext, "Local Variables"], [modules, "modules"], [window, "window"]]; // Default objects; /* Is this an object dereference? */ if (dot < statement) // No. dot = statement - 1; @@ -1022,7 +1039,7 @@ function Completion() //{{{ let [offset, obj, key] = getObjKey(-1); // Wait for a keypress before completing the default objects. - if (!this.context.tabPressed && key == "" && obj.length == 2) + if (!this.context.tabPressed && key == "" && obj.length > 1) { this.context.waitingForTab = true; this.context.message = "Waiting for key press"; diff --git a/content/eval.js b/content/eval.js new file mode 100644 index 00000000..14f5fd95 --- /dev/null +++ b/content/eval.js @@ -0,0 +1,10 @@ +try { __liberator_eval_result = eval(__liberator_eval_string) +} +catch (e) +{ + __liberator_eval_error = e; +} +// Important: The eval statement *must* remain on the first line +// in order for line numbering in any errors to remain correct. +// +// vim: set fdm=marker sw=4 ts=4 et: diff --git a/content/io.js b/content/io.js index b6348958..77ac628e 100644 --- a/content/io.js +++ b/content/io.js @@ -822,11 +822,9 @@ lookup: // handle pure javascript files specially if (/\.js$/.test(filename)) { - let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Components.interfaces.mozIJSSubScriptLoader); try { - loader.loadSubScript(uri.spec, new Script(file.path)); + liberator.loadScript(uri.spec, new Script(file.path)); } catch (e) { diff --git a/content/liberator.js b/content/liberator.js index 15a8bd9f..56868f43 100644 --- a/content/liberator.js +++ b/content/liberator.js @@ -31,6 +31,13 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); const plugins = {}; plugins.__proto__ = modules; +const EVAL_ERROR = "__liberator_eval_error"; +const EVAL_RESULT = "__liberator_eval_result"; +const EVAL_STRING = "__liberator_eval_string"; +const userContext = { + __proto__: modules +}; + const liberator = (function () //{{{ { //////////////////////////////////////////////////////////////////////////////// @@ -705,29 +712,32 @@ const liberator = (function () //{{{ commandline.echo(str, commandline.HL_INFOMSG, flags); }, - eval: function (str) + loadScript: function (uri, context) + { + let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Components.interfaces.mozIJSSubScriptLoader); + loader.loadSubScript(uri, context); + }, + + eval: function (str, context) { - const fileName = "chrome://liberator/content/liberator.js"; - const line = new Error().lineNumber + 3; try { - return window.eval(str); + if (!context) + context = userContext; + context[EVAL_ERROR] = null; + context[EVAL_STRING] = str; + context[EVAL_RESULT] = null; + this.loadScript("chrome://liberator/content/eval.js", context); + if (context[EVAL_ERROR]) + throw context[EVAL_ERROR]; + return context[EVAL_RESULT]; } - catch (e) + finally { - if (e.fileName == fileName && e.lineNumber >= line) - { - e.source = str; - e.fileName = ""; - e.lineNumber -= line; - if (modules.io && io.sourcing) - { - liberator.dump(io.sourcing); - e.fileName = io.sourcing.file; - e.lineNumber += io.sourcing.line; - } - } - throw e; + delete context[EVAL_ERROR]; + delete context[EVAL_RESULT]; + delete context[EVAL_STRING]; } }, diff --git a/content/util.js b/content/util.js index e1bff7eb..8a0a021b 100644 --- a/content/util.js +++ b/content/util.js @@ -350,6 +350,11 @@ const util = { //{{{ try // window.content often does not want to be queried with "var i in object" { let hasValue = !("__iterator__" in object); + if (object.__proto__ == modules || object.__proto__ == plugins) + { + object = Iterator(object); + hasValue = false; + } for (let i in object) { let value = ]]>; @@ -365,6 +370,7 @@ const util = { //{{{ else var noVal = true; } + value = template.highlight(value, true, 150); // FIXME: Inline style. key = {i};