mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 23:02:20 +00:00
Bug 1767525 - Implement (under a flag) ShadowRealm constructor and evaluate function. r=jandem
Shell only, without HostInitializeShadowRealm, nor importValue Differential Revision: https://phabricator.services.mozilla.com/D146236
This commit is contained in:
parent
b15ea105d5
commit
3c9ac3d334
@ -90,6 +90,7 @@
|
||||
REAL(Set, OCLASP(Set)) \
|
||||
REAL(DataView, OCLASP(DataView)) \
|
||||
REAL(Symbol, OCLASP(Symbol)) \
|
||||
REAL(ShadowRealm, OCLASP(ShadowRealm)) \
|
||||
REAL(SharedArrayBuffer, OCLASP(SharedArrayBuffer)) \
|
||||
REAL_IF_INTL(Intl, CLASP(Intl)) \
|
||||
REAL_IF_INTL(Collator, OCLASP(Collator)) \
|
||||
|
@ -214,6 +214,12 @@ class JS_PUBLIC_API RealmCreationOptions {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool getShadowRealmsEnabled() const { return shadowRealms_; }
|
||||
RealmCreationOptions& setShadowRealmsEnabled(bool flag) {
|
||||
shadowRealms_ = flag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
bool getArrayGroupingEnabled() const { return arrayGrouping_; }
|
||||
RealmCreationOptions& setArrayGroupingEnabled(bool flag) {
|
||||
@ -274,6 +280,7 @@ class JS_PUBLIC_API RealmCreationOptions {
|
||||
bool propertyErrorMessageFix_ = false;
|
||||
bool iteratorHelpers_ = false;
|
||||
bool arrayFindLast_ = false;
|
||||
bool shadowRealms_ = false;
|
||||
#ifdef NIGHTLY_BUILD
|
||||
bool arrayGrouping_ = true;
|
||||
#endif
|
||||
|
50
js/public/ShadowRealmCallbacks.h
Normal file
50
js/public/ShadowRealmCallbacks.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- 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_ShadowReamCallbacks_h
|
||||
#define js_ShadowReamCallbacks_h
|
||||
|
||||
#include "jstypes.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
struct JS_PUBLIC_API JSContext;
|
||||
|
||||
namespace JS {
|
||||
|
||||
class RealmOptions;
|
||||
|
||||
using GlobalInitializeCallback = bool (*)(JSContext*, JS::Handle<JSObject*>);
|
||||
|
||||
// Install the HostInitializeShadowRealm callback that will be invoked when
|
||||
// creating a shadow realm.
|
||||
//
|
||||
// The callback will be passed the realm's global object, so that it is possible
|
||||
// for the embedding to make any host-determined manipulations to the global,
|
||||
// such as installing interfaces or helpers that should exist even within
|
||||
// ShadowRealms. (For example, in the web platform, WebIDL with the
|
||||
// [Exposed=*] attribute should be installed within a shadow realm.)
|
||||
extern JS_PUBLIC_API void SetShadowRealmInitializeGlobalCallback(
|
||||
JSContext* cx, GlobalInitializeCallback callback);
|
||||
|
||||
using GlobalCreationCallback =
|
||||
JSObject* (*)(JSContext* cx, JS::RealmOptions& creationOptions,
|
||||
JSPrincipals* principals,
|
||||
JS::Handle<JSObject*> enclosingGlobal);
|
||||
|
||||
// Create the Global object for a ShadowRealm.
|
||||
//
|
||||
// This isn't directly specified, however at least in Gecko, in order to
|
||||
// correctly implement HostInitializeShadowRealm, there are requirements
|
||||
// placed on the global for the ShadowRealm.
|
||||
//
|
||||
// This callback should return a Global object compatible with the
|
||||
// callback installed by SetShadowRealmInitializeGlobalCallback
|
||||
extern JS_PUBLIC_API void SetShadowRealmGlobalCreationCallback(
|
||||
JSContext* cx, GlobalCreationCallback callback);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_ShadowReamCallbacks_h
|
@ -80,12 +80,12 @@ class MOZ_STACK_CLASS JS_PUBLIC_API AutoStableStringChars final {
|
||||
|
||||
mozilla::Range<const Latin1Char> latin1Range() const {
|
||||
MOZ_ASSERT(state_ == Latin1);
|
||||
return mozilla::Range<const Latin1Char>(latin1Chars_, GetStringLength(s_));
|
||||
return mozilla::Range<const Latin1Char>(latin1Chars_, length());
|
||||
}
|
||||
|
||||
mozilla::Range<const char16_t> twoByteRange() const {
|
||||
MOZ_ASSERT(state_ == TwoByte);
|
||||
return mozilla::Range<const char16_t>(twoByteChars_, GetStringLength(s_));
|
||||
return mozilla::Range<const char16_t>(twoByteChars_, length());
|
||||
}
|
||||
|
||||
/* If we own the chars, transfer ownership to the caller. */
|
||||
@ -99,6 +99,8 @@ class MOZ_STACK_CLASS JS_PUBLIC_API AutoStableStringChars final {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t length() const { return GetStringLength(s_); }
|
||||
|
||||
private:
|
||||
AutoStableStringChars(const AutoStableStringChars& other) = delete;
|
||||
void operator=(const AutoStableStringChars& other) = delete;
|
||||
|
@ -178,6 +178,7 @@ MSG_DEF(JSMSG_TOO_MANY_ARGUMENTS, 0, JSEXN_RANGEERR, "too many arguments pr
|
||||
MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 0, JSEXN_EVALERR, "call to eval() blocked by CSP")
|
||||
MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 0, JSEXN_EVALERR, "call to Function() blocked by CSP")
|
||||
MSG_DEF(JSMSG_CSP_BLOCKED_WASM, 1, JSEXN_WASMCOMPILEERROR, "call to {0}() blocked by CSP")
|
||||
MSG_DEF(JSMSG_CSP_BLOCKED_SHADOWREALM, 0, JSEXN_EVALERR, "call to ShadowRealm.prototype.evaluate blocked by CSP")
|
||||
|
||||
// Wrappers
|
||||
MSG_DEF(JSMSG_ACCESSOR_DEF_DENIED, 1, JSEXN_ERR, "Permission denied to define accessor property {0}")
|
||||
@ -781,4 +782,17 @@ MSG_DEF(JSMSG_BAD_TUPLE_INDEX, 0, JSEXN_RANGEERR, "index out of range for
|
||||
MSG_DEF(JSMSG_BAD_TUPLE_OBJECT, 0, JSEXN_TYPEERR, "value of TupleObject must be a Tuple")
|
||||
MSG_DEF(JSMSG_RECORD_TUPLE_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert Record or Tuple to number")
|
||||
|
||||
|
||||
// Shadow Realms
|
||||
MSG_DEF(JSMSG_NOT_SHADOW_REALM, 0, JSEXN_TYPEERR, "Object is not a ShadowRealm")
|
||||
MSG_DEF(JSMSG_SHADOW_REALM_EVALUATE_NOT_STRING, 0, JSEXN_TYPEERR, "a ShadowRealm can only evaluate a string")
|
||||
MSG_DEF(JSMSG_SHADOW_REALM_INVALID_RETURN, 0, JSEXN_TYPEERR, "return value not primitive or callable")
|
||||
MSG_DEF(JSMSG_SHADOW_REALM_WRAP_FAILURE, 0, JSEXN_TYPEERR, "unable to wrap callable return object")
|
||||
MSG_DEF(JSMSG_SHADOW_REALM_EVALUATE_FAILURE, 0, JSEXN_TYPEERR, "evaluate failed")
|
||||
MSG_DEF(JSMSG_SHADOW_REALM_WRAPPED_EXECUTION_FAILURE, 0, JSEXN_TYPEERR, "wrapped function threw")
|
||||
MSG_DEF(JSMSG_SHADOW_REALM_GENERIC_SYNTAX, 0, JSEXN_SYNTAXERR, "Couldn't compile string")
|
||||
|
||||
MSG_DEF(JSMSG_SHADOW_REALM_IMPORTVALUE_NOT_IMPLEMENTED, 0, JSEXN_TYPEERR, "importValue not yet implemented")
|
||||
|
||||
|
||||
//clang-format on
|
||||
|
389
js/src/builtin/ShadowRealm.cpp
Normal file
389
js/src/builtin/ShadowRealm.cpp
Normal file
@ -0,0 +1,389 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* 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/. */
|
||||
|
||||
#include "builtin/ShadowRealm.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "builtin/Promise.h"
|
||||
#include "builtin/WrappedFunctionObject.h"
|
||||
#include "frontend/BytecodeCompilation.h"
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/ErrorReport.h"
|
||||
#include "js/Exception.h"
|
||||
#include "js/GlobalObject.h"
|
||||
#include "js/Principals.h"
|
||||
#include "js/Promise.h"
|
||||
#include "js/PropertyAndElement.h"
|
||||
#include "js/PropertyDescriptor.h"
|
||||
#include "js/ShadowRealmCallbacks.h"
|
||||
#include "js/SourceText.h"
|
||||
#include "js/StableStringChars.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Wrapper.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/ObjectOperations.h"
|
||||
|
||||
#include "builtin/HandlerFunction-inl.h"
|
||||
#include "vm/JSObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
using JS::AutoStableStringChars;
|
||||
using JS::CompileOptions;
|
||||
using JS::SourceOwnership;
|
||||
using JS::SourceText;
|
||||
|
||||
static JSObject* DefaultNewShadowRealmGlobal(JSContext* cx,
|
||||
JS::RealmOptions& options,
|
||||
JSPrincipals* principals,
|
||||
HandleObject unused) {
|
||||
static const JSClass shadowRealmGlobal = {
|
||||
"ShadowRealmGlobal", JSCLASS_GLOBAL_FLAGS, &JS::DefaultGlobalClassOps};
|
||||
|
||||
return JS_NewGlobalObject(cx, &shadowRealmGlobal, principals,
|
||||
JS::FireOnNewGlobalHook, options);
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-shadowrealm/#sec-shadowrealm-constructor
|
||||
/*static*/
|
||||
bool ShadowRealmObject::construct(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// Step 1. If NewTarget is undefined, throw a TypeError exception.
|
||||
if (!args.isConstructing()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_NOT_CONSTRUCTOR, "ShadowRealm");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
|
||||
// "%ShadowRealm.prototype%", « [[ShadowRealm]], [[ExecutionContext]] »).
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ShadowRealm,
|
||||
&proto)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<ShadowRealmObject*> shadowRealmObj(
|
||||
cx, NewObjectWithClassProto<ShadowRealmObject>(cx, proto));
|
||||
if (!shadowRealmObj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Instead of managing Realms, spidermonkey associates a realm with a global
|
||||
// object, and so we will manage and store a global.
|
||||
|
||||
// Step 3. Let realmRec be CreateRealm().
|
||||
|
||||
// Initially steal creation options from current realm:
|
||||
JS::RealmOptions options(cx->realm()->creationOptions(),
|
||||
cx->realm()->behaviors());
|
||||
|
||||
// We don't want to have to deal with CCWs in addition to
|
||||
// WrappedFunctionObjects.
|
||||
options.creationOptions().setExistingCompartment(cx->compartment());
|
||||
|
||||
JS::GlobalCreationCallback newGlobal =
|
||||
cx->runtime()->getShadowRealmGlobalCreationCallback();
|
||||
// If an embedding didn't provide a callback to initialize the global,
|
||||
// use the basic default one.
|
||||
if (!newGlobal) {
|
||||
newGlobal = DefaultNewShadowRealmGlobal;
|
||||
}
|
||||
|
||||
// Our shadow realm inherits the principals of the current realm,
|
||||
// but is otherwise constrained.
|
||||
JSPrincipals* principals = JS::GetRealmPrincipals(cx->realm());
|
||||
|
||||
// Steps 5-11: In SpiderMonkey these fall under the aegis of the global
|
||||
// creation. It's worth noting that the newGlobal callback
|
||||
// needs to respect the SetRealmGlobalObject call below, which
|
||||
// sets the global to
|
||||
// OrdinaryObjectCreate(intrinsics.[[%Object.prototype%]]).
|
||||
//
|
||||
// Step 5. Let context be a new execution context.
|
||||
// Step 6. Set the Function of context to null.
|
||||
// Step 7. Set the Realm of context to realmRec.
|
||||
// Step 8. Set the ScriptOrModule of context to null.
|
||||
// Step 9. Set O.[[ExecutionContext]] to context.
|
||||
// Step 10. Perform ? SetRealmGlobalObject(realmRec, undefined, undefined).
|
||||
// Step 11. Perform ? SetDefaultGlobalBindings(O.[[ShadowRealm]]).
|
||||
RootedObject global(cx, newGlobal(cx, options, principals, cx->global()));
|
||||
if (!global) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the new global hook obeyed our request in the
|
||||
// creation options to have a same compartment global.
|
||||
MOZ_RELEASE_ASSERT(global->compartment() == cx->compartment());
|
||||
|
||||
// Step 4. Set O.[[ShadowRealm]] to realmRec.
|
||||
shadowRealmObj->initFixedSlot(GlobalSlot, ObjectValue(*global));
|
||||
|
||||
// Step 12. Perform ? HostInitializeShadowRealm(O.[[ShadowRealm]]).
|
||||
JS::GlobalInitializeCallback hostInitializeShadowRealm =
|
||||
cx->runtime()->getShadowRealmInitializeGlobalCallback();
|
||||
if (hostInitializeShadowRealm) {
|
||||
if (!hostInitializeShadowRealm(cx, global)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 13. Return O.
|
||||
args.rval().setObject(*shadowRealmObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-shadowrealm/#sec-validateshadowrealmobject
|
||||
// (slightly modified into a cast operator too)
|
||||
static ShadowRealmObject* ValidateShadowRealmObject(JSContext* cx,
|
||||
HandleObject O) {
|
||||
// Step 1. Perform ? RequireInternalSlot(O, [[ShadowRealm]]).
|
||||
// Step 2. Perform ? RequireInternalSlot(O, [[ExecutionContext]]).
|
||||
RootedObject maybeUnwrappedO(cx, O);
|
||||
if (IsCrossCompartmentWrapper(O)) {
|
||||
maybeUnwrappedO = CheckedUnwrapDynamic(O, cx);
|
||||
// Unwrapping failed; security wrapper denied.
|
||||
if (!maybeUnwrappedO) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!maybeUnwrappedO->is<ShadowRealmObject>()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_NOT_SHADOW_REALM);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &maybeUnwrappedO->as<ShadowRealmObject>();
|
||||
}
|
||||
|
||||
// PerformShadowRealmEval ( sourceText: a String, callerRealm: a Realm Record,
|
||||
// evalRealm: a Realm Record, )
|
||||
//
|
||||
// https://tc39.es/proposal-shadowrealm/#sec-performshadowrealmeval
|
||||
static bool PerformShadowRealmEval(JSContext* cx, HandleString sourceText,
|
||||
Realm* callerRealm, Realm* evalRealm,
|
||||
MutableHandleValue rval) {
|
||||
MOZ_ASSERT(callerRealm != evalRealm);
|
||||
|
||||
// Step 1. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm).
|
||||
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, sourceText)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_CSP_BLOCKED_SHADOWREALM);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Need to compile the script into the realm we will execute into.
|
||||
//
|
||||
// We hoist the error handling out however to ensure that errors
|
||||
// are thrown from the correct realm.
|
||||
bool compileSuccess = false;
|
||||
bool evalSuccess = false;
|
||||
|
||||
do {
|
||||
Rooted<GlobalObject*> evalRealmGlobal(cx, evalRealm->maybeGlobal());
|
||||
AutoRealm ar(cx, evalRealmGlobal);
|
||||
|
||||
// Step 2. Perform the following substeps in an implementation-defined
|
||||
// order, possibly interleaving parsing and error detection:
|
||||
// a. Let script be ParseText(! StringToCodePoints(sourceText), Script).
|
||||
// b. If script is a List of errors, throw a SyntaxError exception.
|
||||
// c. If script Contains ScriptBody is false, return undefined.
|
||||
// d. Let body be the ScriptBody of script.
|
||||
// e. If body Contains NewTarget is true, throw a SyntaxError exception.
|
||||
// f. If body Contains SuperProperty is true, throw a SyntaxError
|
||||
// exception. g. If body Contains SuperCall is true, throw a SyntaxError
|
||||
// exception.
|
||||
|
||||
AutoStableStringChars linearChars(cx);
|
||||
if (!linearChars.initTwoByte(cx, sourceText)) {
|
||||
return false;
|
||||
}
|
||||
SourceText<char16_t> srcBuf;
|
||||
|
||||
const char16_t* chars = linearChars.twoByteRange().begin().get();
|
||||
SourceOwnership ownership = linearChars.maybeGiveOwnershipToCaller()
|
||||
? SourceOwnership::TakeOwnership
|
||||
: SourceOwnership::Borrowed;
|
||||
if (!srcBuf.init(cx, chars, linearChars.length(), ownership)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lets propagate some information into the compilation here.
|
||||
//
|
||||
// We may need to censor the stacks eventually, see
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1770017
|
||||
RootedScript callerScript(cx);
|
||||
const char* filename;
|
||||
unsigned lineno;
|
||||
uint32_t pcOffset;
|
||||
bool mutedErrors;
|
||||
DescribeScriptedCallerForCompilation(cx, &callerScript, &filename, &lineno,
|
||||
&pcOffset, &mutedErrors);
|
||||
|
||||
CompileOptions options(cx);
|
||||
options.setIsRunOnce(true)
|
||||
.setNoScriptRval(false)
|
||||
.setMutedErrors(mutedErrors)
|
||||
.setFileAndLine(filename, lineno);
|
||||
|
||||
Rooted<Scope*> enclosing(cx, &evalRealmGlobal->emptyGlobalScope());
|
||||
RootedScript script(
|
||||
cx, frontend::CompileEvalScript(cx, options, srcBuf, enclosing,
|
||||
evalRealmGlobal));
|
||||
|
||||
compileSuccess = !!script;
|
||||
if (!compileSuccess) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Step 3. Let strictEval be IsStrict of script.
|
||||
// Step 4. Let runningContext be the running execution context.
|
||||
// Step 5. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]).
|
||||
// Step 6. Let varEnv be evalRealm.[[GlobalEnv]].
|
||||
// Step 7. If strictEval is true, set varEnv to lexEnv.
|
||||
// Step 8. If runningContext is not already suspended, suspend
|
||||
// runningContext. Step 9. Let evalContext be a new ECMAScript code
|
||||
// execution context. Step 10. Set evalContext's Function to null. Step 11.
|
||||
// Set evalContext's Realm to evalRealm. Step 12. Set evalContext's
|
||||
// ScriptOrModule to null. Step 13. Set evalContext's VariableEnvironment to
|
||||
// varEnv. Step 14. Set evalContext's LexicalEnvironment to lexEnv. Step 15.
|
||||
// Push evalContext onto the execution context stack; evalContext is
|
||||
// now the running execution context.
|
||||
// Step 16. Let result be EvalDeclarationInstantiation(body, varEnv,
|
||||
// lexEnv, null, strictEval).
|
||||
// Step 17. If result.[[Type]] is normal, then
|
||||
// a. Set result to the result of evaluating body.
|
||||
// Step 18. If result.[[Type]] is normal and result.[[Value]] is empty, then
|
||||
// a. Set result to NormalCompletion(undefined).
|
||||
|
||||
// Step 19. Suspend evalContext and remove it from the execution context
|
||||
// stack.
|
||||
// Step 20. Resume the context that is now on the top of the execution
|
||||
// context stack as the running execution context.
|
||||
RootedObject environment(cx, &evalRealmGlobal->lexicalEnvironment());
|
||||
evalSuccess = ExecuteKernel(cx, script, environment,
|
||||
/* evalInFrame = */ NullFramePtr(), rval);
|
||||
} while (false); // AutoRealm
|
||||
|
||||
if (!compileSuccess) {
|
||||
// MG:XXX: figure out how to extract the syntax error information for
|
||||
// re-throw here (See DebuggerObject::getErrorColumnNumber ?)
|
||||
//
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1769848
|
||||
|
||||
// The SyntaxError here needs to come from the calling global, so has to
|
||||
// happen outside the AutoRealm above.
|
||||
JS_ClearPendingException(cx);
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_SHADOW_REALM_GENERIC_SYNTAX);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!evalSuccess) {
|
||||
// Step 21. If result.[[Type]] is not normal, throw a TypeError
|
||||
// exception.
|
||||
//
|
||||
// The type error here needs to come from the calling global, so has to
|
||||
// happen outside the AutoRealm above.
|
||||
|
||||
// MG:XXX: Figure out how to extract the error message and include in
|
||||
// message of TypeError (if possible): See discussion in
|
||||
// https://github.com/tc39/proposal-shadowrealm/issues/353 for some
|
||||
// potential pitfalls (i.e. what if the error has a getter on the message
|
||||
// property?)
|
||||
//
|
||||
// I imagine we could do something like GetPropertyPure, and have a nice
|
||||
// error message if we *don't* have anything to worry about.
|
||||
//
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1769849
|
||||
|
||||
JS_ClearPendingException(cx);
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_SHADOW_REALM_EVALUATE_FAILURE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 22. Return ? GetWrappedValue(callerRealm, result.[[Value]]).
|
||||
return GetWrappedValue(cx, callerRealm, rval, rval);
|
||||
}
|
||||
|
||||
// ShadowRealm.prototype.evaluate ( sourceText )
|
||||
// https://tc39.es/proposal-shadowrealm/#sec-shadowrealm.prototype.evaluate
|
||||
static bool ShadowRealm_evaluate(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// Step 1. Let O be this value (implicit ToObject)
|
||||
RootedObject obj(cx, ToObject(cx, args.thisv()));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2. Perform ? ValidateShadowRealmObject(O)
|
||||
Rooted<ShadowRealmObject*> shadowRealm(cx,
|
||||
ValidateShadowRealmObject(cx, obj));
|
||||
if (!shadowRealm) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3. If Type(sourceText) is not String, throw a TypeError exception.
|
||||
if (!args.get(0).isString()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_SHADOW_REALM_EVALUATE_NOT_STRING);
|
||||
return false;
|
||||
}
|
||||
RootedString sourceText(cx, args.get(0).toString());
|
||||
|
||||
// Step 4. Let callerRealm be the current Realm Record.
|
||||
Realm* callerRealm = cx->realm();
|
||||
|
||||
// Step 5. Let evalRealm be O.[[ShadowRealm]].
|
||||
Realm* evalRealm = shadowRealm->getShadowRealm();
|
||||
// Step 6. Return ? PerformShadowRealmEval(sourceText, callerRealm,
|
||||
// evalRealm).
|
||||
return PerformShadowRealmEval(cx, sourceText, callerRealm, evalRealm,
|
||||
args.rval());
|
||||
}
|
||||
|
||||
// ShadowRealm.prototype.importValue ( specifier, exportName )
|
||||
// https://tc39.es/proposal-shadowrealm/#sec-shadowrealm.prototype.importvalue
|
||||
static bool ShadowRealm_importValue(JSContext* cx, unsigned argc, Value* vp) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_SHADOW_REALM_IMPORTVALUE_NOT_IMPLEMENTED);
|
||||
return false;
|
||||
}
|
||||
|
||||
static const JSFunctionSpec shadowrealm_methods[] = {
|
||||
JS_FN("evaluate", ShadowRealm_evaluate, 1, 0),
|
||||
JS_FN("importValue", ShadowRealm_importValue, 2, 0), JS_FS_END};
|
||||
|
||||
static const JSPropertySpec shadowrealm_properties[] = {
|
||||
JS_STRING_SYM_PS(toStringTag, "ShadowRealm", JSPROP_READONLY), JS_PS_END};
|
||||
|
||||
static const ClassSpec ShadowRealmObjectClassSpec = {
|
||||
GenericCreateConstructor<ShadowRealmObject::construct, 0,
|
||||
gc::AllocKind::FUNCTION>,
|
||||
GenericCreatePrototype<ShadowRealmObject>,
|
||||
nullptr, // Static methods
|
||||
nullptr, // Static properties
|
||||
shadowrealm_methods, // Methods
|
||||
shadowrealm_properties // Properties
|
||||
};
|
||||
|
||||
const JSClass ShadowRealmObject::class_ = {
|
||||
"ShadowRealm",
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_ShadowRealm) |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ShadowRealmObject::SlotCount),
|
||||
JS_NULL_CLASS_OPS, &ShadowRealmObjectClassSpec};
|
||||
|
||||
const JSClass ShadowRealmObject::protoClass_ = {
|
||||
"ShadowRealm.prototype", JSCLASS_HAS_CACHED_PROTO(JSProto_ShadowRealm),
|
||||
JS_NULL_CLASS_OPS, &ShadowRealmObjectClassSpec};
|
37
js/src/builtin/ShadowRealm.h
Normal file
37
js/src/builtin/ShadowRealm.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* 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 builtin_ShadowRealm_h
|
||||
#define builtin_ShadowRealm_h
|
||||
|
||||
#include "js/Wrapper.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class ShadowRealmObject : public NativeObject {
|
||||
public:
|
||||
static const JSClass class_;
|
||||
static const JSClass protoClass_;
|
||||
|
||||
enum { GlobalSlot, SlotCount };
|
||||
|
||||
static bool construct(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
Realm* getShadowRealm() {
|
||||
MOZ_ASSERT(getWrappedGlobal());
|
||||
return getWrappedGlobal()->nonCCWRealm();
|
||||
}
|
||||
|
||||
JSObject* getWrappedGlobal() const {
|
||||
return &getFixedSlot(GlobalSlot).toObject();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif
|
298
js/src/builtin/WrappedFunctionObject.cpp
Normal file
298
js/src/builtin/WrappedFunctionObject.cpp
Normal file
@ -0,0 +1,298 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* 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/. */
|
||||
|
||||
#include "builtin/WrappedFunctionObject.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "js/CallAndConstruct.h"
|
||||
#include "js/Class.h"
|
||||
#include "js/ErrorReport.h"
|
||||
#include "js/Exception.h"
|
||||
#include "js/friend/DumpFunctions.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Value.h"
|
||||
#include "js/Wrapper.h"
|
||||
#include "vm/JSFunction.h"
|
||||
#include "vm/ObjectOperations.h"
|
||||
|
||||
#include "vm/JSObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace JS;
|
||||
|
||||
// GetWrappedValue ( callerRealm: a Realm Record, value: unknown )
|
||||
bool js::GetWrappedValue(JSContext* cx, Realm* callerRealm, HandleValue value,
|
||||
MutableHandleValue res) {
|
||||
// Step 2. Return value (Reordered)
|
||||
if (!value.isObject()) {
|
||||
res.set(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Step 1. If Type(value) is Object, then
|
||||
// a. If IsCallable(value) is false, throw a TypeError exception.
|
||||
RootedObject objectVal(cx, &value.toObject());
|
||||
if (!IsCallable(objectVal)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_SHADOW_REALM_INVALID_RETURN);
|
||||
return false;
|
||||
}
|
||||
|
||||
// b. Return ? WrappedFunctionCreate(callerRealm, value).
|
||||
return WrappedFunctionCreate(cx, callerRealm, objectVal, res);
|
||||
}
|
||||
|
||||
// [[Call]]
|
||||
// https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects-call-thisargument-argumentslist
|
||||
static bool WrappedFunction_Call(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
RootedObject callee(cx, &args.callee());
|
||||
MOZ_ASSERT(callee->is<WrappedFunctionObject>());
|
||||
|
||||
Handle<WrappedFunctionObject*> F = callee.as<WrappedFunctionObject>();
|
||||
|
||||
// 1. Let target be F.[[WrappedTargetFunction]].
|
||||
RootedObject target(cx, F->getTargetFunction());
|
||||
|
||||
// 2. Assert: IsCallable(target) is true.
|
||||
MOZ_ASSERT(IsCallable(ObjectValue(*target)));
|
||||
|
||||
// 3. Let targetRealm be ? GetFunctionRealm(target).
|
||||
Rooted<Realm*> targetRealm(cx, GetFunctionRealm(cx, target));
|
||||
if (!targetRealm) {
|
||||
return false;
|
||||
}
|
||||
// 4. Let callerRealm be ? GetFunctionRealm(F).
|
||||
Rooted<Realm*> callerRealm(cx, GetFunctionRealm(cx, F));
|
||||
if (!callerRealm) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. NOTE: Any exception objects produced after this point are associated
|
||||
// with callerRealm.
|
||||
RootedValue result(cx);
|
||||
{
|
||||
RootedObject global(cx, JS::GetRealmGlobalOrNull(callerRealm));
|
||||
MOZ_RELEASE_ASSERT(
|
||||
global, "global is null; executing in a realm that's being GC'd?");
|
||||
AutoRealm ar(cx, global);
|
||||
|
||||
// https://searchfox.org/mozilla-central/source/js/public/CallAndConstruct.h#57-73
|
||||
// 6. Let wrappedArgs be a new empty List.
|
||||
RootedValueVector wrappedArgs(cx);
|
||||
|
||||
// 7. For each element arg of argumentsList, do
|
||||
// a. Let wrappedValue be ? GetWrappedValue(targetRealm, arg).
|
||||
// b. Append wrappedValue to wrappedArgs.
|
||||
RootedValue element(cx);
|
||||
for (size_t i = 0; i < args.length(); i++) {
|
||||
element = args.get(i);
|
||||
if (!GetWrappedValue(cx, targetRealm, element, &element)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!wrappedArgs.append(element)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Let wrappedThisArgument to ? GetWrappedValue(targetRealm,
|
||||
// thisArgument).
|
||||
RootedValue wrappedThisArgument(cx);
|
||||
if (!GetWrappedValue(cx, targetRealm, args.thisv(), &wrappedThisArgument)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 9. Let result be the Completion Record of Call(target,
|
||||
// wrappedThisArgument, wrappedArgs).
|
||||
if (!JS::Call(cx, wrappedThisArgument, target, wrappedArgs, &result)) {
|
||||
// 11. Else (reordered);
|
||||
// a. Throw a TypeError exception.
|
||||
JS_ClearPendingException(cx);
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_SHADOW_REALM_WRAPPED_EXECUTION_FAILURE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 10. If result.[[Type]] is normal or result.[[Type]] is return, then
|
||||
// a. Return ? GetWrappedValue(callerRealm, result.[[Value]]).
|
||||
if (!GetWrappedValue(cx, callerRealm, result, args.rval())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CopyNameAndLength(JSContext* cx, HandleObject F,
|
||||
HandleObject Target, char* prefix = nullptr,
|
||||
unsigned argCount = 0) {
|
||||
// 1. If argCount is undefined, then set argCount to 0 (implicit)
|
||||
// 2. Let L be 0.
|
||||
double L = 0;
|
||||
|
||||
// 3. Let targetHasLength be ? HasOwnProperty(Target, "length").
|
||||
|
||||
RootedId length(cx, NameToId(cx->names().length));
|
||||
RootedId name(cx, NameToId(cx->names().name));
|
||||
// Try to avoid invoking the resolve hook.
|
||||
if (Target->is<JSFunction>() &&
|
||||
!Target->as<JSFunction>().hasResolvedLength()) {
|
||||
RootedValue targetLen(cx);
|
||||
if (!JSFunction::getUnresolvedLength(cx, Target.as<JSFunction>(),
|
||||
&targetLen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
L = std::max(0.0, targetLen.toNumber() - argCount);
|
||||
} else {
|
||||
bool targetHasLength;
|
||||
if (!HasOwnProperty(cx, Target, length, &targetHasLength)) {
|
||||
return false;
|
||||
}
|
||||
// https://searchfox.org/mozilla-central/source/js/src/vm/JSFunction.cpp#1298
|
||||
// 4. If targetHasLength is true, then
|
||||
if (targetHasLength) {
|
||||
// a. Let targetLen be ? Get(Target, "length").
|
||||
RootedValue targetLen(cx);
|
||||
if (!GetProperty(cx, Target, Target, length, &targetLen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// b. If Type(targetLen) is Number, then
|
||||
// i. If targetLen is +∞𝔽, set L to +∞.
|
||||
// ii. Else if targetLen is -∞𝔽, set L to 0.
|
||||
// iii. Else,
|
||||
// 1. Let targetLenAsInt be ! ToIntegerOrInfinity(targetLen).
|
||||
// 2. Assert: targetLenAsInt is finite.
|
||||
// 3. Set L to max(targetLenAsInt - argCount, 0).
|
||||
if (targetLen.isNumber()) {
|
||||
L = std::max(0.0, JS::ToInteger(targetLen.toNumber()) - argCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Perform ! SetFunctionLength(F, L).
|
||||
RootedValue rootedL(cx, DoubleValue(L));
|
||||
if (!JS_DefinePropertyById(cx, F, length, rootedL, JSPROP_READONLY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. Let targetName be ? Get(Target, "name").
|
||||
RootedValue targetName(cx);
|
||||
if (!GetProperty(cx, Target, Target, cx->names().name, &targetName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 7. If Type(targetName) is not String, set targetName to the empty String.
|
||||
if (!targetName.isString()) {
|
||||
targetName = StringValue(cx->runtime()->emptyString);
|
||||
}
|
||||
|
||||
RootedString targetString(cx, targetName.toString());
|
||||
RootedId targetNameId(cx);
|
||||
if (!JS_StringToId(cx, targetString, &targetNameId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 8. Perform ! SetFunctionName(F, targetName, prefix).
|
||||
// (inlined and specialized from js::SetFunctionName)
|
||||
Rooted<JSAtom*> funName(cx, IdToFunctionName(cx, targetNameId));
|
||||
if (!funName) {
|
||||
return false;
|
||||
}
|
||||
RootedValue rootedFunName(cx, StringValue(funName));
|
||||
return JS_DefinePropertyById(cx, F, name, rootedFunName, JSPROP_READONLY);
|
||||
}
|
||||
|
||||
static const JSClassOps classOps = {
|
||||
nullptr, // addProperty
|
||||
nullptr, // delProperty
|
||||
nullptr, // enumerate
|
||||
nullptr, // newEnumerate
|
||||
nullptr, // resolve
|
||||
nullptr, // mayResolve
|
||||
nullptr, // finalize
|
||||
WrappedFunction_Call, // call
|
||||
nullptr, // construct
|
||||
nullptr, // trace
|
||||
};
|
||||
|
||||
const JSClass WrappedFunctionObject::class_ = {
|
||||
"WrappedFunctionObject",
|
||||
JSCLASS_HAS_CACHED_PROTO(
|
||||
JSProto_Function) | // MG:XXX: Is this going to correctly set the
|
||||
// prototype for me?
|
||||
JSCLASS_HAS_RESERVED_SLOTS(WrappedFunctionObject::SlotCount),
|
||||
&classOps};
|
||||
|
||||
JSObject* GetRealmFunctionPrototype(JSContext* cx, Realm* realm) {
|
||||
CHECK_THREAD(cx);
|
||||
Rooted<GlobalObject*> global(cx, realm->maybeGlobal());
|
||||
MOZ_RELEASE_ASSERT(global);
|
||||
return GlobalObject::getOrCreateFunctionPrototype(cx, global);
|
||||
}
|
||||
|
||||
// WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function
|
||||
// object)
|
||||
bool js::WrappedFunctionCreate(JSContext* cx, Realm* callerRealm,
|
||||
HandleObject target, MutableHandleValue res) {
|
||||
// Ensure that the function object has the correct realm by allocating it
|
||||
// into that realm.
|
||||
RootedObject global(cx, JS::GetRealmGlobalOrNull(callerRealm));
|
||||
MOZ_RELEASE_ASSERT(global,
|
||||
"global is null; executing in a realm that's being GC'd?");
|
||||
AutoRealm ar(cx, global);
|
||||
|
||||
MOZ_ASSERT(target);
|
||||
|
||||
// Target *could* be a function from another realm
|
||||
RootedObject maybeWrappedTarget(cx, target);
|
||||
if (!JS_WrapObject(cx, &maybeWrappedTarget)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. Let internalSlotsList be the internal slots listed in Table 2, plus
|
||||
// [[Prototype]] and [[Extensible]].
|
||||
// 2. Let wrapped be ! MakeBasicObject(internalSlotsList).
|
||||
// 3. Set wrapped.[[Prototype]] to
|
||||
// callerRealm.[[Intrinsics]].[[%Function.prototype%]].
|
||||
RootedObject functionPrototype(cx,
|
||||
GetRealmFunctionPrototype(cx, callerRealm));
|
||||
MOZ_ASSERT(cx->compartment() == functionPrototype->compartment());
|
||||
|
||||
Rooted<WrappedFunctionObject*> wrapped(
|
||||
cx,
|
||||
NewObjectWithGivenProto<WrappedFunctionObject>(cx, functionPrototype));
|
||||
if (!wrapped) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Set wrapped.[[Call]] as described in 2.1 (implicit in JSClass call
|
||||
// hook)
|
||||
// 5. Set wrapped.[[WrappedTargetFunction]] to Target.
|
||||
wrapped->setTargetFunction(*maybeWrappedTarget);
|
||||
// 6. Set wrapped.[[Realm]] to callerRealm. (implicitly the realm of
|
||||
// wrapped, which we assured with the AutoRealm
|
||||
|
||||
MOZ_ASSERT(wrapped->realm() == callerRealm);
|
||||
|
||||
// 7. Let result be CopyNameAndLength(wrapped, Target).
|
||||
if (!CopyNameAndLength(cx, wrapped, maybeWrappedTarget)) {
|
||||
// 8. If result is an Abrupt Completion, throw a TypeError exception.
|
||||
JS_ClearPendingException(cx);
|
||||
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_SHADOW_REALM_WRAP_FAILURE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 9. Return wrapped.
|
||||
res.set(ObjectValue(*wrapped));
|
||||
return true;
|
||||
}
|
43
js/src/builtin/WrappedFunctionObject.h
Normal file
43
js/src/builtin/WrappedFunctionObject.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* 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 builtin_WrappedFunctionObject_h
|
||||
#define builtin_WrappedFunctionObject_h
|
||||
|
||||
#include "js/Value.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
// Implementing Wrapped Function Exotic Objects from the ShadowRealms proposal
|
||||
// https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects
|
||||
//
|
||||
// These are produced as callables are passed across ShadowRealm boundaries,
|
||||
// preventing functions from piercing the shadow realm barrier.
|
||||
class WrappedFunctionObject : public NativeObject {
|
||||
public:
|
||||
static const JSClass class_;
|
||||
|
||||
enum { WrappedTargetFunctionSlot, SlotCount };
|
||||
|
||||
JSObject* getTargetFunction() const {
|
||||
return &getFixedSlot(WrappedTargetFunctionSlot).toObject();
|
||||
}
|
||||
|
||||
void setTargetFunction(JSObject& obj) {
|
||||
setFixedSlot(WrappedTargetFunctionSlot, ObjectValue(obj));
|
||||
}
|
||||
};
|
||||
|
||||
bool WrappedFunctionCreate(JSContext* cx, Realm* callerRealm,
|
||||
HandleObject target, MutableHandleValue res);
|
||||
|
||||
bool GetWrappedValue(JSContext* cx, Realm* callerRealm, HandleValue value,
|
||||
MutableHandleValue res);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif
|
@ -4557,6 +4557,16 @@ JS_PUBLIC_API void JS::SetOutOfMemoryCallback(JSContext* cx,
|
||||
cx->runtime()->oomCallbackData = data;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API void JS::SetShadowRealmInitializeGlobalCallback(
|
||||
JSContext* cx, JS::GlobalInitializeCallback callback) {
|
||||
cx->runtime()->shadowRealmInitializeGlobalCallback = callback;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API void JS::SetShadowRealmGlobalCreationCallback(
|
||||
JSContext* cx, JS::GlobalCreationCallback callback) {
|
||||
cx->runtime()->shadowRealmGlobalCreationCallback = callback;
|
||||
}
|
||||
|
||||
JS::FirstSubsumedFrame::FirstSubsumedFrame(
|
||||
JSContext* cx, bool ignoreSelfHostedFrames /* = true */)
|
||||
: JS::FirstSubsumedFrame(cx, cx->realm()->principals(),
|
||||
|
@ -196,6 +196,7 @@ EXPORTS.js += [
|
||||
"../public/SavedFrameAPI.h",
|
||||
"../public/ScalarType.h",
|
||||
"../public/ScriptPrivate.h",
|
||||
"../public/ShadowRealmCallbacks.h",
|
||||
"../public/SharedArrayBuffer.h",
|
||||
"../public/SliceBudget.h",
|
||||
"../public/SourceText.h",
|
||||
@ -314,6 +315,7 @@ UNIFIED_SOURCES += [
|
||||
"builtin/Promise.cpp",
|
||||
"builtin/Reflect.cpp",
|
||||
"builtin/ReflectParse.cpp",
|
||||
"builtin/ShadowRealm.cpp",
|
||||
"builtin/String.cpp",
|
||||
"builtin/Symbol.cpp",
|
||||
"builtin/TestingFunctions.cpp",
|
||||
@ -321,6 +323,7 @@ UNIFIED_SOURCES += [
|
||||
"builtin/WeakMapObject.cpp",
|
||||
"builtin/WeakRefObject.cpp",
|
||||
"builtin/WeakSetObject.cpp",
|
||||
"builtin/WrappedFunctionObject.cpp",
|
||||
"ds/Bitmap.cpp",
|
||||
"ds/LifoAlloc.cpp",
|
||||
"jsapi.cpp",
|
||||
|
@ -140,6 +140,7 @@
|
||||
#include "js/MemoryFunctions.h"
|
||||
#include "js/Modules.h" // JS::GetModulePrivate, JS::SetModule{DynamicImport,Metadata,Resolve}Hook, JS::SetModulePrivate
|
||||
#include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
|
||||
#include "js/Principals.h"
|
||||
#include "js/Printf.h"
|
||||
#include "js/PropertyAndElement.h" // JS_DefineElement, JS_DefineFunction, JS_DefineFunctions, JS_DefineProperties, JS_DefineProperty, JS_GetElement, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty, JS_SetPropertyById
|
||||
#include "js/PropertySpec.h"
|
||||
@ -626,6 +627,7 @@ bool shell::enableWeakRefs = false;
|
||||
bool shell::enableToSource = false;
|
||||
bool shell::enablePropertyErrorMessageFix = false;
|
||||
bool shell::enableIteratorHelpers = false;
|
||||
bool shell::enableShadowRealms = false;
|
||||
#ifdef NIGHTLY_BUILD
|
||||
bool shell::enableArrayGrouping = true;
|
||||
#endif
|
||||
@ -4246,6 +4248,7 @@ static void SetStandardRealmOptions(JS::RealmOptions& options) {
|
||||
.setToSourceEnabled(enableToSource)
|
||||
.setPropertyErrorMessageFixEnabled(enablePropertyErrorMessageFix)
|
||||
.setIteratorHelpersEnabled(enableIteratorHelpers)
|
||||
.setShadowRealmsEnabled(enableShadowRealms)
|
||||
#ifdef NIGHTLY_BUILD
|
||||
.setArrayGroupingEnabled(enableArrayGrouping)
|
||||
#endif
|
||||
@ -11039,6 +11042,7 @@ static bool SetContextOptions(JSContext* cx, const OptionParser& op) {
|
||||
enablePropertyErrorMessageFix =
|
||||
!op.getBoolOption("disable-property-error-message-fix");
|
||||
enableIteratorHelpers = op.getBoolOption("enable-iterator-helpers");
|
||||
enableShadowRealms = op.getBoolOption("enable-shadow-realms");
|
||||
#ifdef NIGHTLY_BUILD
|
||||
enableArrayGrouping = op.getBoolOption("enable-array-grouping");
|
||||
#endif
|
||||
@ -12074,6 +12078,7 @@ int main(int argc, char** argv) {
|
||||
"property of null or undefined") ||
|
||||
!op.addBoolOption('\0', "enable-iterator-helpers",
|
||||
"Enable iterator helpers") ||
|
||||
!op.addBoolOption('\0', "enable-shadow-realms", "Enable ShadowRealms") ||
|
||||
!op.addBoolOption('\0', "enable-array-grouping",
|
||||
"Enable Array Grouping") ||
|
||||
!op.addBoolOption('\0', "enable-array-find-last",
|
||||
@ -12636,6 +12641,7 @@ int main(int argc, char** argv) {
|
||||
// Waiting is allowed on the shell's main thread, for now.
|
||||
JS_SetFutexCanWait(cx);
|
||||
JS::SetWarningReporter(cx, WarningReporter);
|
||||
|
||||
if (!SetContextOptions(cx, op)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -134,6 +134,7 @@ extern bool enableWeakRefs;
|
||||
extern bool enableToSource;
|
||||
extern bool enablePropertyErrorMessageFix;
|
||||
extern bool enableIteratorHelpers;
|
||||
extern bool enableShadowRealms;
|
||||
extern bool enableArrayGrouping;
|
||||
extern bool enablePrivateClassFields;
|
||||
extern bool enablePrivateClassMethods;
|
||||
|
33
js/src/tests/non262/ShadowRealms/ccw.js
Normal file
33
js/src/tests/non262/ShadowRealms/ccw.js
Normal file
@ -0,0 +1,33 @@
|
||||
// |reftest| shell-option(--enable-shadow-realms) skip-if(!xulRuntime.shell)
|
||||
|
||||
var g = newGlobal({ newCompartment: true });
|
||||
|
||||
var sr = g.evaluate(`new ShadowRealm()`);
|
||||
|
||||
|
||||
// sr should be a CCW to a ShadowRealm.
|
||||
ShadowRealm.prototype.evaluate.call(sr, "var x = 10");
|
||||
assertEq(sr.evaluate("x"), 10);
|
||||
|
||||
// wrappedf should *not* be a CCW, because we're using this realm's ShadowRealm.prototype.evaluate,
|
||||
// and so the active realm when invoking will be this current realm.
|
||||
//
|
||||
// However, the target function (wrappedf's f) comes from another compartment, and will have to be a CCW.
|
||||
var wrappedf = ShadowRealm.prototype.evaluate.call(sr, "function f() { return 10; }; f");
|
||||
assertEq(wrappedf(), 10);
|
||||
|
||||
var evaluate_from_other_realm = g.evaluate('ShadowRealm.prototype.evaluate');
|
||||
|
||||
// wrappedb should be a CCW, since the callee of the .call comes from the other
|
||||
// compartment.
|
||||
var wrappedb = evaluate_from_other_realm.call(sr, "function b() { return 12; }; b");
|
||||
assertEq(wrappedb(), 12);
|
||||
|
||||
nukeAllCCWs()
|
||||
// This throws, but the dead object message is lost and replaced with the wrapped function
|
||||
// object message.
|
||||
assertThrowsInstanceOf(() => wrappedf(), TypeError);
|
||||
assertThrowsInstanceOf(() => wrappedb(), TypeError);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(true, true);
|
@ -31,6 +31,7 @@
|
||||
#include "builtin/Object.h"
|
||||
#include "builtin/RegExp.h"
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "builtin/ShadowRealm.h"
|
||||
#include "builtin/Stream.h"
|
||||
#include "builtin/streams/QueueingStrategies.h" // js::{ByteLength,Count}QueueingStrategy
|
||||
#include "builtin/streams/ReadableStream.h" // js::ReadableStream
|
||||
@ -209,6 +210,9 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) {
|
||||
case JSProto_AsyncIterator:
|
||||
return !cx->realm()->creationOptions().getIteratorHelpersEnabled();
|
||||
|
||||
case JSProto_ShadowRealm:
|
||||
return !cx->realm()->creationOptions().getShadowRealmsEnabled();
|
||||
|
||||
default:
|
||||
MOZ_CRASH("unexpected JSProtoKey");
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "builtin/WrappedFunctionObject.h"
|
||||
#include "debugger/DebugAPI.h"
|
||||
#include "debugger/Debugger.h"
|
||||
#include "gc/Policy.h"
|
||||
@ -777,6 +778,12 @@ JS_PUBLIC_API Realm* JS::GetFunctionRealm(JSContext* cx, HandleObject objArg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// WrappedFunctionObjects also have a [[Realm]] internal slot,
|
||||
// which is the nonCCWRealm by construction.
|
||||
if (obj->is<WrappedFunctionObject>()) {
|
||||
return obj->nonCCWRealm();
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
if (IsScriptedProxy(obj)) {
|
||||
// Steps 4.a-b.
|
||||
|
@ -48,6 +48,7 @@
|
||||
# include "js/Proxy.h" // For AutoEnterPolicy
|
||||
#endif
|
||||
#include "js/ScriptPrivate.h"
|
||||
#include "js/ShadowRealmCallbacks.h"
|
||||
#include "js/Stack.h"
|
||||
#include "js/Stream.h" // JS::AbortSignalIsAborted
|
||||
#include "js/StreamConsumer.h"
|
||||
@ -1030,6 +1031,21 @@ struct JSRuntime {
|
||||
};
|
||||
ErrorInterceptionSupport errorInterception;
|
||||
#endif // defined(NIGHTLY_BUILD)
|
||||
|
||||
public:
|
||||
JS::GlobalInitializeCallback getShadowRealmInitializeGlobalCallback() {
|
||||
return shadowRealmInitializeGlobalCallback;
|
||||
}
|
||||
|
||||
JS::GlobalCreationCallback getShadowRealmGlobalCreationCallback() {
|
||||
return shadowRealmGlobalCreationCallback;
|
||||
}
|
||||
|
||||
js::MainThreadData<JS::GlobalInitializeCallback>
|
||||
shadowRealmInitializeGlobalCallback;
|
||||
|
||||
js::MainThreadData<JS::GlobalCreationCallback>
|
||||
shadowRealmGlobalCreationCallback;
|
||||
};
|
||||
|
||||
// Context for sending telemetry to the embedder from any thread, main or
|
||||
|
Loading…
Reference in New Issue
Block a user