зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1246215
- Console prevents let re-declaration even when first was an error; r=jryans,shu
--HG-- extra : rebase_source : c911f3cbb74624b8e71297d975cd0aa3f055081b
This commit is contained in:
Родитель
1ccd72789b
Коммит
fd4ebed1e5
|
@ -19,6 +19,7 @@ loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webco
|
|||
loader.lazyRequireGetter(this, "events", "sdk/event/core");
|
||||
loader.lazyRequireGetter(this, "ServerLoggingListener", "devtools/shared/webconsole/server-logger", true);
|
||||
loader.lazyRequireGetter(this, "JSPropertyProvider", "devtools/shared/webconsole/js-property-provider", true);
|
||||
loader.lazyRequireGetter(this, "Parser", "resource://devtools/shared/Parser.jsm", true);
|
||||
|
||||
for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
|
||||
"ConsoleAPIListener", "addWebConsoleCommands",
|
||||
|
@ -1277,6 +1278,27 @@ WebConsoleActor.prototype =
|
|||
}
|
||||
else {
|
||||
result = dbgWindow.executeInGlobalWithBindings(aString, bindings, evalOptions);
|
||||
// Attempt to initialize any declarations found in the evaluated string
|
||||
// since they may now be stuck in an "initializing" state due to the
|
||||
// error. Already-initialized bindings will be ignored.
|
||||
if ("throw" in result) {
|
||||
let ast;
|
||||
// Parse errors will raise an exception. We can/should ignore the error
|
||||
// since it's already being handled elsewhere and we are only interested
|
||||
// in initializing bindings.
|
||||
try {
|
||||
ast = Parser.reflectionAPI.parse(aString);
|
||||
} catch (ex) {
|
||||
ast = {"body": []};
|
||||
}
|
||||
for (let line of ast.body) {
|
||||
if (line.type == "VariableDeclaration" &&
|
||||
(line.kind == "let" || line.kind == "const")) {
|
||||
for (let decl of line.declarations)
|
||||
dbgWindow.forceLexicalInitializationByName(decl.id.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let helperResult = helpers.helperResult;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft= javascript ts=2 et sw=2 tw=80: */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
|
|
@ -66,7 +66,8 @@ function onAttach(aState, aResponse)
|
|||
|
||||
let tests = [doSimpleEval, doWindowEval, doEvalWithException,
|
||||
doEvalWithHelper, doEvalString, doEvalLongString,
|
||||
doEvalWithBinding, doEvalWithBindingFrame].map(t => {
|
||||
doEvalWithBinding, doEvalWithBindingFrame,
|
||||
forceLexicalInit].map(t => {
|
||||
return Task.async(t);
|
||||
});
|
||||
|
||||
|
@ -223,6 +224,25 @@ function* doEvalWithBindingFrame() {
|
|||
nextTest()
|
||||
}
|
||||
|
||||
function* forceLexicalInit() {
|
||||
info("test `let x = SomeError` results in x being initialized to undefined");
|
||||
let response = yield evaluateJS("let foopie = wubbalubadubdub;");
|
||||
checkObject(response, {
|
||||
from: gState.actor,
|
||||
input: "let foopie = wubbalubadubdub;",
|
||||
result: undefined,
|
||||
});
|
||||
ok(response.exception, "expected exception");
|
||||
let response2 = yield evaluateJS("foopie;");
|
||||
checkObject(response2, {
|
||||
from: gState.actor,
|
||||
input: "foopie;",
|
||||
result: undefined,
|
||||
});
|
||||
ok(!response2.exception, "unexpected exception");
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function testEnd()
|
||||
{
|
||||
// If this is the first run, reload the page and do it again.
|
||||
|
|
|
@ -544,3 +544,7 @@ code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception.
|
|||
Debugger API: adapted portions of the code can use `Debugger.Object`
|
||||
instances, but use this method to pass direct object references to code
|
||||
that has not yet been updated.
|
||||
|
||||
<code>forceLexicalInitializationByName(<i>binding</i>)</code>
|
||||
: If <i>binding</i> is in an uninitialized state initialize it to undefined
|
||||
and return true, otherwise do nothing and return false.
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
load(libdir + "asserts.js");
|
||||
|
||||
var g = newGlobal();
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
|
||||
let errorOne, errorTwo;
|
||||
|
||||
function evalErrorStr(global, evalString) {
|
||||
try {
|
||||
global.evaluate(evalString);
|
||||
return undefined;
|
||||
} catch (e) {
|
||||
return e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
assertEq(evalErrorStr(g, "let y = IDONTEXIST;"), "ReferenceError: IDONTEXIST is not defined");
|
||||
|
||||
assertEq(evalErrorStr(g, "y = 1;"),
|
||||
"ReferenceError: can't access lexical declaration `y' before initialization");
|
||||
|
||||
// Here we flip the uninitialized binding to undfined.
|
||||
assertEq(gw.forceLexicalInitializationByName("y"), true);
|
||||
assertEq(g.evaluate("y"), undefined);
|
||||
g.evaluate("y = 1;");
|
||||
assertEq(g.evaluate("y"), 1);
|
||||
|
||||
// Ensure that bogus bindings return false, but otherwise trigger no error or
|
||||
// side effect.
|
||||
assertEq(gw.forceLexicalInitializationByName("idontexist"), false);
|
||||
assertEq(evalErrorStr(g, "idontexist"), "ReferenceError: idontexist is not defined");
|
||||
|
||||
// Ensure that only strings are accepted by forceLexicalInitializationByName
|
||||
const bad_types = [
|
||||
2112,
|
||||
{geddy: "lee"},
|
||||
() => 1,
|
||||
[],
|
||||
Array
|
||||
]
|
||||
|
||||
for (var badType of bad_types) {
|
||||
assertThrowsInstanceOf(() => {
|
||||
gw.forceLexicalInitializationByName(badType);
|
||||
}, TypeError);
|
||||
}
|
||||
|
||||
// Finally, supplying no arguments should throw a type error
|
||||
assertThrowsInstanceOf(() => {
|
||||
Debugger.isCompilableUnit();
|
||||
}, TypeError);
|
|
@ -4767,6 +4767,7 @@ Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Debugger::drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
@ -8032,6 +8033,48 @@ RequireGlobalObject(JSContext* cx, HandleValue dbgobj, HandleObject referent)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Lookup a binding on the referent's global scope and change it to undefined
|
||||
// if it is an uninitialized lexical, otherwise do nothing. The method's return
|
||||
// value is true _only_ when an uninitialized lexical has been altered, otherwise
|
||||
// it is false.
|
||||
bool
|
||||
DebuggerObject_forceLexicalInitializationByName(JSContext *cx, unsigned argc, Value* vp)
|
||||
{
|
||||
THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "forceLexicalInitializationByname", args, referent);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Object.prototype.forceLexicalInitializationByName", 1))
|
||||
return false;
|
||||
if (!RequireGlobalObject(cx, args.thisv(), referent))
|
||||
return false;
|
||||
|
||||
RootedObject globalLexical(cx, &referent->as<GlobalObject>().lexicalScope());
|
||||
|
||||
if (!args[0].isString()) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_NOT_EXPECTED_TYPE, "Debugger.Object.prototype.forceLexicalInitializationByName",
|
||||
"string", InformalValueTypeName(args[0]));
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyName* name = args[0].toString()->asAtom().asPropertyName();
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
Shape* s = nullptr;
|
||||
JSObject* scope = nullptr;
|
||||
JSObject* pobj = nullptr;
|
||||
|
||||
if (LookupNameNoGC(cx, name, globalLexical, &scope, &pobj, &s)) {
|
||||
Value v = globalLexical->as<NativeObject>().getSlot(s->slot());
|
||||
if (s->hasSlot() && v.isMagic() && v.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
|
||||
globalLexical->as<NativeObject>().setSlot(s->slot(), UndefinedValue());
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
args.rval().setBoolean(initialized);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
@ -8151,6 +8194,7 @@ static const JSFunctionSpec DebuggerObject_methods[] = {
|
|||
JS_FN("freeze", DebuggerObject_freeze, 0, 0),
|
||||
JS_FN("preventExtensions", DebuggerObject_preventExtensions, 0, 0),
|
||||
JS_FN("isSealed", DebuggerObject_isSealed, 0, 0),
|
||||
JS_FN("forceLexicalInitializationByName", DebuggerObject_forceLexicalInitializationByName, 1, 0),
|
||||
JS_FN("isFrozen", DebuggerObject_isFrozen, 0, 0),
|
||||
JS_FN("isExtensible", DebuggerObject_isExtensible, 0, 0),
|
||||
JS_FN("apply", DebuggerObject_apply, 0, 0),
|
||||
|
|
Загрузка…
Ссылка в новой задаче