mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1223007 - Fix eval redeclaration check for Debugger.Frame.eval. (r=efaust)
This commit is contained in:
parent
09a5c6135d
commit
a064648b31
19
js/src/jit-test/tests/debug/Frame-eval-30.js
Normal file
19
js/src/jit-test/tests/debug/Frame-eval-30.js
Normal file
@ -0,0 +1,19 @@
|
||||
// Test that Debugger.Frame.eval correctly throws on redeclaration.
|
||||
|
||||
load(libdir + "evalInFrame.js");
|
||||
|
||||
let x;
|
||||
|
||||
function f() {
|
||||
evalInFrame(1, "var x;");
|
||||
}
|
||||
|
||||
var log = "";
|
||||
|
||||
try {
|
||||
f();
|
||||
} catch (e) {
|
||||
log += "e";
|
||||
}
|
||||
|
||||
assertEq(log, "e");
|
@ -328,140 +328,6 @@ SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject s
|
||||
return ok && result.checkStrictErrorOrWarning(cx, scope, id, strict);
|
||||
}
|
||||
|
||||
inline bool
|
||||
CheckLexicalNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandleObject varObj, HandlePropertyName name)
|
||||
{
|
||||
mozilla::Maybe<frontend::Definition::Kind> redeclKind;
|
||||
RootedId id(cx, NameToId(name));
|
||||
RootedShape shape(cx);
|
||||
if ((shape = lexicalScope->lookup(cx, name))) {
|
||||
redeclKind = mozilla::Some(shape->writable() ? frontend::Definition::LET
|
||||
: frontend::Definition::CONSTANT);
|
||||
} else if (varObj->isNative() && (shape = varObj->as<NativeObject>().lookup(cx, name))) {
|
||||
if (!shape->configurable())
|
||||
redeclKind = mozilla::Some(frontend::Definition::VAR);
|
||||
} else {
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!GetOwnPropertyDescriptor(cx, varObj, id, &desc))
|
||||
return false;
|
||||
if (desc.object() && desc.hasConfigurable() && !desc.configurable())
|
||||
redeclKind = mozilla::Some(frontend::Definition::VAR);
|
||||
}
|
||||
|
||||
if (redeclKind.isSome()) {
|
||||
ReportRuntimeRedeclaration(cx, name, *redeclKind);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
CheckVarNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandlePropertyName name)
|
||||
{
|
||||
if (Shape* shape = lexicalScope->lookup(cx, name)) {
|
||||
ReportRuntimeRedeclaration(cx, name, shape->writable() ? frontend::Definition::LET
|
||||
: frontend::Definition::CONSTANT);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
CheckVarNameConflict(JSContext* cx, Handle<CallObject*> callObj, HandlePropertyName name)
|
||||
{
|
||||
RootedFunction fun(cx, &callObj->callee());
|
||||
RootedScript script(cx, fun->nonLazyScript());
|
||||
uint32_t bodyLevelLexicalsStart = script->bindings.numVars();
|
||||
|
||||
for (BindingIter bi(script); !bi.done(); bi++) {
|
||||
if (name == bi->name() &&
|
||||
bi.isBodyLevelLexical() &&
|
||||
bi.localIndex() >= bodyLevelLexicalsStart)
|
||||
{
|
||||
ReportRuntimeRedeclaration(cx, name,
|
||||
bi->kind() == Binding::CONSTANT
|
||||
? frontend::Definition::CONSTANT
|
||||
: frontend::Definition::LET);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
CheckGlobalDeclarationConflicts(JSContext* cx, HandleScript script,
|
||||
Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandleObject varObj)
|
||||
{
|
||||
// Due to the extensibility of the global lexical scope, we must check for
|
||||
// redeclaring a binding.
|
||||
//
|
||||
// In the case of non-syntactic scope chains, we are checking
|
||||
// redeclarations against the non-syntactic lexical scope and the
|
||||
// variables object that the lexical scope corresponds to.
|
||||
RootedPropertyName name(cx);
|
||||
BindingIter bi(script);
|
||||
|
||||
for (uint32_t i = 0; i < script->bindings.numVars(); i++, bi++) {
|
||||
name = bi->name();
|
||||
if (!CheckVarNameConflict(cx, lexicalScope, name))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < script->bindings.numBodyLevelLexicals(); i++, bi++) {
|
||||
name = bi->name();
|
||||
if (!CheckLexicalNameConflict(cx, lexicalScope, varObj, name))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
|
||||
HandleObject scopeChain, HandleObject varObj)
|
||||
{
|
||||
// We don't need to check body-level lexical bindings for conflict. Eval
|
||||
// scripts always execute under their own lexical scope.
|
||||
if (script->bindings.numVars() == 0)
|
||||
return true;
|
||||
|
||||
RootedPropertyName name(cx);
|
||||
RootedObject obj(cx, scopeChain);
|
||||
Rooted<ClonedBlockObject*> lexicalScope(cx);
|
||||
|
||||
// ES6 18.2.1.2 step d
|
||||
//
|
||||
// Check that a direct eval will not hoist 'var' bindings over lexical
|
||||
// bindings with the same name.
|
||||
while (obj != varObj) {
|
||||
if (obj->is<ClonedBlockObject>()) {
|
||||
lexicalScope = &obj->as<ClonedBlockObject>();
|
||||
for (BindingIter bi(script); !bi.done(); bi++) {
|
||||
name = bi->name();
|
||||
if (!CheckVarNameConflict(cx, lexicalScope, name))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
obj = obj->enclosingScope();
|
||||
}
|
||||
|
||||
if (varObj->is<CallObject>()) {
|
||||
Rooted<CallObject*> callObj(cx, &varObj->as<CallObject>());
|
||||
for (BindingIter bi(script); !bi.done(); bi++) {
|
||||
name = bi->name();
|
||||
if (!CheckVarNameConflict(cx, callObj, name))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
DefLexicalOperation(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandleObject varObj, HandlePropertyName name, unsigned attrs)
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "builtin/ModuleObject.h"
|
||||
|
||||
#include "frontend/ParseNode.h"
|
||||
|
||||
#include "vm/ArgumentsObject.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/ProxyObject.h"
|
||||
@ -2923,6 +2925,152 @@ js::StaticScopeChainLength(JSObject* staticScope)
|
||||
return length;
|
||||
}
|
||||
|
||||
bool
|
||||
js::CheckLexicalNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandleObject varObj, HandlePropertyName name)
|
||||
{
|
||||
mozilla::Maybe<frontend::Definition::Kind> redeclKind;
|
||||
RootedId id(cx, NameToId(name));
|
||||
RootedShape shape(cx);
|
||||
if ((shape = lexicalScope->lookup(cx, name))) {
|
||||
redeclKind = mozilla::Some(shape->writable() ? frontend::Definition::LET
|
||||
: frontend::Definition::CONSTANT);
|
||||
} else if (varObj->isNative() && (shape = varObj->as<NativeObject>().lookup(cx, name))) {
|
||||
if (!shape->configurable())
|
||||
redeclKind = mozilla::Some(frontend::Definition::VAR);
|
||||
} else {
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!GetOwnPropertyDescriptor(cx, varObj, id, &desc))
|
||||
return false;
|
||||
if (desc.object() && desc.hasConfigurable() && !desc.configurable())
|
||||
redeclKind = mozilla::Some(frontend::Definition::VAR);
|
||||
}
|
||||
|
||||
if (redeclKind.isSome()) {
|
||||
ReportRuntimeRedeclaration(cx, name, *redeclKind);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::CheckVarNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandlePropertyName name)
|
||||
{
|
||||
if (Shape* shape = lexicalScope->lookup(cx, name)) {
|
||||
ReportRuntimeRedeclaration(cx, name, shape->writable() ? frontend::Definition::LET
|
||||
: frontend::Definition::CONSTANT);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckVarNameConflict(JSContext* cx, Handle<CallObject*> callObj, HandlePropertyName name)
|
||||
{
|
||||
RootedFunction fun(cx, &callObj->callee());
|
||||
RootedScript script(cx, fun->nonLazyScript());
|
||||
uint32_t bodyLevelLexicalsStart = script->bindings.numVars();
|
||||
|
||||
for (BindingIter bi(script); !bi.done(); bi++) {
|
||||
if (name == bi->name() &&
|
||||
bi.isBodyLevelLexical() &&
|
||||
bi.localIndex() >= bodyLevelLexicalsStart)
|
||||
{
|
||||
ReportRuntimeRedeclaration(cx, name,
|
||||
bi->kind() == Binding::CONSTANT
|
||||
? frontend::Definition::CONSTANT
|
||||
: frontend::Definition::LET);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::CheckGlobalDeclarationConflicts(JSContext* cx, HandleScript script,
|
||||
Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandleObject varObj)
|
||||
{
|
||||
// Due to the extensibility of the global lexical scope, we must check for
|
||||
// redeclaring a binding.
|
||||
//
|
||||
// In the case of non-syntactic scope chains, we are checking
|
||||
// redeclarations against the non-syntactic lexical scope and the
|
||||
// variables object that the lexical scope corresponds to.
|
||||
RootedPropertyName name(cx);
|
||||
BindingIter bi(script);
|
||||
|
||||
for (uint32_t i = 0; i < script->bindings.numVars(); i++, bi++) {
|
||||
name = bi->name();
|
||||
if (!CheckVarNameConflict(cx, lexicalScope, name))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < script->bindings.numBodyLevelLexicals(); i++, bi++) {
|
||||
name = bi->name();
|
||||
if (!CheckLexicalNameConflict(cx, lexicalScope, varObj, name))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class ScopeT>
|
||||
static bool
|
||||
CheckVarNameConflictsInScope(JSContext* cx, HandleScript script, HandleObject obj)
|
||||
{
|
||||
Rooted<ScopeT*> scope(cx);
|
||||
|
||||
// We return true when the scope object is not ScopeT below, because
|
||||
// ScopeT is either ClonedBlockObject or CallObject. No other scope
|
||||
// objects can contain lexical bindings, and there are no other overloads
|
||||
// for CheckVarNameConflict.
|
||||
|
||||
if (obj->is<ScopeT>())
|
||||
scope = &obj->as<ScopeT>();
|
||||
else if (obj->is<DebugScopeObject>() && obj->as<DebugScopeObject>().scope().is<ScopeT>())
|
||||
scope = &obj->as<DebugScopeObject>().scope().as<ScopeT>();
|
||||
else
|
||||
return true;
|
||||
|
||||
RootedPropertyName name(cx);
|
||||
|
||||
for (BindingIter bi(script); !bi.done(); bi++) {
|
||||
name = bi->name();
|
||||
if (!CheckVarNameConflict(cx, scope, name))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
|
||||
HandleObject scopeChain, HandleObject varObj)
|
||||
{
|
||||
// We don't need to check body-level lexical bindings for conflict. Eval
|
||||
// scripts always execute under their own lexical scope.
|
||||
if (script->bindings.numVars() == 0)
|
||||
return true;
|
||||
|
||||
RootedObject obj(cx, scopeChain);
|
||||
|
||||
// ES6 18.2.1.2 step d
|
||||
//
|
||||
// Check that a direct eval will not hoist 'var' bindings over lexical
|
||||
// bindings with the same name.
|
||||
while (obj != varObj) {
|
||||
if (!CheckVarNameConflictsInScope<ClonedBlockObject>(cx, script, obj))
|
||||
return false;
|
||||
obj = obj->enclosingScope();
|
||||
}
|
||||
|
||||
return CheckVarNameConflictsInScope<CallObject>(cx, script, varObj);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
void
|
||||
|
@ -1447,6 +1447,19 @@ CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
|
||||
bool HasNonSyntacticStaticScopeChain(JSObject* staticScope);
|
||||
uint32_t StaticScopeChainLength(JSObject* staticScope);
|
||||
|
||||
bool CheckVarNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandlePropertyName name);
|
||||
|
||||
bool CheckLexicalNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandleObject varObj, HandlePropertyName name);
|
||||
|
||||
bool CheckGlobalDeclarationConflicts(JSContext* cx, HandleScript script,
|
||||
Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandleObject varObj);
|
||||
|
||||
bool CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
|
||||
HandleObject scopeChain, HandleObject varObj);
|
||||
|
||||
#ifdef DEBUG
|
||||
void DumpStaticScopeChain(JSScript* script);
|
||||
void DumpStaticScopeChain(JSObject* staticScope);
|
||||
|
Loading…
Reference in New Issue
Block a user