Bug 1394490 - Support NSVOs with JSOP_FUNCTIONTHIS fallback
When a non-strict function is called with undefined |this|, we must
substitute in the global |this|. This patch fixes this behavior when a
NonSyntacticVariablesObject is on envChain.
MozReview-Commit-ID: C3oOVQQNhNa
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -1079,16 +1079,24 @@ IsExtensibleLexicalEnvironment(JSObject*
inline bool
IsGlobalLexicalEnvironment(JSObject* env)
{
return env->is<LexicalEnvironmentObject>() &&
env->as<LexicalEnvironmentObject>().isGlobal();
}
+inline bool
+IsNSVOLexicalEnvironment(JSObject* env)
+{
+ return env->is<LexicalEnvironmentObject>() &&
+ env->as<LexicalEnvironmentObject>().enclosingEnvironment()
+ .is<NonSyntacticVariablesObject>();
+}
+
inline JSObject*
MaybeUnwrapWithEnvironment(JSObject* env)
{
if (env->is<WithEnvironmentObject>())
return &env->as<WithEnvironmentObject>().object();
return env;
}
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -90,17 +90,17 @@ js::BoxNonStrictThis(JSContext* cx, Hand
{
/*
* Check for SynthesizeFrame poisoning and fast constructors which
* didn't check their callee properly.
*/
MOZ_ASSERT(!thisv.isMagic());
if (thisv.isNullOrUndefined()) {
- vp.set(GetThisValue(cx->global()));
+ vp.set(cx->global()->lexicalEnvironment().thisValue());
return true;
}
if (thisv.isObject()) {
vp.set(thisv);
return true;
}
@@ -122,16 +122,41 @@ js::GetFunctionThis(JSContext* cx, Abstr
frame.callee()->strict() ||
frame.callee()->isSelfHostedBuiltin())
{
res.set(frame.thisArgument());
return true;
}
RootedValue thisv(cx, frame.thisArgument());
+
+ // If there is a NSVO on environment chain, use it as basis for fallback
+ // global |this|. This gives a consistent definition of global lexical
+ // |this| between function and global contexts.
+ //
+ // NOTE: If only non-syntactic WithEnvironments are on the chain, we use
+ // the global lexical |this| value.
+ if (frame.script()->hasNonSyntacticScope() && thisv.isNullOrUndefined()) {
+ RootedObject env(cx, frame.environmentChain());
+ while (true) {
+ if (IsNSVOLexicalEnvironment(env) || IsGlobalLexicalEnvironment(env)) {
+ res.set(GetThisValue(env));
+ return true;
+ }
+ if (!env->enclosingEnvironment()) {
+ // This can only happen in Debugger eval frames: in that case we
+ // don't always have a global lexical env, see EvaluateInEnv.
+ MOZ_ASSERT(env->is<GlobalObject>());
+ res.set(GetThisValue(env));
+ return true;
+ }
+ env = env->enclosingEnvironment();
+ }
+ }
+
return BoxNonStrictThis(cx, thisv, res);
}
void
js::GetNonSyntacticGlobalThis(JSContext* cx, HandleObject envChain, MutableHandleValue res)
{
RootedObject env(cx, envChain);
while (true) {