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:
@@ -21,6 +21,7 @@
|
||||
#include "js/CallAndConstruct.h" // JS::IsCallable, JS_CallFunctionValue
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/CompileOptions.h"
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/experimental/JSStencil.h"
|
||||
#include "js/GCVector.h"
|
||||
#include "js/JSON.h"
|
||||
@@ -1212,7 +1213,7 @@ void nsMessageManagerScriptExecutor::LoadScriptInternal(
|
||||
}
|
||||
} else {
|
||||
JS::Rooted<JS::Value> rval(cx);
|
||||
JS::RootedVector<JSObject*> envChain(cx);
|
||||
JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
|
||||
if (!envChain.append(aMessageManager)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/CompileOptions.h"
|
||||
#include "js/Date.h"
|
||||
#include "js/EnvironmentChain.h"
|
||||
#include "js/GCVector.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/Modules.h"
|
||||
@@ -80,7 +81,7 @@ nsresult nsJSUtils::UpdateFunctionDebugMetadata(
|
||||
}
|
||||
|
||||
nsresult nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
|
||||
JS::HandleVector<JSObject*> aScopeChain,
|
||||
const JS::EnvironmentChain& aEnvChain,
|
||||
JS::CompileOptions& aOptions,
|
||||
const nsACString& aName, uint32_t aArgCount,
|
||||
const char** aArgArray,
|
||||
@@ -88,12 +89,12 @@ nsresult nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
|
||||
JSObject** aFunctionObject) {
|
||||
JSContext* cx = jsapi.cx();
|
||||
MOZ_ASSERT(js::GetContextRealm(cx));
|
||||
MOZ_ASSERT_IF(aScopeChain.length() != 0,
|
||||
js::IsObjectInContextCompartment(aScopeChain[0], cx));
|
||||
MOZ_ASSERT_IF(aEnvChain.length() != 0,
|
||||
js::IsObjectInContextCompartment(aEnvChain.chain()[0], cx));
|
||||
|
||||
// Do the junk Gecko is supposed to do before calling into JSAPI.
|
||||
for (size_t i = 0; i < aScopeChain.length(); ++i) {
|
||||
JS::ExposeObjectToActiveJS(aScopeChain[i]);
|
||||
for (size_t i = 0; i < aEnvChain.length(); ++i) {
|
||||
JS::ExposeObjectToActiveJS(aEnvChain.chain()[i]);
|
||||
}
|
||||
|
||||
// Compile.
|
||||
@@ -106,7 +107,7 @@ nsresult nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
|
||||
}
|
||||
|
||||
JS::Rooted<JSFunction*> fun(
|
||||
cx, JS::CompileFunction(cx, aScopeChain, aOptions,
|
||||
cx, JS::CompileFunction(cx, aEnvChain, aOptions,
|
||||
PromiseFlatCString(aName).get(), aArgCount,
|
||||
aArgArray, source));
|
||||
if (!fun) {
|
||||
@@ -122,14 +123,14 @@ bool nsJSUtils::IsScriptable(JS::Handle<JSObject*> aEvaluationGlobal) {
|
||||
return xpc::Scriptability::AllowedIfExists(aEvaluationGlobal);
|
||||
}
|
||||
|
||||
static bool AddScopeChainItem(JSContext* aCx, nsINode* aNode,
|
||||
JS::MutableHandleVector<JSObject*> aScopeChain) {
|
||||
static bool AddEnvChainItem(JSContext* aCx, nsINode* aNode,
|
||||
JS::EnvironmentChain& aEnvChain) {
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, aNode, &val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aScopeChain.append(&val.toObject())) {
|
||||
if (!aEnvChain.append(&val.toObject())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -137,11 +138,10 @@ static bool AddScopeChainItem(JSContext* aCx, nsINode* aNode,
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool nsJSUtils::GetScopeChainForElement(
|
||||
JSContext* aCx, Element* aElement,
|
||||
JS::MutableHandleVector<JSObject*> aScopeChain) {
|
||||
bool nsJSUtils::GetEnvironmentChainForElement(JSContext* aCx, Element* aElement,
|
||||
JS::EnvironmentChain& aEnvChain) {
|
||||
for (nsINode* cur = aElement; cur; cur = cur->GetScopeChainParent()) {
|
||||
if (!AddScopeChainItem(aCx, cur, aScopeChain)) {
|
||||
if (!AddEnvChainItem(aCx, cur, aEnvChain)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,10 @@ class nsIScriptElement;
|
||||
class nsIScriptGlobalObject;
|
||||
class nsXBLPrototypeBinding;
|
||||
|
||||
namespace JS {
|
||||
class JS_PUBLIC_API EnvironmentChain;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
union Utf8Unit;
|
||||
|
||||
@@ -51,7 +55,7 @@ class nsJSUtils {
|
||||
static uint64_t GetCurrentlyRunningCodeInnerWindowID(JSContext* aContext);
|
||||
|
||||
static nsresult CompileFunction(mozilla::dom::AutoJSAPI& jsapi,
|
||||
JS::HandleVector<JSObject*> aScopeChain,
|
||||
const JS::EnvironmentChain& aEnvChain,
|
||||
JS::CompileOptions& aOptions,
|
||||
const nsACString& aName, uint32_t aArgCount,
|
||||
const char** aArgArray,
|
||||
@@ -66,10 +70,10 @@ class nsJSUtils {
|
||||
static bool IsScriptable(JS::Handle<JSObject*> aEvaluationGlobal);
|
||||
|
||||
// Returns false if an exception got thrown on aCx. Passing a null
|
||||
// aElement is allowed; that wil produce an empty aScopeChain.
|
||||
static bool GetScopeChainForElement(
|
||||
JSContext* aCx, mozilla::dom::Element* aElement,
|
||||
JS::MutableHandleVector<JSObject*> aScopeChain);
|
||||
// aElement is allowed; that wil produce an empty aEnvChain.
|
||||
static bool GetEnvironmentChainForElement(JSContext* aCx,
|
||||
mozilla::dom::Element* aElement,
|
||||
JS::EnvironmentChain& aEnvChain);
|
||||
|
||||
static void ResetTimeZone();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#undef CreateEvent
|
||||
|
||||
#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/loader/LoadedScript.h"
|
||||
#include "js/loader/ScriptFetchOptions.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
@@ -1219,12 +1220,12 @@ nsresult EventListenerManager::CompileEventHandlerInternal(
|
||||
JSAutoRealm ar(cx, target);
|
||||
|
||||
// Now that we've entered the realm we actually care about, create our
|
||||
// scope chain. Note that we start with |element|, not aElement, because
|
||||
// mTarget is different from aElement in the <body> case, where mTarget is a
|
||||
// Window, and in that case we do not want the scope chain to include the body
|
||||
// or the document.
|
||||
JS::RootedVector<JSObject*> scopeChain(cx);
|
||||
if (!nsJSUtils::GetScopeChainForElement(cx, element, &scopeChain)) {
|
||||
// environment chain. Note that we start with |element|, not aElement,
|
||||
// because mTarget is different from aElement in the <body> case, where
|
||||
// mTarget is a Window, and in that case we do not want the environment chain
|
||||
// to include the body or the document.
|
||||
JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::Yes);
|
||||
if (!nsJSUtils::GetEnvironmentChainForElement(cx, element, envChain)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
@@ -1257,7 +1258,7 @@ nsresult EventListenerManager::CompileEventHandlerInternal(
|
||||
.setDeferDebugMetadata(true);
|
||||
|
||||
JS::Rooted<JSObject*> handler(cx);
|
||||
result = nsJSUtils::CompileFunction(jsapi, scopeChain, options,
|
||||
result = nsJSUtils::CompileFunction(jsapi, envChain, options,
|
||||
nsAtomCString(aTypeAtom), argCount,
|
||||
argNames, *body, handler.address());
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
|
||||
@@ -27,6 +27,7 @@ union Utf8Unit;
|
||||
|
||||
namespace JS {
|
||||
|
||||
class JS_PUBLIC_API EnvironmentChain;
|
||||
class JS_PUBLIC_API InstantiateOptions;
|
||||
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.
|
||||
*/
|
||||
extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
|
||||
JS::HandleObjectVector envChain,
|
||||
const JS::EnvironmentChain& envChain,
|
||||
JS::Handle<JSScript*> script,
|
||||
JS::MutableHandle<JS::Value> rval);
|
||||
|
||||
extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
|
||||
JS::HandleObjectVector envChain,
|
||||
const JS::EnvironmentChain& envChain,
|
||||
JS::Handle<JSScript*> script);
|
||||
|
||||
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
|
||||
* 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,
|
||||
SourceText<char16_t>& srcBuf,
|
||||
MutableHandle<Value> rval);
|
||||
@@ -173,7 +175,7 @@ extern JS_PUBLIC_API JSScript* CompileUtf8Path(
|
||||
* global must not be explicitly included in the scope chain.
|
||||
*/
|
||||
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 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.
|
||||
*/
|
||||
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 char* const* argnames, SourceText<mozilla::Utf8Unit>& srcBuf);
|
||||
|
||||
@@ -194,7 +196,7 @@ extern JS_PUBLIC_API JSFunction* CompileFunction(
|
||||
* Rust-friendly ergonomics.
|
||||
*/
|
||||
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 char* const* argnames, const char* utf8, size_t length);
|
||||
|
||||
|
||||
67
js/public/EnvironmentChain.h
Normal file
67
js/public/EnvironmentChain.h
Normal 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 */
|
||||
@@ -45,6 +45,8 @@
|
||||
|
||||
namespace JS {
|
||||
|
||||
class JS_PUBLIC_API EnvironmentChain;
|
||||
|
||||
/**
|
||||
* Allocate a new environment in the current compartment that is compatible with
|
||||
* JSM shared loading.
|
||||
@@ -68,7 +70,7 @@ extern JS_PUBLIC_API bool ExecuteInJSMEnvironment(JSContext* cx,
|
||||
// temporarily placed on the environment chain.
|
||||
extern JS_PUBLIC_API bool ExecuteInJSMEnvironment(
|
||||
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
|
||||
// by looking at stack frames. Returns nullptr if top frame isn't a scripted
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "frontend/BytecodeCompiler.h" // frontend::CompileEvalScript
|
||||
#include "gc/HashUtil.h"
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
|
||||
#include "js/friend/JSMEnvironment.h" // JS::NewJSMEnvironment, JS::ExecuteInJSMEnvironment, JS::GetJSMEnvironmentOfScriptedCaller, JS::IsJSMEnvironment
|
||||
#include "js/friend/WindowProxy.h" // js::IsWindowProxy
|
||||
@@ -410,7 +411,7 @@ JS_PUBLIC_API bool js::ExecuteInFrameScriptEnvironment(
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObjectVector envChain(cx);
|
||||
JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
|
||||
if (!envChain.append(objArg)) {
|
||||
return false;
|
||||
}
|
||||
@@ -461,14 +462,13 @@ JS_PUBLIC_API JSObject* JS::NewJSMEnvironment(JSContext* cx) {
|
||||
JS_PUBLIC_API bool JS::ExecuteInJSMEnvironment(JSContext* cx,
|
||||
HandleScript scriptArg,
|
||||
HandleObject varEnv) {
|
||||
RootedObjectVector emptyChain(cx);
|
||||
JS::EnvironmentChain emptyChain(cx, JS::SupportUnscopables::No);
|
||||
return ExecuteInJSMEnvironment(cx, scriptArg, varEnv, emptyChain);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API bool JS::ExecuteInJSMEnvironment(JSContext* cx,
|
||||
HandleScript scriptArg,
|
||||
HandleObject varEnv,
|
||||
HandleObjectVector targetObj) {
|
||||
JS_PUBLIC_API bool JS::ExecuteInJSMEnvironment(
|
||||
JSContext* cx, HandleScript scriptArg, HandleObject varEnv,
|
||||
const EnvironmentChain& targetObj) {
|
||||
cx->check(varEnv);
|
||||
MOZ_ASSERT(
|
||||
ObjectRealm::get(varEnv).getNonSyntacticLexicalEnvironment(varEnv));
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "jit/JSJitFrameIter.h" // for InlineFrameIterator
|
||||
#include "jit/RematerializedFrame.h" // for RematerializedFrame
|
||||
#include "js/CallArgs.h" // for CallArgs
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/friend/ErrorMessages.h" // for GetErrorMessage, JSMSG_*
|
||||
#include "js/GCVector.h" // for JS::StackGCVector
|
||||
#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)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "js/AllocPolicy.h" // js::SystemAllocPolicy, ReportOutOfMemory
|
||||
#include "js/CharacterEncoding.h" // JS_EncodeStringToUTF8
|
||||
#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
|
||||
#include "js/EnvironmentChain.h" // JS::SupportUnscopables
|
||||
#include "js/ErrorReport.h" // JS_ReportErrorASCII
|
||||
#include "js/experimental/JSStencil.h"
|
||||
#include "js/GCVector.h" // JS::StackGCVector
|
||||
@@ -612,8 +613,8 @@ static WithEnvironmentObject* CreateExtraBindingsEnvironment(
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> globalLexical(cx, &cx->global()->lexicalEnvironment());
|
||||
return WithEnvironmentObject::createNonSyntactic(cx, extraBindingsObj,
|
||||
globalLexical);
|
||||
return WithEnvironmentObject::createNonSyntactic(
|
||||
cx, extraBindingsObj, globalLexical, JS::SupportUnscopables::No);
|
||||
}
|
||||
|
||||
JSScript* frontend::CompileGlobalScriptWithExtraBindings(
|
||||
|
||||
@@ -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);
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "js/CallAndConstruct.h" // JS_CallFunctionValue
|
||||
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction
|
||||
#include "js/ContextOptions.h"
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/GlobalObject.h" // JS_NewGlobalObject
|
||||
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
||||
#include "js/SourceText.h" // JS::Source{Ownership,Text}
|
||||
@@ -87,8 +88,8 @@ BEGIN_TEST(testChromeBuffer) {
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine("", 0);
|
||||
|
||||
JS::RootedObjectVector emptyScopeChain(cx);
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, "trusted", 1,
|
||||
JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
|
||||
fun = JS::CompileFunction(cx, emptyEnvChain, options, "trusted", 1,
|
||||
¶mName, srcBuf);
|
||||
CHECK(fun);
|
||||
CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun,
|
||||
@@ -118,8 +119,8 @@ BEGIN_TEST(testChromeBuffer) {
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine("", 0);
|
||||
|
||||
JS::RootedObjectVector emptyScopeChain(cx);
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, "untrusted", 1,
|
||||
JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
|
||||
fun = JS::CompileFunction(cx, emptyEnvChain, options, "untrusted", 1,
|
||||
¶mName, srcBuf);
|
||||
CHECK(fun);
|
||||
CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE));
|
||||
@@ -165,8 +166,8 @@ BEGIN_TEST(testChromeBuffer) {
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine("", 0);
|
||||
|
||||
JS::RootedObjectVector emptyScopeChain(cx);
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, "trusted", 1,
|
||||
JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
|
||||
fun = JS::CompileFunction(cx, emptyEnvChain, options, "trusted", 1,
|
||||
¶mName, srcBuf);
|
||||
CHECK(fun);
|
||||
CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun,
|
||||
@@ -192,8 +193,8 @@ BEGIN_TEST(testChromeBuffer) {
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine("", 0);
|
||||
|
||||
JS::RootedObjectVector emptyScopeChain(cx);
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, "untrusted", 1,
|
||||
JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
|
||||
fun = JS::CompileFunction(cx, emptyEnvChain, options, "untrusted", 1,
|
||||
¶mName, srcBuf);
|
||||
CHECK(fun);
|
||||
CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE));
|
||||
@@ -226,8 +227,8 @@ BEGIN_TEST(testChromeBuffer) {
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine("", 0);
|
||||
|
||||
JS::RootedObjectVector emptyScopeChain(cx);
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, "trusted", 0,
|
||||
JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
|
||||
fun = JS::CompileFunction(cx, emptyEnvChain, options, "trusted", 0,
|
||||
nullptr, srcBuf);
|
||||
CHECK(fun);
|
||||
CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun,
|
||||
@@ -254,8 +255,8 @@ BEGIN_TEST(testChromeBuffer) {
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine("", 0);
|
||||
|
||||
JS::RootedObjectVector emptyScopeChain(cx);
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, "untrusted", 1,
|
||||
JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
|
||||
fun = JS::CompileFunction(cx, emptyEnvChain, options, "untrusted", 1,
|
||||
¶mName, srcBuf);
|
||||
CHECK(fun);
|
||||
CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE));
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "js/CallAndConstruct.h"
|
||||
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/SourceText.h" // JS::Source{Ownership,Text}
|
||||
#include "jsapi-tests/tests.h"
|
||||
#include "util/Text.h"
|
||||
@@ -23,7 +24,7 @@ BEGIN_TEST(test_functionBinding) {
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(__FILE__, __LINE__);
|
||||
|
||||
JS::RootedObjectVector emptyScopeChain(cx);
|
||||
JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
|
||||
|
||||
// Named function shouldn't have it's binding.
|
||||
{
|
||||
@@ -33,7 +34,7 @@ BEGIN_TEST(test_functionBinding) {
|
||||
CHECK(srcBuf.init(cx, s1chars, js_strlen(s1chars),
|
||||
JS::SourceOwnership::Borrowed));
|
||||
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, "s1", 0, nullptr,
|
||||
fun = JS::CompileFunction(cx, emptyEnvChain, options, "s1", 0, nullptr,
|
||||
srcBuf);
|
||||
CHECK(fun);
|
||||
}
|
||||
@@ -52,7 +53,7 @@ BEGIN_TEST(test_functionBinding) {
|
||||
CHECK(srcBuf.init(cx, s2chars, js_strlen(s2chars),
|
||||
JS::SourceOwnership::Borrowed));
|
||||
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, "s2", 0, nullptr,
|
||||
fun = JS::CompileFunction(cx, emptyEnvChain, options, "s2", 0, nullptr,
|
||||
srcBuf);
|
||||
CHECK(fun);
|
||||
}
|
||||
@@ -69,7 +70,7 @@ BEGIN_TEST(test_functionBinding) {
|
||||
CHECK(srcBuf.init(cx, s3chars, js_strlen(s3chars),
|
||||
JS::SourceOwnership::Borrowed));
|
||||
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, nullptr, 0, nullptr,
|
||||
fun = JS::CompileFunction(cx, emptyEnvChain, options, nullptr, 0, nullptr,
|
||||
srcBuf);
|
||||
CHECK(fun);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "js/CallAndConstruct.h"
|
||||
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
||||
#include "js/SourceText.h" // JS::Source{Ownership,Text}
|
||||
#include "jsapi-tests/tests.h"
|
||||
@@ -22,7 +23,7 @@
|
||||
using namespace js;
|
||||
|
||||
BEGIN_TEST(testFunctionNonSyntactic) {
|
||||
JS::RootedObjectVector scopeChain(cx);
|
||||
JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
|
||||
|
||||
{
|
||||
JS::RootedObject scopeObj(cx, JS_NewPlainObject(cx));
|
||||
@@ -30,7 +31,7 @@ BEGIN_TEST(testFunctionNonSyntactic) {
|
||||
JS::RootedValue val(cx);
|
||||
val.setNumber(1);
|
||||
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);
|
||||
val.setNumber(20);
|
||||
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);
|
||||
options.setFileAndLine(__FILE__, __LINE__);
|
||||
RootedFunction fun(cx, JS::CompileFunction(cx, scopeChain, options, "test",
|
||||
0, nullptr, srcBuf));
|
||||
RootedFunction fun(cx, JS::CompileFunction(cx, envChain, options, "test", 0,
|
||||
nullptr, srcBuf));
|
||||
CHECK(fun);
|
||||
|
||||
CHECK(fun->enclosingScope()->kind() == ScopeKind::NonSyntactic);
|
||||
@@ -76,8 +77,8 @@ BEGIN_TEST(testFunctionNonSyntactic) {
|
||||
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(__FILE__, __LINE__);
|
||||
RootedFunction fun(cx, JS::CompileFunction(cx, scopeChain, options, "test",
|
||||
1, args, srcBuf));
|
||||
RootedFunction fun(cx, JS::CompileFunction(cx, envChain, options, "test", 1,
|
||||
args, srcBuf));
|
||||
CHECK(fun);
|
||||
|
||||
CHECK(fun->enclosingScope()->kind() == ScopeKind::NonSyntactic);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/PropertyAndElement.h" // JS_AlreadyHasOwnProperty, JS_HasProperty
|
||||
#include "js/SourceText.h"
|
||||
#include "jsapi-tests/tests.h"
|
||||
@@ -16,13 +17,13 @@ BEGIN_TEST(testJSEvaluateScript) {
|
||||
|
||||
JS::RootedValue retval(cx);
|
||||
JS::CompileOptions opts(cx);
|
||||
JS::RootedObjectVector scopeChain(cx);
|
||||
CHECK(scopeChain.append(obj));
|
||||
JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
|
||||
CHECK(envChain.append(obj));
|
||||
|
||||
JS::SourceText<char16_t> srcBuf;
|
||||
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));
|
||||
|
||||
bool hasProp = true;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "jit/Ion.h" // js::jit::IsIonEnabled
|
||||
#include "js/CallAndConstruct.h" // JS::CallFunction
|
||||
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/GlobalObject.h" // JS_NewGlobalObject
|
||||
#include "js/SourceText.h" // JS::Source{Ownership,Text}
|
||||
#include "jsapi-tests/tests.h"
|
||||
@@ -69,9 +70,9 @@ bool testPreserveJitCode(bool preserveJitCode, unsigned remainingIonScripts) {
|
||||
options.setFileAndLine(__FILE__, 1);
|
||||
|
||||
JS::RootedFunction fun(cx);
|
||||
JS::RootedObjectVector emptyScopeChain(cx);
|
||||
fun = JS::CompileFunction(cx, emptyScopeChain, options, "f", 0, nullptr,
|
||||
srcBuf);
|
||||
JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No);
|
||||
fun =
|
||||
JS::CompileFunction(cx, emptyEnvChain, options, "f", 0, nullptr, srcBuf);
|
||||
CHECK(fun);
|
||||
|
||||
RootedValue value(cx);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||
|
||||
#include "js/CompilationAndEvaluation.h" // JS::CompileFunction, JS::Evaluate
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/GlobalObject.h" // JS_NewGlobalObject
|
||||
#include "js/MemoryFunctions.h"
|
||||
#include "js/SourceText.h" // JS::Source{Ownership,Text}
|
||||
@@ -41,13 +42,13 @@ BEGIN_TEST(testBug795104) {
|
||||
CHECK(JS::Evaluate(cx, opts, srcBuf, &unused));
|
||||
|
||||
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
|
||||
// mode, since it's not supported for functions.
|
||||
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);
|
||||
|
||||
JS_free(cx, s);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "frontend/CompilationStencil.h"
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/experimental/CompileScript.h"
|
||||
#include "js/experimental/JSStencil.h"
|
||||
#include "js/Modules.h"
|
||||
@@ -136,11 +137,11 @@ BEGIN_TEST(testStencil_NonSyntactic) {
|
||||
CHECK(obj);
|
||||
CHECK(JS_SetProperty(cx, obj, "x", val));
|
||||
|
||||
JS::RootedObjectVector chain(cx);
|
||||
CHECK(chain.append(obj));
|
||||
JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
|
||||
CHECK(envChain.append(obj));
|
||||
|
||||
JS::RootedValue rval(cx);
|
||||
CHECK(JS_ExecuteScript(cx, chain, script, &rval));
|
||||
CHECK(JS_ExecuteScript(cx, envChain, script, &rval));
|
||||
CHECK(rval.isNumber() && rval.toNumber() == 42);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -136,6 +136,7 @@ EXPORTS.js += [
|
||||
"../public/Conversions.h",
|
||||
"../public/Date.h",
|
||||
"../public/Debug.h",
|
||||
"../public/EnvironmentChain.h",
|
||||
"../public/Equality.h",
|
||||
"../public/ErrorInterceptor.h",
|
||||
"../public/ErrorReport.h",
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions, JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::InstantiateOptions
|
||||
#include "js/ContextOptions.h" // JS::ContextOptions{,Ref}
|
||||
#include "js/Debug.h" // JS::dbg::ShouldAvoidSideEffects, JS::ExecutionTrace
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/Equality.h" // JS::SameValue
|
||||
#include "js/ErrorReport.h" // JS::PrintError
|
||||
#include "js/Exception.h" // JS::StealPendingExceptionStack
|
||||
@@ -2693,7 +2694,7 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
|
||||
bool saveIncrementalBytecode = false;
|
||||
bool execute = true;
|
||||
bool assertEqBytecode = false;
|
||||
JS::RootedObjectVector envChain(cx);
|
||||
JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
|
||||
RootedObject callerGlobal(cx, cx->global());
|
||||
|
||||
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
|
||||
// bytecode cache is stored.
|
||||
if (loadBytecode || saveIncrementalBytecode) {
|
||||
@@ -2796,7 +2804,7 @@ static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
|
||||
// Wrap the envChainObject list into target realm.
|
||||
JSAutoRealm ar(cx, global);
|
||||
for (size_t i = 0; i < envChain.length(); ++i) {
|
||||
if (!JS_WrapObject(cx, envChain[i])) {
|
||||
if (!JS_WrapObject(cx, envChain.chain()[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -9746,6 +9754,8 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||
" 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"
|
||||
" 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,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "frontend/Parser.h" // frontend::Parser, frontend::ParseGoal
|
||||
#include "js/CharacterEncoding.h" // JS::UTF8Chars, JS::ConstUTF8CharsZ, JS::UTF8CharsToNewTwoByteCharsZ
|
||||
#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/experimental/JSStencil.h" // JS::Stencil
|
||||
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
|
||||
#include "js/RootingAPI.h" // JS::Rooted
|
||||
@@ -299,7 +300,7 @@ class FunctionCompiler {
|
||||
return funStr_.append(srcBuf.get(), srcBuf.length());
|
||||
}
|
||||
|
||||
JSFunction* finish(HandleObjectVector envChain,
|
||||
JSFunction* finish(const JS::EnvironmentChain& envChain,
|
||||
const ReadOnlyCompileOptions& optionsArg) {
|
||||
using js::frontend::FunctionSyntaxKind;
|
||||
|
||||
@@ -381,7 +382,7 @@ class FunctionCompiler {
|
||||
};
|
||||
|
||||
JS_PUBLIC_API JSFunction* JS::CompileFunction(
|
||||
JSContext* cx, HandleObjectVector envChain,
|
||||
JSContext* cx, const EnvironmentChain& envChain,
|
||||
const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
|
||||
const char* const* argnames, SourceText<char16_t>& srcBuf) {
|
||||
ManualReportFrontendContext fc(cx);
|
||||
@@ -397,7 +398,7 @@ 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 char* const* argnames, SourceText<Utf8Unit>& srcBuf) {
|
||||
ManualReportFrontendContext fc(cx);
|
||||
@@ -413,7 +414,7 @@ JS_PUBLIC_API JSFunction* JS::CompileFunction(
|
||||
}
|
||||
|
||||
JS_PUBLIC_API JSFunction* JS::CompileFunctionUtf8(
|
||||
JSContext* cx, HandleObjectVector envChain,
|
||||
JSContext* cx, const EnvironmentChain& envChain,
|
||||
const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
|
||||
const char* const* argnames, const char* bytes, size_t length) {
|
||||
SourceText<Utf8Unit> srcBuf;
|
||||
@@ -495,7 +496,7 @@ MOZ_NEVER_INLINE static bool ExecuteScript(JSContext* cx, HandleObject envChain,
|
||||
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) {
|
||||
RootedObject env(cx, CreateNonSyntacticEnvironmentChain(cx, envChain));
|
||||
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(
|
||||
JSContext* cx, HandleObjectVector envChain, HandleScript scriptArg,
|
||||
JSContext* cx, const JS::EnvironmentChain& envChain, HandleScript scriptArg,
|
||||
MutableHandleValue rval) {
|
||||
return ExecuteScript(cx, envChain, scriptArg, rval);
|
||||
}
|
||||
|
||||
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);
|
||||
return ExecuteScript(cx, envChain, scriptArg, &rval);
|
||||
}
|
||||
@@ -576,7 +578,7 @@ JS_PUBLIC_API bool JS::Evaluate(JSContext* cx,
|
||||
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,
|
||||
SourceText<char16_t>& srcBuf,
|
||||
MutableHandleValue rval) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "builtin/Array.h"
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "js/EnvironmentChain.h" // JS::EnvironmentChain
|
||||
#include "js/Exception.h"
|
||||
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
|
||||
#include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
|
||||
@@ -717,10 +718,9 @@ JSObject* js::GetThisObject(JSObject* obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
WithEnvironmentObject* WithEnvironmentObject::create(JSContext* cx,
|
||||
HandleObject object,
|
||||
HandleObject enclosing,
|
||||
Handle<WithScope*> scope) {
|
||||
WithEnvironmentObject* WithEnvironmentObject::create(
|
||||
JSContext* cx, HandleObject object, HandleObject enclosing,
|
||||
Handle<WithScope*> scope, JS::SupportUnscopables supportUnscopables) {
|
||||
Rooted<SharedShape*> shape(cx,
|
||||
EmptyEnvironmentShape<WithEnvironmentObject>(cx));
|
||||
if (!shape) {
|
||||
@@ -738,17 +738,22 @@ WithEnvironmentObject* WithEnvironmentObject::create(JSContext* cx,
|
||||
obj->initReservedSlot(OBJECT_SLOT, ObjectValue(*object));
|
||||
obj->initReservedSlot(THIS_SLOT, ObjectValue(*thisObj));
|
||||
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 {
|
||||
obj->initReservedSlot(SCOPE_SLOT, NullValue());
|
||||
Value v = BooleanValue(supportUnscopables == JS::SupportUnscopables::Yes);
|
||||
obj->initReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT, v);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
WithEnvironmentObject* WithEnvironmentObject::createNonSyntactic(
|
||||
JSContext* cx, HandleObject object, HandleObject enclosing) {
|
||||
return create(cx, object, enclosing, nullptr);
|
||||
JSContext* cx, HandleObject object, HandleObject enclosing,
|
||||
JS::SupportUnscopables supportUnscopables) {
|
||||
return create(cx, object, enclosing, nullptr, supportUnscopables);
|
||||
}
|
||||
|
||||
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()) {
|
||||
bool scopable;
|
||||
if (!CheckUnscopables(cx, actual, id, &scopable)) {
|
||||
bool scopable = true;
|
||||
if (obj->as<WithEnvironmentObject>().supportUnscopables() &&
|
||||
!CheckUnscopables(cx, actual, id, &scopable)) {
|
||||
return false;
|
||||
}
|
||||
if (!scopable) {
|
||||
@@ -840,7 +846,7 @@ static bool with_HasProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
if (!HasProperty(cx, actual, id, foundp)) {
|
||||
return false;
|
||||
}
|
||||
if (!*foundp) {
|
||||
if (!*foundp || !obj->as<WithEnvironmentObject>().supportUnscopables()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -938,7 +944,7 @@ const JSClass NonSyntacticVariablesObject::class_ = {
|
||||
};
|
||||
|
||||
NonSyntacticLexicalEnvironmentObject* js::CreateNonSyntacticEnvironmentChain(
|
||||
JSContext* cx, HandleObjectVector envChain) {
|
||||
JSContext* cx, const JS::EnvironmentChain& envChain) {
|
||||
// Callers are responsible for segregating the NonSyntactic case from simple
|
||||
// compilation cases.
|
||||
MOZ_RELEASE_ASSERT(!envChain.empty());
|
||||
@@ -2437,9 +2443,12 @@ class DebugEnvironmentProxyHandler : public NurseryAllocableProxyHandler {
|
||||
|
||||
if (isWith) {
|
||||
size_t j = 0;
|
||||
bool supportUnscopables =
|
||||
env->as<WithEnvironmentObject>().supportUnscopables();
|
||||
for (size_t i = 0; i < props.length(); i++) {
|
||||
bool inScope;
|
||||
if (!CheckUnscopables(cx, env, props[i], &inScope)) {
|
||||
bool inScope = true;
|
||||
if (supportUnscopables &&
|
||||
!CheckUnscopables(cx, env, props[i], &inScope)) {
|
||||
return false;
|
||||
}
|
||||
if (inScope) {
|
||||
@@ -3429,13 +3438,14 @@ JSObject* js::GetDebugEnvironmentForGlobalLexicalEnvironment(JSContext* cx) {
|
||||
}
|
||||
|
||||
WithEnvironmentObject* js::CreateObjectsForEnvironmentChain(
|
||||
JSContext* cx, HandleObjectVector chain, HandleObject terminatingEnv) {
|
||||
JSContext* cx, const JS::EnvironmentChain& chain,
|
||||
HandleObject terminatingEnv) {
|
||||
MOZ_ASSERT(!chain.empty());
|
||||
|
||||
#ifdef DEBUG
|
||||
for (size_t i = 0; i < chain.length(); ++i) {
|
||||
cx->check(chain[i]);
|
||||
MOZ_ASSERT(!chain[i]->isUnqualifiedVarObj());
|
||||
cx->check(chain.chain()[i]);
|
||||
MOZ_ASSERT(!chain.chain()[i]->isUnqualifiedVarObj());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3444,8 +3454,8 @@ WithEnvironmentObject* js::CreateObjectsForEnvironmentChain(
|
||||
Rooted<WithEnvironmentObject*> withEnv(cx);
|
||||
RootedObject enclosingEnv(cx, terminatingEnv);
|
||||
for (size_t i = chain.length(); i > 0;) {
|
||||
withEnv =
|
||||
WithEnvironmentObject::createNonSyntactic(cx, chain[--i], enclosingEnv);
|
||||
withEnv = WithEnvironmentObject::createNonSyntactic(
|
||||
cx, chain.chain()[--i], enclosingEnv, chain.supportUnscopables());
|
||||
if (!withEnv) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -3470,14 +3480,24 @@ JSObject* WithEnvironmentObject::withThis() const {
|
||||
}
|
||||
|
||||
bool WithEnvironmentObject::isSyntactic() const {
|
||||
Value v = getReservedSlot(SCOPE_SLOT);
|
||||
MOZ_ASSERT(v.isPrivateGCThing() || v.isNull());
|
||||
Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT);
|
||||
MOZ_ASSERT(v.isPrivateGCThing() || v.isBoolean());
|
||||
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 {
|
||||
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) {
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
#include "vm/Scope.h"
|
||||
#include "vm/ScopeKind.h" // ScopeKind
|
||||
|
||||
namespace JS {
|
||||
class JS_PUBLIC_API EnvironmentChain;
|
||||
enum class SupportUnscopables : bool;
|
||||
}; // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
class AbstractGeneratorObject;
|
||||
@@ -175,6 +180,11 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* When the embedding wants qualified 'var' bindings and unqualified
|
||||
@@ -234,7 +244,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
* |
|
||||
* GlobalLexicalEnvironmentObject[this=global]
|
||||
* |
|
||||
* WithEnvironmentObject wrapping target (qualified 'var's)
|
||||
* WithEnvironmentObject [SupportUnscopables=No] wrapping target
|
||||
* (qualified 'var's)
|
||||
* |
|
||||
* NonSyntacticLexicalEnvironmentObject[this=target] (lexical vars)
|
||||
*
|
||||
@@ -263,7 +274,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
* |
|
||||
* NonSyntacticLexicalEnvironmentObject[this=nsvo]
|
||||
* |
|
||||
* WithEnvironmentObject wrapping target (qualified 'var's)
|
||||
* WithEnvironmentObject [SupportUnscopables=No] wrapping target
|
||||
* (qualified 'var's)
|
||||
* |
|
||||
* NonSyntacticLexicalEnvironmentObject[this=target] (lexical vars)
|
||||
*
|
||||
@@ -298,7 +310,7 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
* |
|
||||
* NonSyntacticVariablesObject (qualified 'var's and unqualified names)
|
||||
* |
|
||||
* WithEnvironmentObject wrapping messageManager
|
||||
* WithEnvironmentObject [SupportUnscopables=No] wrapping messageManager
|
||||
* |
|
||||
* NonSyntacticLexicalEnvironmentObject[this=messageManager] (lexical vars)
|
||||
*
|
||||
@@ -315,7 +327,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
* |
|
||||
* GlobalLexicalEnvironmentObject[this=global]
|
||||
* |
|
||||
* WithEnvironmentObject wrapping messageManager (qualified 'var's)
|
||||
* WithEnvironmentObject [SupportUnscopables=No] wrapping messageManager
|
||||
* (qualified 'var's)
|
||||
* |
|
||||
* NonSyntacticLexicalEnvironmentObject[this=messageManager] (lexical vars)
|
||||
*
|
||||
@@ -334,13 +347,13 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
* |
|
||||
* 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*]
|
||||
*
|
||||
@@ -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*]
|
||||
* |
|
||||
@@ -383,7 +396,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
* |
|
||||
* [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
|
||||
* it:
|
||||
@@ -394,7 +408,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
* |
|
||||
* [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)
|
||||
*
|
||||
@@ -415,7 +430,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
* |
|
||||
* 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
|
||||
* bindings object wrapped by WithEnvironmentObject, and they shadow globals
|
||||
@@ -424,7 +440,8 @@ extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
|
||||
* |
|
||||
* 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
|
||||
* conflict with the bindings object's properties, the write on them
|
||||
@@ -982,13 +999,16 @@ class NonSyntacticVariablesObject : public EnvironmentObject {
|
||||
};
|
||||
|
||||
NonSyntacticLexicalEnvironmentObject* CreateNonSyntacticEnvironmentChain(
|
||||
JSContext* cx, JS::HandleObjectVector envChain);
|
||||
JSContext* cx, const JS::EnvironmentChain& envChain);
|
||||
|
||||
// With environment objects on the run-time environment chain.
|
||||
class WithEnvironmentObject : public EnvironmentObject {
|
||||
static constexpr uint32_t OBJECT_SLOT = 1;
|
||||
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:
|
||||
static const JSClass class_;
|
||||
@@ -996,12 +1016,12 @@ class WithEnvironmentObject : public EnvironmentObject {
|
||||
static constexpr uint32_t RESERVED_SLOTS = 4;
|
||||
static constexpr ObjectFlags OBJECT_FLAGS = {};
|
||||
|
||||
static WithEnvironmentObject* create(JSContext* cx, HandleObject object,
|
||||
HandleObject enclosing,
|
||||
Handle<WithScope*> scope);
|
||||
static WithEnvironmentObject* createNonSyntactic(JSContext* cx,
|
||||
HandleObject object,
|
||||
HandleObject enclosing);
|
||||
static WithEnvironmentObject* create(
|
||||
JSContext* cx, HandleObject object, HandleObject enclosing,
|
||||
Handle<WithScope*> scope, JS::SupportUnscopables supportUnscopables);
|
||||
static WithEnvironmentObject* createNonSyntactic(
|
||||
JSContext* cx, HandleObject object, HandleObject enclosing,
|
||||
JS::SupportUnscopables supportUnscopables);
|
||||
|
||||
/* Return the 'o' in 'with (o)'. */
|
||||
JSObject& object() const;
|
||||
@@ -1017,6 +1037,10 @@ class WithEnvironmentObject : public EnvironmentObject {
|
||||
*/
|
||||
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.
|
||||
WithScope& scope() const;
|
||||
|
||||
@@ -1565,7 +1589,8 @@ inline bool IsFrameInitialEnvironment(AbstractFramePtr frame,
|
||||
}
|
||||
|
||||
WithEnvironmentObject* CreateObjectsForEnvironmentChain(
|
||||
JSContext* cx, HandleObjectVector chain, HandleObject terminatingEnv);
|
||||
JSContext* cx, const JS::EnvironmentChain& envChain,
|
||||
HandleObject terminatingEnv);
|
||||
|
||||
ModuleObject* GetModuleObjectForScript(JSScript* script);
|
||||
|
||||
|
||||
@@ -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. */
|
||||
if (!receiver->is<NativeObject>() || !holder->is<NativeObject>() ||
|
||||
receiver->is<WithEnvironmentObject>()) {
|
||||
(receiver->is<WithEnvironmentObject>() &&
|
||||
receiver->as<WithEnvironmentObject>().supportUnscopables())) {
|
||||
Rooted<jsid> id(cx, NameToId(name));
|
||||
if (!GetProperty(cx, receiver, receiver, id, vp)) {
|
||||
return false;
|
||||
@@ -108,8 +109,11 @@ inline bool FetchName(JSContext* cx, HandleObject receiver, HandleObject holder,
|
||||
/* Fast path for Object instance properties. */
|
||||
vp.set(holder->as<NativeObject>().getSlot(propInfo.slot()));
|
||||
} else {
|
||||
// Unwrap 'with' environments for reasons given in
|
||||
// GetNameBoundInEnvironment.
|
||||
RootedObject normalized(cx, MaybeUnwrapWithEnvironment(receiver));
|
||||
RootedId id(cx, NameToId(name));
|
||||
if (!NativeGetExistingProperty(cx, receiver, holder.as<NativeObject>(),
|
||||
if (!NativeGetExistingProperty(cx, normalized, holder.as<NativeObject>(),
|
||||
id, propInfo, vp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/Jit.h"
|
||||
#include "jit/JitRuntime.h"
|
||||
#include "js/EnvironmentChain.h" // JS::SupportUnscopables
|
||||
#include "js/experimental/JitInfo.h" // JSJitInfo
|
||||
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
|
||||
#include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
|
||||
@@ -1071,8 +1072,8 @@ bool js::EnterWithOperation(JSContext* cx, AbstractFramePtr frame,
|
||||
}
|
||||
|
||||
RootedObject envChain(cx, frame.environmentChain());
|
||||
WithEnvironmentObject* withobj =
|
||||
WithEnvironmentObject::create(cx, obj, envChain, scope);
|
||||
WithEnvironmentObject* withobj = WithEnvironmentObject::create(
|
||||
cx, obj, envChain, scope, JS::SupportUnscopables::Yes);
|
||||
if (!withobj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "xpcprivate.h" // xpc::OptionsBase
|
||||
#include "js/CompilationAndEvaluation.h" // JS::Compile
|
||||
#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/SourceText.h" // JS::Source{Ownership,Text}
|
||||
#include "js/Wrapper.h"
|
||||
@@ -146,7 +147,7 @@ static bool EvalStencil(JSContext* cx, HandleObject targetObj,
|
||||
}
|
||||
retval.setUndefined();
|
||||
} else {
|
||||
JS::RootedObjectVector envChain(cx);
|
||||
JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
|
||||
if (!envChain.append(targetObj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user