Bug 1223007 - Fix eval redeclaration check for Debugger.Frame.eval. (r=efaust)

This commit is contained in:
Shu-yu Guo 2015-11-12 17:01:34 -08:00
parent 09a5c6135d
commit a064648b31
4 changed files with 180 additions and 134 deletions

View 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");

View File

@ -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)

View File

@ -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

View File

@ -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);