Bug 1914895 - Make Symbol.unscopables support optional for non-syntactic with-environments. r=arai

In D220213 we tried to change `Symbol.unscopables` so that it's only supported on
syntactic `with` environments (for performance reasons). This failed some WPT tests
for event handlers because they use non-syntactic with-environments but have to
support `Symbol.unscopables`.

This patch adds a `JS::EnvironmentChain` class and uses it instead of an object vector
for some JS APIs. This class also lets the embedder specify whether `Symbol.unscopables`
must be supported. The `evaluate` shell function now has an option for this too.

Differential Revision: https://phabricator.services.mozilla.com/D226139
This commit is contained in:
Jan de Mooij
2024-10-21 17:56:43 +00:00
parent 5db868c0f5
commit 33eb26d6d2
26 changed files with 303 additions and 138 deletions

View File

@@ -21,6 +21,7 @@
#include "js/CallAndConstruct.h" // JS::IsCallable, JS_CallFunctionValue #include "js/CallAndConstruct.h" // JS::IsCallable, JS_CallFunctionValue
#include "js/CompilationAndEvaluation.h" #include "js/CompilationAndEvaluation.h"
#include "js/CompileOptions.h" #include "js/CompileOptions.h"
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/experimental/JSStencil.h" #include "js/experimental/JSStencil.h"
#include "js/GCVector.h" #include "js/GCVector.h"
#include "js/JSON.h" #include "js/JSON.h"
@@ -1212,7 +1213,7 @@ void nsMessageManagerScriptExecutor::LoadScriptInternal(
} }
} else { } else {
JS::Rooted<JS::Value> rval(cx); JS::Rooted<JS::Value> rval(cx);
JS::RootedVector<JSObject*> envChain(cx); JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
if (!envChain.append(aMessageManager)) { if (!envChain.append(aMessageManager)) {
return; return;
} }

View File

@@ -19,6 +19,7 @@
#include "js/CompilationAndEvaluation.h" #include "js/CompilationAndEvaluation.h"
#include "js/CompileOptions.h" #include "js/CompileOptions.h"
#include "js/Date.h" #include "js/Date.h"
#include "js/EnvironmentChain.h"
#include "js/GCVector.h" #include "js/GCVector.h"
#include "js/HeapAPI.h" #include "js/HeapAPI.h"
#include "js/Modules.h" #include "js/Modules.h"
@@ -80,7 +81,7 @@ nsresult nsJSUtils::UpdateFunctionDebugMetadata(
} }
nsresult nsJSUtils::CompileFunction(AutoJSAPI& jsapi, nsresult nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
JS::HandleVector<JSObject*> aScopeChain, const JS::EnvironmentChain& aEnvChain,
JS::CompileOptions& aOptions, JS::CompileOptions& aOptions,
const nsACString& aName, uint32_t aArgCount, const nsACString& aName, uint32_t aArgCount,
const char** aArgArray, const char** aArgArray,
@@ -88,12 +89,12 @@ nsresult nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
JSObject** aFunctionObject) { JSObject** aFunctionObject) {
JSContext* cx = jsapi.cx(); JSContext* cx = jsapi.cx();
MOZ_ASSERT(js::GetContextRealm(cx)); MOZ_ASSERT(js::GetContextRealm(cx));
MOZ_ASSERT_IF(aScopeChain.length() != 0, MOZ_ASSERT_IF(aEnvChain.length() != 0,
js::IsObjectInContextCompartment(aScopeChain[0], cx)); js::IsObjectInContextCompartment(aEnvChain.chain()[0], cx));
// Do the junk Gecko is supposed to do before calling into JSAPI. // Do the junk Gecko is supposed to do before calling into JSAPI.
for (size_t i = 0; i < aScopeChain.length(); ++i) { for (size_t i = 0; i < aEnvChain.length(); ++i) {
JS::ExposeObjectToActiveJS(aScopeChain[i]); JS::ExposeObjectToActiveJS(aEnvChain.chain()[i]);
} }
// Compile. // Compile.
@@ -106,7 +107,7 @@ nsresult nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
} }
JS::Rooted<JSFunction*> fun( JS::Rooted<JSFunction*> fun(
cx, JS::CompileFunction(cx, aScopeChain, aOptions, cx, JS::CompileFunction(cx, aEnvChain, aOptions,
PromiseFlatCString(aName).get(), aArgCount, PromiseFlatCString(aName).get(), aArgCount,
aArgArray, source)); aArgArray, source));
if (!fun) { if (!fun) {
@@ -122,14 +123,14 @@ bool nsJSUtils::IsScriptable(JS::Handle<JSObject*> aEvaluationGlobal) {
return xpc::Scriptability::AllowedIfExists(aEvaluationGlobal); return xpc::Scriptability::AllowedIfExists(aEvaluationGlobal);
} }
static bool AddScopeChainItem(JSContext* aCx, nsINode* aNode, static bool AddEnvChainItem(JSContext* aCx, nsINode* aNode,
JS::MutableHandleVector<JSObject*> aScopeChain) { JS::EnvironmentChain& aEnvChain) {
JS::Rooted<JS::Value> val(aCx); JS::Rooted<JS::Value> val(aCx);
if (!GetOrCreateDOMReflector(aCx, aNode, &val)) { if (!GetOrCreateDOMReflector(aCx, aNode, &val)) {
return false; return false;
} }
if (!aScopeChain.append(&val.toObject())) { if (!aEnvChain.append(&val.toObject())) {
return false; return false;
} }
@@ -137,11 +138,10 @@ static bool AddScopeChainItem(JSContext* aCx, nsINode* aNode,
} }
/* static */ /* static */
bool nsJSUtils::GetScopeChainForElement( bool nsJSUtils::GetEnvironmentChainForElement(JSContext* aCx, Element* aElement,
JSContext* aCx, Element* aElement, JS::EnvironmentChain& aEnvChain) {
JS::MutableHandleVector<JSObject*> aScopeChain) {
for (nsINode* cur = aElement; cur; cur = cur->GetScopeChainParent()) { for (nsINode* cur = aElement; cur; cur = cur->GetScopeChainParent()) {
if (!AddScopeChainItem(aCx, cur, aScopeChain)) { if (!AddEnvChainItem(aCx, cur, aEnvChain)) {
return false; return false;
} }
} }

View File

@@ -29,6 +29,10 @@ class nsIScriptElement;
class nsIScriptGlobalObject; class nsIScriptGlobalObject;
class nsXBLPrototypeBinding; class nsXBLPrototypeBinding;
namespace JS {
class JS_PUBLIC_API EnvironmentChain;
};
namespace mozilla { namespace mozilla {
union Utf8Unit; union Utf8Unit;
@@ -51,7 +55,7 @@ class nsJSUtils {
static uint64_t GetCurrentlyRunningCodeInnerWindowID(JSContext* aContext); static uint64_t GetCurrentlyRunningCodeInnerWindowID(JSContext* aContext);
static nsresult CompileFunction(mozilla::dom::AutoJSAPI& jsapi, static nsresult CompileFunction(mozilla::dom::AutoJSAPI& jsapi,
JS::HandleVector<JSObject*> aScopeChain, const JS::EnvironmentChain& aEnvChain,
JS::CompileOptions& aOptions, JS::CompileOptions& aOptions,
const nsACString& aName, uint32_t aArgCount, const nsACString& aName, uint32_t aArgCount,
const char** aArgArray, const char** aArgArray,
@@ -66,10 +70,10 @@ class nsJSUtils {
static bool IsScriptable(JS::Handle<JSObject*> aEvaluationGlobal); static bool IsScriptable(JS::Handle<JSObject*> aEvaluationGlobal);
// Returns false if an exception got thrown on aCx. Passing a null // Returns false if an exception got thrown on aCx. Passing a null
// aElement is allowed; that wil produce an empty aScopeChain. // aElement is allowed; that wil produce an empty aEnvChain.
static bool GetScopeChainForElement( static bool GetEnvironmentChainForElement(JSContext* aCx,
JSContext* aCx, mozilla::dom::Element* aElement, mozilla::dom::Element* aElement,
JS::MutableHandleVector<JSObject*> aScopeChain); JS::EnvironmentChain& aEnvChain);
static void ResetTimeZone(); static void ResetTimeZone();

View File

@@ -8,6 +8,7 @@
#undef CreateEvent #undef CreateEvent
#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/loader/LoadedScript.h" #include "js/loader/LoadedScript.h"
#include "js/loader/ScriptFetchOptions.h" #include "js/loader/ScriptFetchOptions.h"
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
@@ -1219,12 +1220,12 @@ nsresult EventListenerManager::CompileEventHandlerInternal(
JSAutoRealm ar(cx, target); JSAutoRealm ar(cx, target);
// Now that we've entered the realm we actually care about, create our // Now that we've entered the realm we actually care about, create our
// scope chain. Note that we start with |element|, not aElement, because // environment chain. Note that we start with |element|, not aElement,
// mTarget is different from aElement in the <body> case, where mTarget is a // because mTarget is different from aElement in the <body> case, where
// Window, and in that case we do not want the scope chain to include the body // mTarget is a Window, and in that case we do not want the environment chain
// or the document. // to include the body or the document.
JS::RootedVector<JSObject*> scopeChain(cx); JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::Yes);
if (!nsJSUtils::GetScopeChainForElement(cx, element, &scopeChain)) { if (!nsJSUtils::GetEnvironmentChainForElement(cx, element, envChain)) {
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }
@@ -1257,7 +1258,7 @@ nsresult EventListenerManager::CompileEventHandlerInternal(
.setDeferDebugMetadata(true); .setDeferDebugMetadata(true);
JS::Rooted<JSObject*> handler(cx); JS::Rooted<JSObject*> handler(cx);
result = nsJSUtils::CompileFunction(jsapi, scopeChain, options, result = nsJSUtils::CompileFunction(jsapi, envChain, options,
nsAtomCString(aTypeAtom), argCount, nsAtomCString(aTypeAtom), argCount,
argNames, *body, handler.address()); argNames, *body, handler.address());
NS_ENSURE_SUCCESS(result, result); NS_ENSURE_SUCCESS(result, result);

View File

@@ -27,6 +27,7 @@ union Utf8Unit;
namespace JS { namespace JS {
class JS_PUBLIC_API EnvironmentChain;
class JS_PUBLIC_API InstantiateOptions; class JS_PUBLIC_API InstantiateOptions;
class JS_PUBLIC_API ReadOnlyCompileOptions; class JS_PUBLIC_API ReadOnlyCompileOptions;
@@ -84,12 +85,12 @@ extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
* objects that should end up on the script's scope chain. * objects that should end up on the script's scope chain.
*/ */
extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx, extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
JS::HandleObjectVector envChain, const JS::EnvironmentChain& envChain,
JS::Handle<JSScript*> script, JS::Handle<JSScript*> script,
JS::MutableHandle<JS::Value> rval); JS::MutableHandle<JS::Value> rval);
extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx, extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
JS::HandleObjectVector envChain, const JS::EnvironmentChain& envChain,
JS::Handle<JSScript*> script); JS::Handle<JSScript*> script);
namespace JS { namespace JS {
@@ -108,7 +109,8 @@ extern JS_PUBLIC_API bool Evaluate(JSContext* cx,
* the global object on it; that's implicit. It needs to contain the other * the global object on it; that's implicit. It needs to contain the other
* objects that should end up on the script's scope chain. * objects that should end up on the script's scope chain.
*/ */
extern JS_PUBLIC_API bool Evaluate(JSContext* cx, HandleObjectVector envChain, extern JS_PUBLIC_API bool Evaluate(JSContext* cx,
const JS::EnvironmentChain& envChain,
const ReadOnlyCompileOptions& options, const ReadOnlyCompileOptions& options,
SourceText<char16_t>& srcBuf, SourceText<char16_t>& srcBuf,
MutableHandle<Value> rval); MutableHandle<Value> rval);
@@ -173,7 +175,7 @@ extern JS_PUBLIC_API JSScript* CompileUtf8Path(
* global must not be explicitly included in the scope chain. * global must not be explicitly included in the scope chain.
*/ */
extern JS_PUBLIC_API JSFunction* CompileFunction( extern JS_PUBLIC_API JSFunction* CompileFunction(
JSContext* cx, HandleObjectVector envChain, JSContext* cx, const JS::EnvironmentChain& envChain,
const ReadOnlyCompileOptions& options, const char* name, unsigned nargs, const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
const char* const* argnames, SourceText<char16_t>& srcBuf); const char* const* argnames, SourceText<char16_t>& srcBuf);
@@ -185,7 +187,7 @@ extern JS_PUBLIC_API JSFunction* CompileFunction(
* global must not be explicitly included in the scope chain. * global must not be explicitly included in the scope chain.
*/ */
extern JS_PUBLIC_API JSFunction* CompileFunction( extern JS_PUBLIC_API JSFunction* CompileFunction(
JSContext* cx, HandleObjectVector envChain, JSContext* cx, const JS::EnvironmentChain& envChain,
const ReadOnlyCompileOptions& options, const char* name, unsigned nargs, const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
const char* const* argnames, SourceText<mozilla::Utf8Unit>& srcBuf); const char* const* argnames, SourceText<mozilla::Utf8Unit>& srcBuf);
@@ -194,7 +196,7 @@ extern JS_PUBLIC_API JSFunction* CompileFunction(
* Rust-friendly ergonomics. * Rust-friendly ergonomics.
*/ */
extern JS_PUBLIC_API JSFunction* CompileFunctionUtf8( extern JS_PUBLIC_API JSFunction* CompileFunctionUtf8(
JSContext* cx, HandleObjectVector envChain, JSContext* cx, const JS::EnvironmentChain& envChain,
const ReadOnlyCompileOptions& options, const char* name, unsigned nargs, const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
const char* const* argnames, const char* utf8, size_t length); const char* const* argnames, const char* utf8, size_t length);

View File

@@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef js_EnvironmentChain_h
#define js_EnvironmentChain_h
#include "mozilla/Attributes.h" // MOZ_RAII
#include <stddef.h> // size_t
#include "jstypes.h" // JS_PUBLIC_API
#include "js/GCVector.h" // JS::RootedVector
struct JS_PUBLIC_API JSContext;
class JS_PUBLIC_API JSObject;
namespace JS {
enum class SupportUnscopables : bool { No = false, Yes = true };
/**
* JS::EnvironmentChain stores a list of objects to put on the environment
* chain.
*
* Internally the engine will create a non-syntactic 'with' environment for each
* of these objects. Note that 'with' environments aren't optimized well so you
* should use this class only if you really have to.
*
* The SupportUnscopables enum class controls whether these non-syntactic 'with'
* environments support Symbol.unscopables similar to syntactic 'with'
* statements in JS.
*
* Passing SupportUnscopables::No is better for performance because it lets us
* skip the Symbol.unscopables property lookup. Some Web APIs require supporting
* Symbol.unscopables though. In Firefox, SupportUnscopables::Yes is used for
* event handlers.
*/
class MOZ_RAII JS_PUBLIC_API EnvironmentChain {
JS::RootedObjectVector chain_;
SupportUnscopables supportUnscopables_;
public:
EnvironmentChain(JSContext* cx, SupportUnscopables supportUnscopables)
: chain_(cx), supportUnscopables_(supportUnscopables) {}
EnvironmentChain(const EnvironmentChain&) = delete;
void operator=(const EnvironmentChain&) = delete;
[[nodiscard]] bool append(JSObject* obj) { return chain_.append(obj); }
bool empty() const { return chain_.empty(); }
size_t length() const { return chain_.length(); }
RootedObjectVector& chain() { return chain_; }
const RootedObjectVector& chain() const { return chain_; }
void setSupportUnscopables(SupportUnscopables supportUnscopables) {
supportUnscopables_ = supportUnscopables;
}
SupportUnscopables supportUnscopables() const { return supportUnscopables_; }
};
} // namespace JS
#endif /* js_EnvironmentChain_h */

View File

@@ -45,6 +45,8 @@
namespace JS { namespace JS {
class JS_PUBLIC_API EnvironmentChain;
/** /**
* Allocate a new environment in the current compartment that is compatible with * Allocate a new environment in the current compartment that is compatible with
* JSM shared loading. * JSM shared loading.
@@ -68,7 +70,7 @@ extern JS_PUBLIC_API bool ExecuteInJSMEnvironment(JSContext* cx,
// temporarily placed on the environment chain. // temporarily placed on the environment chain.
extern JS_PUBLIC_API bool ExecuteInJSMEnvironment( extern JS_PUBLIC_API bool ExecuteInJSMEnvironment(
JSContext* cx, Handle<JSScript*> script, Handle<JSObject*> jsmEnv, JSContext* cx, Handle<JSScript*> script, Handle<JSObject*> jsmEnv,
Handle<StackGCVector<JSObject*>> targetObj); const EnvironmentChain& targetObj);
// Used by native methods to determine the JSMEnvironment of caller if possible // Used by native methods to determine the JSMEnvironment of caller if possible
// by looking at stack frames. Returns nullptr if top frame isn't a scripted // by looking at stack frames. Returns nullptr if top frame isn't a scripted

View File

@@ -12,6 +12,7 @@
#include "frontend/BytecodeCompiler.h" // frontend::CompileEvalScript #include "frontend/BytecodeCompiler.h" // frontend::CompileEvalScript
#include "gc/HashUtil.h" #include "gc/HashUtil.h"
#include "js/CompilationAndEvaluation.h" #include "js/CompilationAndEvaluation.h"
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/friend/JSMEnvironment.h" // JS::NewJSMEnvironment, JS::ExecuteInJSMEnvironment, JS::GetJSMEnvironmentOfScriptedCaller, JS::IsJSMEnvironment #include "js/friend/JSMEnvironment.h" // JS::NewJSMEnvironment, JS::ExecuteInJSMEnvironment, JS::GetJSMEnvironmentOfScriptedCaller, JS::IsJSMEnvironment
#include "js/friend/WindowProxy.h" // js::IsWindowProxy #include "js/friend/WindowProxy.h" // js::IsWindowProxy
@@ -410,7 +411,7 @@ JS_PUBLIC_API bool js::ExecuteInFrameScriptEnvironment(
return false; return false;
} }
RootedObjectVector envChain(cx); JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
if (!envChain.append(objArg)) { if (!envChain.append(objArg)) {
return false; return false;
} }
@@ -461,14 +462,13 @@ JS_PUBLIC_API JSObject* JS::NewJSMEnvironment(JSContext* cx) {
JS_PUBLIC_API bool JS::ExecuteInJSMEnvironment(JSContext* cx, JS_PUBLIC_API bool JS::ExecuteInJSMEnvironment(JSContext* cx,
HandleScript scriptArg, HandleScript scriptArg,
HandleObject varEnv) { HandleObject varEnv) {
RootedObjectVector emptyChain(cx); JS::EnvironmentChain emptyChain(cx, JS::SupportUnscopables::No);
return ExecuteInJSMEnvironment(cx, scriptArg, varEnv, emptyChain); return ExecuteInJSMEnvironment(cx, scriptArg, varEnv, emptyChain);
} }
JS_PUBLIC_API bool JS::ExecuteInJSMEnvironment(JSContext* cx, JS_PUBLIC_API bool JS::ExecuteInJSMEnvironment(
HandleScript scriptArg, JSContext* cx, HandleScript scriptArg, HandleObject varEnv,
HandleObject varEnv, const EnvironmentChain& targetObj) {
HandleObjectVector targetObj) {
cx->check(varEnv); cx->check(varEnv);
MOZ_ASSERT( MOZ_ASSERT(
ObjectRealm::get(varEnv).getNonSyntacticLexicalEnvironment(varEnv)); ObjectRealm::get(varEnv).getNonSyntacticLexicalEnvironment(varEnv));

View File

@@ -41,6 +41,7 @@
#include "jit/JSJitFrameIter.h" // for InlineFrameIterator #include "jit/JSJitFrameIter.h" // for InlineFrameIterator
#include "jit/RematerializedFrame.h" // for RematerializedFrame #include "jit/RematerializedFrame.h" // for RematerializedFrame
#include "js/CallArgs.h" // for CallArgs #include "js/CallArgs.h" // for CallArgs
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/friend/ErrorMessages.h" // for GetErrorMessage, JSMSG_* #include "js/friend/ErrorMessages.h" // for GetErrorMessage, JSMSG_*
#include "js/GCVector.h" // for JS::StackGCVector #include "js/GCVector.h" // for JS::StackGCVector
#include "js/Object.h" // for SetReservedSlot #include "js/Object.h" // for SetReservedSlot
@@ -954,7 +955,7 @@ static WithEnvironmentObject* CreateBindingsEnv(
} }
} }
RootedObjectVector envChain(cx); JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
if (!envChain.append(bindingsObj)) { if (!envChain.append(bindingsObj)) {
return nullptr; return nullptr;
} }

View File

@@ -28,6 +28,7 @@
#include "js/AllocPolicy.h" // js::SystemAllocPolicy, ReportOutOfMemory #include "js/AllocPolicy.h" // js::SystemAllocPolicy, ReportOutOfMemory
#include "js/CharacterEncoding.h" // JS_EncodeStringToUTF8 #include "js/CharacterEncoding.h" // JS_EncodeStringToUTF8
#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
#include "js/EnvironmentChain.h" // JS::SupportUnscopables
#include "js/ErrorReport.h" // JS_ReportErrorASCII #include "js/ErrorReport.h" // JS_ReportErrorASCII
#include "js/experimental/JSStencil.h" #include "js/experimental/JSStencil.h"
#include "js/GCVector.h" // JS::StackGCVector #include "js/GCVector.h" // JS::StackGCVector
@@ -612,8 +613,8 @@ static WithEnvironmentObject* CreateExtraBindingsEnvironment(
} }
JS::Rooted<JSObject*> globalLexical(cx, &cx->global()->lexicalEnvironment()); JS::Rooted<JSObject*> globalLexical(cx, &cx->global()->lexicalEnvironment());
return WithEnvironmentObject::createNonSyntactic(cx, extraBindingsObj, return WithEnvironmentObject::createNonSyntactic(
globalLexical); cx, extraBindingsObj, globalLexical, JS::SupportUnscopables::No);
} }
JSScript* frontend::CompileGlobalScriptWithExtraBindings( JSScript* frontend::CompileGlobalScriptWithExtraBindings(

View File

@@ -0,0 +1,15 @@
// Tests evaluate's supportUnscopables option.
function test(supportUnscopables) {
var env = {x: 1, y: 2};
Object.defineProperty(env, Symbol.unscopables, {get: function() {
assertEq(supportUnscopables, true);
return {x: false, y: true};
}});
evaluate(`this.gotX = x; try { this.gotY = y; } catch {}`,
{envChainObject: env, supportUnscopables});
assertEq(env.gotX, 1);
assertEq(env.gotY, supportUnscopables ? undefined : 2);
}
test(false);
test(true);

View File

@@ -9,6 +9,7 @@
#include "js/CallAndConstruct.h" // JS_CallFunctionValue #include "js/CallAndConstruct.h" // JS_CallFunctionValue
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction #include "js/CompilationAndEvaluation.h" // JS::CompileFunction
#include "js/ContextOptions.h" #include "js/ContextOptions.h"
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/GlobalObject.h" // JS_NewGlobalObject #include "js/GlobalObject.h" // JS_NewGlobalObject
#include "js/PropertyAndElement.h" // JS_DefineProperty #include "js/PropertyAndElement.h" // JS_DefineProperty
#include "js/SourceText.h" // JS::Source{Ownership,Text} #include "js/SourceText.h" // JS::Source{Ownership,Text}
@@ -87,8 +88,8 @@ BEGIN_TEST(testChromeBuffer) {
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
options.setFileAndLine("", 0); options.setFileAndLine("", 0);
JS::RootedObjectVector emptyScopeChain(cx); JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
fun = JS::CompileFunction(cx, emptyScopeChain, options, "trusted", 1, fun = JS::CompileFunction(cx, emptyEnvChain, options, "trusted", 1,
&paramName, srcBuf); &paramName, srcBuf);
CHECK(fun); CHECK(fun);
CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun, CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun,
@@ -118,8 +119,8 @@ BEGIN_TEST(testChromeBuffer) {
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
options.setFileAndLine("", 0); options.setFileAndLine("", 0);
JS::RootedObjectVector emptyScopeChain(cx); JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
fun = JS::CompileFunction(cx, emptyScopeChain, options, "untrusted", 1, fun = JS::CompileFunction(cx, emptyEnvChain, options, "untrusted", 1,
&paramName, srcBuf); &paramName, srcBuf);
CHECK(fun); CHECK(fun);
CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE)); CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE));
@@ -165,8 +166,8 @@ BEGIN_TEST(testChromeBuffer) {
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
options.setFileAndLine("", 0); options.setFileAndLine("", 0);
JS::RootedObjectVector emptyScopeChain(cx); JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
fun = JS::CompileFunction(cx, emptyScopeChain, options, "trusted", 1, fun = JS::CompileFunction(cx, emptyEnvChain, options, "trusted", 1,
&paramName, srcBuf); &paramName, srcBuf);
CHECK(fun); CHECK(fun);
CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun, CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun,
@@ -192,8 +193,8 @@ BEGIN_TEST(testChromeBuffer) {
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
options.setFileAndLine("", 0); options.setFileAndLine("", 0);
JS::RootedObjectVector emptyScopeChain(cx); JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
fun = JS::CompileFunction(cx, emptyScopeChain, options, "untrusted", 1, fun = JS::CompileFunction(cx, emptyEnvChain, options, "untrusted", 1,
&paramName, srcBuf); &paramName, srcBuf);
CHECK(fun); CHECK(fun);
CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE)); CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE));
@@ -226,8 +227,8 @@ BEGIN_TEST(testChromeBuffer) {
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
options.setFileAndLine("", 0); options.setFileAndLine("", 0);
JS::RootedObjectVector emptyScopeChain(cx); JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
fun = JS::CompileFunction(cx, emptyScopeChain, options, "trusted", 0, fun = JS::CompileFunction(cx, emptyEnvChain, options, "trusted", 0,
nullptr, srcBuf); nullptr, srcBuf);
CHECK(fun); CHECK(fun);
CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun, CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun,
@@ -254,8 +255,8 @@ BEGIN_TEST(testChromeBuffer) {
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
options.setFileAndLine("", 0); options.setFileAndLine("", 0);
JS::RootedObjectVector emptyScopeChain(cx); JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
fun = JS::CompileFunction(cx, emptyScopeChain, options, "untrusted", 1, fun = JS::CompileFunction(cx, emptyEnvChain, options, "untrusted", 1,
&paramName, srcBuf); &paramName, srcBuf);
CHECK(fun); CHECK(fun);
CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE)); CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE));

View File

@@ -11,6 +11,7 @@
#include "js/CallAndConstruct.h" #include "js/CallAndConstruct.h"
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction #include "js/CompilationAndEvaluation.h" // JS::CompileFunction
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/SourceText.h" // JS::Source{Ownership,Text} #include "js/SourceText.h" // JS::Source{Ownership,Text}
#include "jsapi-tests/tests.h" #include "jsapi-tests/tests.h"
#include "util/Text.h" #include "util/Text.h"
@@ -23,7 +24,7 @@ BEGIN_TEST(test_functionBinding) {
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
options.setFileAndLine(__FILE__, __LINE__); options.setFileAndLine(__FILE__, __LINE__);
JS::RootedObjectVector emptyScopeChain(cx); JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
// Named function shouldn't have it's binding. // Named function shouldn't have it's binding.
{ {
@@ -33,7 +34,7 @@ BEGIN_TEST(test_functionBinding) {
CHECK(srcBuf.init(cx, s1chars, js_strlen(s1chars), CHECK(srcBuf.init(cx, s1chars, js_strlen(s1chars),
JS::SourceOwnership::Borrowed)); JS::SourceOwnership::Borrowed));
fun = JS::CompileFunction(cx, emptyScopeChain, options, "s1", 0, nullptr, fun = JS::CompileFunction(cx, emptyEnvChain, options, "s1", 0, nullptr,
srcBuf); srcBuf);
CHECK(fun); CHECK(fun);
} }
@@ -52,7 +53,7 @@ BEGIN_TEST(test_functionBinding) {
CHECK(srcBuf.init(cx, s2chars, js_strlen(s2chars), CHECK(srcBuf.init(cx, s2chars, js_strlen(s2chars),
JS::SourceOwnership::Borrowed)); JS::SourceOwnership::Borrowed));
fun = JS::CompileFunction(cx, emptyScopeChain, options, "s2", 0, nullptr, fun = JS::CompileFunction(cx, emptyEnvChain, options, "s2", 0, nullptr,
srcBuf); srcBuf);
CHECK(fun); CHECK(fun);
} }
@@ -69,7 +70,7 @@ BEGIN_TEST(test_functionBinding) {
CHECK(srcBuf.init(cx, s3chars, js_strlen(s3chars), CHECK(srcBuf.init(cx, s3chars, js_strlen(s3chars),
JS::SourceOwnership::Borrowed)); JS::SourceOwnership::Borrowed));
fun = JS::CompileFunction(cx, emptyScopeChain, options, nullptr, 0, nullptr, fun = JS::CompileFunction(cx, emptyEnvChain, options, nullptr, 0, nullptr,
srcBuf); srcBuf);
CHECK(fun); CHECK(fun);
} }

View File

@@ -11,6 +11,7 @@
#include "js/CallAndConstruct.h" #include "js/CallAndConstruct.h"
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction #include "js/CompilationAndEvaluation.h" // JS::CompileFunction
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/PropertyAndElement.h" // JS_DefineProperty #include "js/PropertyAndElement.h" // JS_DefineProperty
#include "js/SourceText.h" // JS::Source{Ownership,Text} #include "js/SourceText.h" // JS::Source{Ownership,Text}
#include "jsapi-tests/tests.h" #include "jsapi-tests/tests.h"
@@ -22,7 +23,7 @@
using namespace js; using namespace js;
BEGIN_TEST(testFunctionNonSyntactic) { BEGIN_TEST(testFunctionNonSyntactic) {
JS::RootedObjectVector scopeChain(cx); JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
{ {
JS::RootedObject scopeObj(cx, JS_NewPlainObject(cx)); JS::RootedObject scopeObj(cx, JS_NewPlainObject(cx));
@@ -30,7 +31,7 @@ BEGIN_TEST(testFunctionNonSyntactic) {
JS::RootedValue val(cx); JS::RootedValue val(cx);
val.setNumber(1); val.setNumber(1);
CHECK(JS_DefineProperty(cx, scopeObj, "foo", val, JSPROP_ENUMERATE)); CHECK(JS_DefineProperty(cx, scopeObj, "foo", val, JSPROP_ENUMERATE));
CHECK(scopeChain.append(scopeObj)); CHECK(envChain.append(scopeObj));
} }
{ {
@@ -39,7 +40,7 @@ BEGIN_TEST(testFunctionNonSyntactic) {
JS::RootedValue val(cx); JS::RootedValue val(cx);
val.setNumber(20); val.setNumber(20);
CHECK(JS_DefineProperty(cx, scopeObj, "bar", val, JSPROP_ENUMERATE)); CHECK(JS_DefineProperty(cx, scopeObj, "bar", val, JSPROP_ENUMERATE));
CHECK(scopeChain.append(scopeObj)); CHECK(envChain.append(scopeObj));
} }
{ {
@@ -50,8 +51,8 @@ BEGIN_TEST(testFunctionNonSyntactic) {
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
options.setFileAndLine(__FILE__, __LINE__); options.setFileAndLine(__FILE__, __LINE__);
RootedFunction fun(cx, JS::CompileFunction(cx, scopeChain, options, "test", RootedFunction fun(cx, JS::CompileFunction(cx, envChain, options, "test", 0,
0, nullptr, srcBuf)); nullptr, srcBuf));
CHECK(fun); CHECK(fun);
CHECK(fun->enclosingScope()->kind() == ScopeKind::NonSyntactic); CHECK(fun->enclosingScope()->kind() == ScopeKind::NonSyntactic);
@@ -76,8 +77,8 @@ BEGIN_TEST(testFunctionNonSyntactic) {
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
options.setFileAndLine(__FILE__, __LINE__); options.setFileAndLine(__FILE__, __LINE__);
RootedFunction fun(cx, JS::CompileFunction(cx, scopeChain, options, "test", RootedFunction fun(cx, JS::CompileFunction(cx, envChain, options, "test", 1,
1, args, srcBuf)); args, srcBuf));
CHECK(fun); CHECK(fun);
CHECK(fun->enclosingScope()->kind() == ScopeKind::NonSyntactic); CHECK(fun->enclosingScope()->kind() == ScopeKind::NonSyntactic);

View File

@@ -3,6 +3,7 @@
*/ */
#include "js/CompilationAndEvaluation.h" #include "js/CompilationAndEvaluation.h"
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/PropertyAndElement.h" // JS_AlreadyHasOwnProperty, JS_HasProperty #include "js/PropertyAndElement.h" // JS_AlreadyHasOwnProperty, JS_HasProperty
#include "js/SourceText.h" #include "js/SourceText.h"
#include "jsapi-tests/tests.h" #include "jsapi-tests/tests.h"
@@ -16,13 +17,13 @@ BEGIN_TEST(testJSEvaluateScript) {
JS::RootedValue retval(cx); JS::RootedValue retval(cx);
JS::CompileOptions opts(cx); JS::CompileOptions opts(cx);
JS::RootedObjectVector scopeChain(cx); JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
CHECK(scopeChain.append(obj)); CHECK(envChain.append(obj));
JS::SourceText<char16_t> srcBuf; JS::SourceText<char16_t> srcBuf;
CHECK(srcBuf.init(cx, src, js_strlen(src), JS::SourceOwnership::Borrowed)); CHECK(srcBuf.init(cx, src, js_strlen(src), JS::SourceOwnership::Borrowed));
CHECK(JS::Evaluate(cx, scopeChain, opts.setFileAndLine(__FILE__, __LINE__), CHECK(JS::Evaluate(cx, envChain, opts.setFileAndLine(__FILE__, __LINE__),
srcBuf, &retval)); srcBuf, &retval));
bool hasProp = true; bool hasProp = true;

View File

@@ -7,6 +7,7 @@
#include "jit/Ion.h" // js::jit::IsIonEnabled #include "jit/Ion.h" // js::jit::IsIonEnabled
#include "js/CallAndConstruct.h" // JS::CallFunction #include "js/CallAndConstruct.h" // JS::CallFunction
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction #include "js/CompilationAndEvaluation.h" // JS::CompileFunction
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/GlobalObject.h" // JS_NewGlobalObject #include "js/GlobalObject.h" // JS_NewGlobalObject
#include "js/SourceText.h" // JS::Source{Ownership,Text} #include "js/SourceText.h" // JS::Source{Ownership,Text}
#include "jsapi-tests/tests.h" #include "jsapi-tests/tests.h"
@@ -69,9 +70,9 @@ bool testPreserveJitCode(bool preserveJitCode, unsigned remainingIonScripts) {
options.setFileAndLine(__FILE__, 1); options.setFileAndLine(__FILE__, 1);
JS::RootedFunction fun(cx); JS::RootedFunction fun(cx);
JS::RootedObjectVector emptyScopeChain(cx); JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
fun = JS::CompileFunction(cx, emptyScopeChain, options, "f", 0, nullptr, fun =
srcBuf); JS::CompileFunction(cx, emptyEnvChain, options, "f", 0, nullptr, srcBuf);
CHECK(fun); CHECK(fun);
RootedValue value(cx); RootedValue value(cx);

View File

@@ -5,6 +5,7 @@
#include "mozilla/Utf8.h" // mozilla::Utf8Unit #include "mozilla/Utf8.h" // mozilla::Utf8Unit
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction, JS::Evaluate #include "js/CompilationAndEvaluation.h" // JS::CompileFunction, JS::Evaluate
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/GlobalObject.h" // JS_NewGlobalObject #include "js/GlobalObject.h" // JS_NewGlobalObject
#include "js/MemoryFunctions.h" #include "js/MemoryFunctions.h"
#include "js/SourceText.h" // JS::Source{Ownership,Text} #include "js/SourceText.h" // JS::Source{Ownership,Text}
@@ -41,13 +42,13 @@ BEGIN_TEST(testBug795104) {
CHECK(JS::Evaluate(cx, opts, srcBuf, &unused)); CHECK(JS::Evaluate(cx, opts, srcBuf, &unused));
JS::RootedFunction fun(cx); JS::RootedFunction fun(cx);
JS::RootedObjectVector emptyScopeChain(cx); JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
// But when compiling a function we don't want to use no-rval // But when compiling a function we don't want to use no-rval
// mode, since it's not supported for functions. // mode, since it's not supported for functions.
opts.setNoScriptRval(false); opts.setNoScriptRval(false);
fun = JS::CompileFunction(cx, emptyScopeChain, opts, "f", 0, nullptr, srcBuf); fun = JS::CompileFunction(cx, emptyEnvChain, opts, "f", 0, nullptr, srcBuf);
CHECK(fun); CHECK(fun);
JS_free(cx, s); JS_free(cx, s);

View File

@@ -11,6 +11,7 @@
#include "frontend/CompilationStencil.h" #include "frontend/CompilationStencil.h"
#include "js/CompilationAndEvaluation.h" #include "js/CompilationAndEvaluation.h"
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/experimental/CompileScript.h" #include "js/experimental/CompileScript.h"
#include "js/experimental/JSStencil.h" #include "js/experimental/JSStencil.h"
#include "js/Modules.h" #include "js/Modules.h"
@@ -136,11 +137,11 @@ BEGIN_TEST(testStencil_NonSyntactic) {
CHECK(obj); CHECK(obj);
CHECK(JS_SetProperty(cx, obj, "x", val)); CHECK(JS_SetProperty(cx, obj, "x", val));
JS::RootedObjectVector chain(cx); JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
CHECK(chain.append(obj)); CHECK(envChain.append(obj));
JS::RootedValue rval(cx); JS::RootedValue rval(cx);
CHECK(JS_ExecuteScript(cx, chain, script, &rval)); CHECK(JS_ExecuteScript(cx, envChain, script, &rval));
CHECK(rval.isNumber() && rval.toNumber() == 42); CHECK(rval.isNumber() && rval.toNumber() == 42);
return true; return true;

View File

@@ -136,6 +136,7 @@ EXPORTS.js += [
"../public/Conversions.h", "../public/Conversions.h",
"../public/Date.h", "../public/Date.h",
"../public/Debug.h", "../public/Debug.h",
"../public/EnvironmentChain.h",
"../public/Equality.h", "../public/Equality.h",
"../public/ErrorInterceptor.h", "../public/ErrorInterceptor.h",
"../public/ErrorReport.h", "../public/ErrorReport.h",

View File

@@ -125,6 +125,7 @@
#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions, JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::InstantiateOptions #include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions, JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::InstantiateOptions
#include "js/ContextOptions.h" // JS::ContextOptions{,Ref} #include "js/ContextOptions.h" // JS::ContextOptions{,Ref}
#include "js/Debug.h" // JS::dbg::ShouldAvoidSideEffects, JS::ExecutionTrace #include "js/Debug.h" // JS::dbg::ShouldAvoidSideEffects, JS::ExecutionTrace
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/Equality.h" // JS::SameValue #include "js/Equality.h" // JS::SameValue
#include "js/ErrorReport.h" // JS::PrintError #include "js/ErrorReport.h" // JS::PrintError
#include "js/Exception.h" // JS::StealPendingExceptionStack #include "js/Exception.h" // JS::StealPendingExceptionStack
@@ -2693,7 +2694,7 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
bool saveIncrementalBytecode = false; bool saveIncrementalBytecode = false;
bool execute = true; bool execute = true;
bool assertEqBytecode = false; bool assertEqBytecode = false;
JS::RootedObjectVector envChain(cx); JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
RootedObject callerGlobal(cx, cx->global()); RootedObject callerGlobal(cx, cx->global());
options.setIntroductionType("js shell evaluate") options.setIntroductionType("js shell evaluate")
@@ -2781,6 +2782,13 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
} }
} }
if (!JS_GetProperty(cx, opts, "supportUnscopables", &v)) {
return false;
}
if (!v.isUndefined()) {
envChain.setSupportUnscopables(JS::SupportUnscopables(ToBoolean(v)));
}
// We cannot load or save the bytecode if we have no object where the // We cannot load or save the bytecode if we have no object where the
// bytecode cache is stored. // bytecode cache is stored.
if (loadBytecode || saveIncrementalBytecode) { if (loadBytecode || saveIncrementalBytecode) {
@@ -2796,7 +2804,7 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
// Wrap the envChainObject list into target realm. // Wrap the envChainObject list into target realm.
JSAutoRealm ar(cx, global); JSAutoRealm ar(cx, global);
for (size_t i = 0; i < envChain.length(); ++i) { for (size_t i = 0; i < envChain.length(); ++i) {
if (!JS_WrapObject(cx, envChain[i])) { if (!JS_WrapObject(cx, envChain.chain()[i])) {
return false; return false;
} }
} }
@@ -9746,6 +9754,8 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
" envChainObject: object to put on the scope chain, with its fields added\n" " envChainObject: object to put on the scope chain, with its fields added\n"
" as var bindings, akin to how elements are added to the environment in\n" " as var bindings, akin to how elements are added to the environment in\n"
" event handlers in Gecko.\n" " event handlers in Gecko.\n"
" supportUnscopables: if true, support Symbol.unscopables lookups for\n"
" envChainObject, similar to (syntactic) with-statements.\n"
), ),
JS_FN_HELP("run", Run, 1, 0, JS_FN_HELP("run", Run, 1, 0,

View File

@@ -23,6 +23,7 @@
#include "frontend/Parser.h" // frontend::Parser, frontend::ParseGoal #include "frontend/Parser.h" // frontend::Parser, frontend::ParseGoal
#include "js/CharacterEncoding.h" // JS::UTF8Chars, JS::ConstUTF8CharsZ, JS::UTF8CharsToNewTwoByteCharsZ #include "js/CharacterEncoding.h" // JS::UTF8Chars, JS::ConstUTF8CharsZ, JS::UTF8CharsToNewTwoByteCharsZ
#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/experimental/JSStencil.h" // JS::Stencil #include "js/experimental/JSStencil.h" // JS::Stencil
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/RootingAPI.h" // JS::Rooted #include "js/RootingAPI.h" // JS::Rooted
@@ -299,7 +300,7 @@ class FunctionCompiler {
return funStr_.append(srcBuf.get(), srcBuf.length()); return funStr_.append(srcBuf.get(), srcBuf.length());
} }
JSFunction* finish(HandleObjectVector envChain, JSFunction* finish(const JS::EnvironmentChain& envChain,
const ReadOnlyCompileOptions& optionsArg) { const ReadOnlyCompileOptions& optionsArg) {
using js::frontend::FunctionSyntaxKind; using js::frontend::FunctionSyntaxKind;
@@ -381,7 +382,7 @@ class FunctionCompiler {
}; };
JS_PUBLIC_API JSFunction* JS::CompileFunction( JS_PUBLIC_API JSFunction* JS::CompileFunction(
JSContext* cx, HandleObjectVector envChain, JSContext* cx, const EnvironmentChain& envChain,
const ReadOnlyCompileOptions& options, const char* name, unsigned nargs, const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
const char* const* argnames, SourceText<char16_t>& srcBuf) { const char* const* argnames, SourceText<char16_t>& srcBuf) {
ManualReportFrontendContext fc(cx); ManualReportFrontendContext fc(cx);
@@ -397,7 +398,7 @@ JS_PUBLIC_API JSFunction* JS::CompileFunction(
} }
JS_PUBLIC_API JSFunction* JS::CompileFunction( JS_PUBLIC_API JSFunction* JS::CompileFunction(
JSContext* cx, HandleObjectVector envChain, JSContext* cx, const EnvironmentChain& envChain,
const ReadOnlyCompileOptions& options, const char* name, unsigned nargs, const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
const char* const* argnames, SourceText<Utf8Unit>& srcBuf) { const char* const* argnames, SourceText<Utf8Unit>& srcBuf) {
ManualReportFrontendContext fc(cx); ManualReportFrontendContext fc(cx);
@@ -413,7 +414,7 @@ JS_PUBLIC_API JSFunction* JS::CompileFunction(
} }
JS_PUBLIC_API JSFunction* JS::CompileFunctionUtf8( JS_PUBLIC_API JSFunction* JS::CompileFunctionUtf8(
JSContext* cx, HandleObjectVector envChain, JSContext* cx, const EnvironmentChain& envChain,
const ReadOnlyCompileOptions& options, const char* name, unsigned nargs, const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
const char* const* argnames, const char* bytes, size_t length) { const char* const* argnames, const char* bytes, size_t length) {
SourceText<Utf8Unit> srcBuf; SourceText<Utf8Unit> srcBuf;
@@ -495,7 +496,7 @@ MOZ_NEVER_INLINE static bool ExecuteScript(JSContext* cx, HandleObject envChain,
return Execute(cx, script, envChain, rval); return Execute(cx, script, envChain, rval);
} }
static bool ExecuteScript(JSContext* cx, HandleObjectVector envChain, static bool ExecuteScript(JSContext* cx, const JS::EnvironmentChain& envChain,
HandleScript script, MutableHandleValue rval) { HandleScript script, MutableHandleValue rval) {
RootedObject env(cx, CreateNonSyntacticEnvironmentChain(cx, envChain)); RootedObject env(cx, CreateNonSyntacticEnvironmentChain(cx, envChain));
if (!env) { if (!env) {
@@ -520,13 +521,14 @@ MOZ_NEVER_INLINE JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
} }
MOZ_NEVER_INLINE JS_PUBLIC_API bool JS_ExecuteScript( MOZ_NEVER_INLINE JS_PUBLIC_API bool JS_ExecuteScript(
JSContext* cx, HandleObjectVector envChain, HandleScript scriptArg, JSContext* cx, const JS::EnvironmentChain& envChain, HandleScript scriptArg,
MutableHandleValue rval) { MutableHandleValue rval) {
return ExecuteScript(cx, envChain, scriptArg, rval); return ExecuteScript(cx, envChain, scriptArg, rval);
} }
MOZ_NEVER_INLINE JS_PUBLIC_API bool JS_ExecuteScript( MOZ_NEVER_INLINE JS_PUBLIC_API bool JS_ExecuteScript(
JSContext* cx, HandleObjectVector envChain, HandleScript scriptArg) { JSContext* cx, const JS::EnvironmentChain& envChain,
HandleScript scriptArg) {
RootedValue rval(cx); RootedValue rval(cx);
return ExecuteScript(cx, envChain, scriptArg, &rval); return ExecuteScript(cx, envChain, scriptArg, &rval);
} }
@@ -576,7 +578,7 @@ JS_PUBLIC_API bool JS::Evaluate(JSContext* cx,
srcBuf, rval); srcBuf, rval);
} }
JS_PUBLIC_API bool JS::Evaluate(JSContext* cx, HandleObjectVector envChain, JS_PUBLIC_API bool JS::Evaluate(JSContext* cx, const EnvironmentChain& envChain,
const ReadOnlyCompileOptions& options, const ReadOnlyCompileOptions& options,
SourceText<char16_t>& srcBuf, SourceText<char16_t>& srcBuf,
MutableHandleValue rval) { MutableHandleValue rval) {

View File

@@ -10,6 +10,7 @@
#include "builtin/Array.h" #include "builtin/Array.h"
#include "builtin/ModuleObject.h" #include "builtin/ModuleObject.h"
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/Exception.h" #include "js/Exception.h"
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
@@ -717,10 +718,9 @@ JSObject* js::GetThisObject(JSObject* obj) {
return obj; return obj;
} }
WithEnvironmentObject* WithEnvironmentObject::create(JSContext* cx, WithEnvironmentObject* WithEnvironmentObject::create(
HandleObject object, JSContext* cx, HandleObject object, HandleObject enclosing,
HandleObject enclosing, Handle<WithScope*> scope, JS::SupportUnscopables supportUnscopables) {
Handle<WithScope*> scope) {
Rooted<SharedShape*> shape(cx, Rooted<SharedShape*> shape(cx,
EmptyEnvironmentShape<WithEnvironmentObject>(cx)); EmptyEnvironmentShape<WithEnvironmentObject>(cx));
if (!shape) { if (!shape) {
@@ -738,17 +738,22 @@ WithEnvironmentObject* WithEnvironmentObject::create(JSContext* cx,
obj->initReservedSlot(OBJECT_SLOT, ObjectValue(*object)); obj->initReservedSlot(OBJECT_SLOT, ObjectValue(*object));
obj->initReservedSlot(THIS_SLOT, ObjectValue(*thisObj)); obj->initReservedSlot(THIS_SLOT, ObjectValue(*thisObj));
if (scope) { if (scope) {
obj->initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope)); MOZ_ASSERT(supportUnscopables == JS::SupportUnscopables::Yes,
"with-statements must support Symbol.unscopables");
obj->initReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT,
PrivateGCThingValue(scope));
} else { } else {
obj->initReservedSlot(SCOPE_SLOT, NullValue()); Value v = BooleanValue(supportUnscopables == JS::SupportUnscopables::Yes);
obj->initReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT, v);
} }
return obj; return obj;
} }
WithEnvironmentObject* WithEnvironmentObject::createNonSyntactic( WithEnvironmentObject* WithEnvironmentObject::createNonSyntactic(
JSContext* cx, HandleObject object, HandleObject enclosing) { JSContext* cx, HandleObject object, HandleObject enclosing,
return create(cx, object, enclosing, nullptr); JS::SupportUnscopables supportUnscopables) {
return create(cx, object, enclosing, nullptr, supportUnscopables);
} }
static inline bool IsUnscopableDotName(JSContext* cx, HandleId id) { static inline bool IsUnscopableDotName(JSContext* cx, HandleId id) {
@@ -811,8 +816,9 @@ static bool with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
} }
if (propp->isFound()) { if (propp->isFound()) {
bool scopable; bool scopable = true;
if (!CheckUnscopables(cx, actual, id, &scopable)) { if (obj->as<WithEnvironmentObject>().supportUnscopables() &&
!CheckUnscopables(cx, actual, id, &scopable)) {
return false; return false;
} }
if (!scopable) { if (!scopable) {
@@ -840,7 +846,7 @@ static bool with_HasProperty(JSContext* cx, HandleObject obj, HandleId id,
if (!HasProperty(cx, actual, id, foundp)) { if (!HasProperty(cx, actual, id, foundp)) {
return false; return false;
} }
if (!*foundp) { if (!*foundp || !obj->as<WithEnvironmentObject>().supportUnscopables()) {
return true; return true;
} }
@@ -938,7 +944,7 @@ const JSClass NonSyntacticVariablesObject::class_ = {
}; };
NonSyntacticLexicalEnvironmentObject* js::CreateNonSyntacticEnvironmentChain( NonSyntacticLexicalEnvironmentObject* js::CreateNonSyntacticEnvironmentChain(
JSContext* cx, HandleObjectVector envChain) { JSContext* cx, const JS::EnvironmentChain& envChain) {
// Callers are responsible for segregating the NonSyntactic case from simple // Callers are responsible for segregating the NonSyntactic case from simple
// compilation cases. // compilation cases.
MOZ_RELEASE_ASSERT(!envChain.empty()); MOZ_RELEASE_ASSERT(!envChain.empty());
@@ -2437,9 +2443,12 @@ class DebugEnvironmentProxyHandler : public NurseryAllocableProxyHandler {
if (isWith) { if (isWith) {
size_t j = 0; size_t j = 0;
bool supportUnscopables =
env->as<WithEnvironmentObject>().supportUnscopables();
for (size_t i = 0; i < props.length(); i++) { for (size_t i = 0; i < props.length(); i++) {
bool inScope; bool inScope = true;
if (!CheckUnscopables(cx, env, props[i], &inScope)) { if (supportUnscopables &&
!CheckUnscopables(cx, env, props[i], &inScope)) {
return false; return false;
} }
if (inScope) { if (inScope) {
@@ -3429,13 +3438,14 @@ JSObject* js::GetDebugEnvironmentForGlobalLexicalEnvironment(JSContext* cx) {
} }
WithEnvironmentObject* js::CreateObjectsForEnvironmentChain( WithEnvironmentObject* js::CreateObjectsForEnvironmentChain(
JSContext* cx, HandleObjectVector chain, HandleObject terminatingEnv) { JSContext* cx, const JS::EnvironmentChain& chain,
HandleObject terminatingEnv) {
MOZ_ASSERT(!chain.empty()); MOZ_ASSERT(!chain.empty());
#ifdef DEBUG #ifdef DEBUG
for (size_t i = 0; i < chain.length(); ++i) { for (size_t i = 0; i < chain.length(); ++i) {
cx->check(chain[i]); cx->check(chain.chain()[i]);
MOZ_ASSERT(!chain[i]->isUnqualifiedVarObj()); MOZ_ASSERT(!chain.chain()[i]->isUnqualifiedVarObj());
} }
#endif #endif
@@ -3444,8 +3454,8 @@ WithEnvironmentObject* js::CreateObjectsForEnvironmentChain(
Rooted<WithEnvironmentObject*> withEnv(cx); Rooted<WithEnvironmentObject*> withEnv(cx);
RootedObject enclosingEnv(cx, terminatingEnv); RootedObject enclosingEnv(cx, terminatingEnv);
for (size_t i = chain.length(); i > 0;) { for (size_t i = chain.length(); i > 0;) {
withEnv = withEnv = WithEnvironmentObject::createNonSyntactic(
WithEnvironmentObject::createNonSyntactic(cx, chain[--i], enclosingEnv); cx, chain.chain()[--i], enclosingEnv, chain.supportUnscopables());
if (!withEnv) { if (!withEnv) {
return nullptr; return nullptr;
} }
@@ -3470,14 +3480,24 @@ JSObject* WithEnvironmentObject::withThis() const {
} }
bool WithEnvironmentObject::isSyntactic() const { bool WithEnvironmentObject::isSyntactic() const {
Value v = getReservedSlot(SCOPE_SLOT); Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT);
MOZ_ASSERT(v.isPrivateGCThing() || v.isNull()); MOZ_ASSERT(v.isPrivateGCThing() || v.isBoolean());
return v.isPrivateGCThing(); return v.isPrivateGCThing();
} }
bool WithEnvironmentObject::supportUnscopables() const {
if (isSyntactic()) {
return true;
}
Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT);
MOZ_ASSERT(v.isBoolean());
return v.isTrue();
}
WithScope& WithEnvironmentObject::scope() const { WithScope& WithEnvironmentObject::scope() const {
MOZ_ASSERT(isSyntactic()); MOZ_ASSERT(isSyntactic());
return *static_cast<WithScope*>(getReservedSlot(SCOPE_SLOT).toGCThing()); Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT);
return *static_cast<WithScope*>(v.toGCThing());
} }
ModuleEnvironmentObject* js::GetModuleEnvironmentForScript(JSScript* script) { ModuleEnvironmentObject* js::GetModuleEnvironmentForScript(JSScript* script) {

View File

@@ -20,6 +20,11 @@
#include "vm/Scope.h" #include "vm/Scope.h"
#include "vm/ScopeKind.h" // ScopeKind #include "vm/ScopeKind.h" // ScopeKind
namespace JS {
class JS_PUBLIC_API EnvironmentChain;
enum class SupportUnscopables : bool;
}; // namespace JS
namespace js { namespace js {
class AbstractGeneratorObject; class AbstractGeneratorObject;
@@ -175,6 +180,11 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* *
* Does not hold 'let' or 'const' bindings. * Does not hold 'let' or 'const' bindings.
* *
* The embedding can specify whether these non-syntactic WithEnvironment
* objects support Symbol.unscopables similar to syntactic 'with' statements
* in JS. In Firefox, we support Symbol.unscopables only for DOM event
* handlers because this is required by the spec.
*
* 2. NonSyntacticVariablesObject * 2. NonSyntacticVariablesObject
* *
* When the embedding wants qualified 'var' bindings and unqualified * When the embedding wants qualified 'var' bindings and unqualified
@@ -234,7 +244,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* GlobalLexicalEnvironmentObject[this=global] * GlobalLexicalEnvironmentObject[this=global]
* | * |
* WithEnvironmentObject wrapping target (qualified 'var's) * WithEnvironmentObject [SupportUnscopables=No] wrapping target
* (qualified 'var's)
* | * |
* NonSyntacticLexicalEnvironmentObject[this=target] (lexical vars) * NonSyntacticLexicalEnvironmentObject[this=target] (lexical vars)
* *
@@ -263,7 +274,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* NonSyntacticLexicalEnvironmentObject[this=nsvo] * NonSyntacticLexicalEnvironmentObject[this=nsvo]
* | * |
* WithEnvironmentObject wrapping target (qualified 'var's) * WithEnvironmentObject [SupportUnscopables=No] wrapping target
* (qualified 'var's)
* | * |
* NonSyntacticLexicalEnvironmentObject[this=target] (lexical vars) * NonSyntacticLexicalEnvironmentObject[this=target] (lexical vars)
* *
@@ -298,7 +310,7 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* NonSyntacticVariablesObject (qualified 'var's and unqualified names) * NonSyntacticVariablesObject (qualified 'var's and unqualified names)
* | * |
* WithEnvironmentObject wrapping messageManager * WithEnvironmentObject [SupportUnscopables=No] wrapping messageManager
* | * |
* NonSyntacticLexicalEnvironmentObject[this=messageManager] (lexical vars) * NonSyntacticLexicalEnvironmentObject[this=messageManager] (lexical vars)
* *
@@ -315,7 +327,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* GlobalLexicalEnvironmentObject[this=global] * GlobalLexicalEnvironmentObject[this=global]
* | * |
* WithEnvironmentObject wrapping messageManager (qualified 'var's) * WithEnvironmentObject [SupportUnscopables=No] wrapping messageManager
* (qualified 'var's)
* | * |
* NonSyntacticLexicalEnvironmentObject[this=messageManager] (lexical vars) * NonSyntacticLexicalEnvironmentObject[this=messageManager] (lexical vars)
* *
@@ -334,13 +347,13 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* GlobalLexicalEnvironmentObject[this=global] * GlobalLexicalEnvironmentObject[this=global]
* | * |
* WithEnvironmentObject wrapping eN * WithEnvironmentObject [SupportUnscopables=Yes] wrapping eN
* | * |
* ... * ...
* | * |
* WithEnvironmentObject wrapping e1 * WithEnvironmentObject [SupportUnscopables=Yes] wrapping e1
* | * |
* WithEnvironmentObject wrapping e0 * WithEnvironmentObject [SupportUnscopables=Yes] wrapping e0
* | * |
* NonSyntacticLexicalEnvironmentObject [this=*unused*] * NonSyntacticLexicalEnvironmentObject [this=*unused*]
* *
@@ -355,9 +368,9 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* ... * ...
* | * |
* WithEnvironmentObject wrapping e1 * WithEnvironmentObject [SupportUnscopables=Yes] wrapping e1
* | * |
* WithEnvironmentObject wrapping e0 * WithEnvironmentObject [SupportUnscopables=Yes] wrapping e0
* | * |
* NonSyntacticLexicalEnvironmentObject [this=*unused*] * NonSyntacticLexicalEnvironmentObject [this=*unused*]
* | * |
@@ -383,7 +396,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* [DebugProxy] CallObject (qualified 'var's) * [DebugProxy] CallObject (qualified 'var's)
* | * |
* WithEnvironmentObject wrapping bindings (conflicting 'var's and names) * WithEnvironmentObject [SupportUnscopables=No] wrapping bindings
* (conflicting 'var's and names)
* *
* If the script has direct eval, BlockLexicalEnvironmentObject is created for * If the script has direct eval, BlockLexicalEnvironmentObject is created for
* it: * it:
@@ -394,7 +408,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* [DebugProxy] CallObject (qualified 'var's) * [DebugProxy] CallObject (qualified 'var's)
* | * |
* WithEnvironmentObject wrapping bindings (conflicting 'var's and names) * WithEnvironmentObject [SupportUnscopables=No] wrapping bindings
* (conflicting 'var's and names)
* | * |
* BlockLexicalEnvironmentObject (lexical vars, and conflicting lexical vars) * BlockLexicalEnvironmentObject (lexical vars, and conflicting lexical vars)
* *
@@ -415,7 +430,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* GlobalLexicalEnvironmentObject[this=global] (lexical vars) * GlobalLexicalEnvironmentObject[this=global] (lexical vars)
* | * |
* WithEnvironmentObject wrapping object with not-conflicting bindings * WithEnvironmentObject [SupportUnscopables=No] wrapping object with
* not-conflicting bindings
* *
* If `options.useInnerBindings` is true, all bindings are stored into the * If `options.useInnerBindings` is true, all bindings are stored into the
* bindings object wrapped by WithEnvironmentObject, and they shadow globals * bindings object wrapped by WithEnvironmentObject, and they shadow globals
@@ -424,7 +440,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
* | * |
* GlobalLexicalEnvironmentObject[this=global] (lexical vars) * GlobalLexicalEnvironmentObject[this=global] (lexical vars)
* | * |
* WithEnvironmentObject wrapping object with all bindings * WithEnvironmentObject [SupportUnscopables=No] wrapping object with all
* bindings
* *
* NOTE: If `options.useInnerBindings` is true, and if lexical variable names * NOTE: If `options.useInnerBindings` is true, and if lexical variable names
* conflict with the bindings object's properties, the write on them * conflict with the bindings object's properties, the write on them
@@ -982,13 +999,16 @@ class NonSyntacticVariablesObject : public EnvironmentObject {
}; };
NonSyntacticLexicalEnvironmentObject* CreateNonSyntacticEnvironmentChain( NonSyntacticLexicalEnvironmentObject* CreateNonSyntacticEnvironmentChain(
JSContext* cx, JS::HandleObjectVector envChain); JSContext* cx, const JS::EnvironmentChain& envChain);
// With environment objects on the run-time environment chain. // With environment objects on the run-time environment chain.
class WithEnvironmentObject : public EnvironmentObject { class WithEnvironmentObject : public EnvironmentObject {
static constexpr uint32_t OBJECT_SLOT = 1; static constexpr uint32_t OBJECT_SLOT = 1;
static constexpr uint32_t THIS_SLOT = 2; static constexpr uint32_t THIS_SLOT = 2;
static constexpr uint32_t SCOPE_SLOT = 3; // For syntactic with-environments this slot stores the js::Scope*.
// For non-syntactic with-environments it stores a boolean indicating whether
// we need to look up and use Symbol.unscopables.
static constexpr uint32_t SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT = 3;
public: public:
static const JSClass class_; static const JSClass class_;
@@ -996,12 +1016,12 @@ class WithEnvironmentObject : public EnvironmentObject {
static constexpr uint32_t RESERVED_SLOTS = 4; static constexpr uint32_t RESERVED_SLOTS = 4;
static constexpr ObjectFlags OBJECT_FLAGS = {}; static constexpr ObjectFlags OBJECT_FLAGS = {};
static WithEnvironmentObject* create(JSContext* cx, HandleObject object, static WithEnvironmentObject* create(
HandleObject enclosing, JSContext* cx, HandleObject object, HandleObject enclosing,
Handle<WithScope*> scope); Handle<WithScope*> scope, JS::SupportUnscopables supportUnscopables);
static WithEnvironmentObject* createNonSyntactic(JSContext* cx, static WithEnvironmentObject* createNonSyntactic(
HandleObject object, JSContext* cx, HandleObject object, HandleObject enclosing,
HandleObject enclosing); JS::SupportUnscopables supportUnscopables);
/* Return the 'o' in 'with (o)'. */ /* Return the 'o' in 'with (o)'. */
JSObject& object() const; JSObject& object() const;
@@ -1017,6 +1037,10 @@ class WithEnvironmentObject : public EnvironmentObject {
*/ */
bool isSyntactic() const; bool isSyntactic() const;
// Whether Symbol.unscopables must be supported for this with-environment.
// This always returns true for syntactic with-environments.
bool supportUnscopables() const;
// For syntactic with environment objects, the with scope. // For syntactic with environment objects, the with scope.
WithScope& scope() const; WithScope& scope() const;
@@ -1565,7 +1589,8 @@ inline bool IsFrameInitialEnvironment(AbstractFramePtr frame,
} }
WithEnvironmentObject* CreateObjectsForEnvironmentChain( WithEnvironmentObject* CreateObjectsForEnvironmentChain(
JSContext* cx, HandleObjectVector chain, HandleObject terminatingEnv); JSContext* cx, const JS::EnvironmentChain& envChain,
HandleObject terminatingEnv);
ModuleObject* GetModuleObjectForScript(JSScript* script); ModuleObject* GetModuleObjectForScript(JSScript* script);

View File

@@ -97,7 +97,8 @@ inline bool FetchName(JSContext* cx, HandleObject receiver, HandleObject holder,
/* Take the slow path if shape was not found in a native object. */ /* Take the slow path if shape was not found in a native object. */
if (!receiver->is<NativeObject>() || !holder->is<NativeObject>() || if (!receiver->is<NativeObject>() || !holder->is<NativeObject>() ||
receiver->is<WithEnvironmentObject>()) { (receiver->is<WithEnvironmentObject>() &&
receiver->as<WithEnvironmentObject>().supportUnscopables())) {
Rooted<jsid> id(cx, NameToId(name)); Rooted<jsid> id(cx, NameToId(name));
if (!GetProperty(cx, receiver, receiver, id, vp)) { if (!GetProperty(cx, receiver, receiver, id, vp)) {
return false; return false;
@@ -108,8 +109,11 @@ inline bool FetchName(JSContext* cx, HandleObject receiver, HandleObject holder,
/* Fast path for Object instance properties. */ /* Fast path for Object instance properties. */
vp.set(holder->as<NativeObject>().getSlot(propInfo.slot())); vp.set(holder->as<NativeObject>().getSlot(propInfo.slot()));
} else { } else {
// Unwrap 'with' environments for reasons given in
// GetNameBoundInEnvironment.
RootedObject normalized(cx, MaybeUnwrapWithEnvironment(receiver));
RootedId id(cx, NameToId(name)); RootedId id(cx, NameToId(name));
if (!NativeGetExistingProperty(cx, receiver, holder.as<NativeObject>(), if (!NativeGetExistingProperty(cx, normalized, holder.as<NativeObject>(),
id, propInfo, vp)) { id, propInfo, vp)) {
return false; return false;
} }

View File

@@ -31,6 +31,7 @@
#include "jit/BaselineJIT.h" #include "jit/BaselineJIT.h"
#include "jit/Jit.h" #include "jit/Jit.h"
#include "jit/JitRuntime.h" #include "jit/JitRuntime.h"
#include "js/EnvironmentChain.h" // JS::SupportUnscopables
#include "js/experimental/JitInfo.h" // JSJitInfo #include "js/experimental/JitInfo.h" // JSJitInfo
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
@@ -1071,8 +1072,8 @@ bool js::EnterWithOperation(JSContext* cx, AbstractFramePtr frame,
} }
RootedObject envChain(cx, frame.environmentChain()); RootedObject envChain(cx, frame.environmentChain());
WithEnvironmentObject* withobj = WithEnvironmentObject* withobj = WithEnvironmentObject::create(
WithEnvironmentObject::create(cx, obj, envChain, scope); cx, obj, envChain, scope, JS::SupportUnscopables::Yes);
if (!withobj) { if (!withobj) {
return false; return false;
} }

View File

@@ -21,6 +21,7 @@
#include "xpcprivate.h" // xpc::OptionsBase #include "xpcprivate.h" // xpc::OptionsBase
#include "js/CompilationAndEvaluation.h" // JS::Compile #include "js/CompilationAndEvaluation.h" // JS::Compile
#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions, JS::DecodeOptions #include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions, JS::DecodeOptions
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
#include "js/friend/JSMEnvironment.h" // JS::ExecuteInJSMEnvironment, JS::IsJSMEnvironment #include "js/friend/JSMEnvironment.h" // JS::ExecuteInJSMEnvironment, JS::IsJSMEnvironment
#include "js/SourceText.h" // JS::Source{Ownership,Text} #include "js/SourceText.h" // JS::Source{Ownership,Text}
#include "js/Wrapper.h" #include "js/Wrapper.h"
@@ -146,7 +147,7 @@ static bool EvalStencil(JSContext* cx, HandleObject targetObj,
} }
retval.setUndefined(); retval.setUndefined();
} else { } else {
JS::RootedObjectVector envChain(cx); JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
if (!envChain.append(targetObj)) { if (!envChain.append(targetObj)) {
return false; return false;
} }