icecat: add release icecat-140.8.0-2 for aramo

This commit is contained in:
Ark74 2026-03-11 06:58:43 -06:00
parent d9a6c0aa96
commit d570f39e11
616 changed files with 39955 additions and 33937 deletions

View file

@ -36,6 +36,7 @@
#include "vm/Compartment-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/Realm-inl.h"
using namespace js;
@ -1451,6 +1452,7 @@ bool js::atomics_notify_impl(JSContext* cx, SharedArrayRawBuffer* sarb,
// avoid mutex ordering problems.
RootedValue resultMsg(cx, StringValue(cx->names().ok));
for (uint32_t i = 0; i < promisesToResolve.length(); i++) {
AutoRealm ar(cx, promisesToResolve[i]);
if (!PromiseObject::resolve(cx, promisesToResolve[i], resultMsg)) {
MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed());
return false;

View file

@ -913,8 +913,9 @@ bool ModuleObject::isInstance(HandleValue value) {
}
bool ModuleObject::hasCyclicModuleFields() const {
// This currently only returns false if we GC during initialization.
return !getReservedSlot(CyclicModuleFieldsSlot).isUndefined();
bool result = !getReservedSlot(CyclicModuleFieldsSlot).isUndefined();
MOZ_ASSERT_IF(result, !hasSyntheticModuleFields());
return result;
}
CyclicModuleFields* ModuleObject::cyclicModuleFields() {
@ -1496,7 +1497,9 @@ bool ModuleObject::createSyntheticEnvironment(JSContext* cx,
return false;
}
MOZ_ASSERT(env->shape()->propMapLength() == values.length());
// We expect one property per synthetic value plus one for the *namespace*
// binding.
MOZ_ASSERT(env->shape()->propMapLength() == values.length() + 1);
for (uint32_t i = 0; i < values.length(); i++) {
env->setAliasedBinding(env->firstSyntheticValueSlot() + i, values[i]);

View file

@ -1058,7 +1058,7 @@ static constexpr auto AsciiRegExpEscapeMap() {
*/
template <typename CharT>
[[nodiscard]] static bool EncodeForRegExpEscape(
mozilla::Span<const CharT> chars, JSStringBuilder& sb) {
JSContext* cx, mozilla::Span<const CharT> chars, JSStringBuilder& sb) {
MOZ_ASSERT(sb.empty());
const size_t length = chars.size();
@ -1075,7 +1075,7 @@ template <typename CharT>
// Initial scan to determine if escape sequences are needed and to compute
// the output length.
size_t outLength = length;
mozilla::CheckedInt<size_t> outLength = length;
// Leading Ascii alpha-numeric character is hex-escaped.
size_t scanStart = 0;
@ -1115,12 +1115,16 @@ template <typename CharT>
outLength += UnicodeEscapeAddLength;
}
}
if (!outLength.isValid()) {
ReportAllocationOverflow(cx);
return false;
}
// Return if no escape sequences are needed.
if (outLength == length) {
if (outLength.value() == length) {
return true;
}
MOZ_ASSERT(outLength > length);
MOZ_ASSERT(outLength.value() > length);
// Inflating is fallible, so we have to convert to two-byte upfront.
if constexpr (std::is_same_v<CharT, char16_t>) {
@ -1130,7 +1134,7 @@ template <typename CharT>
}
// Allocate memory for the output using the final length.
if (!sb.reserve(outLength)) {
if (!sb.reserve(outLength.value())) {
return false;
}
@ -1230,19 +1234,20 @@ template <typename CharT>
appendUnescaped(length);
}
MOZ_ASSERT(sb.length() == outLength, "all characters were written");
MOZ_ASSERT(sb.length() == outLength.value(), "all characters were written");
return true;
}
[[nodiscard]] static bool EncodeForRegExpEscape(JSLinearString* string,
[[nodiscard]] static bool EncodeForRegExpEscape(JSContext* cx,
JSLinearString* string,
JSStringBuilder& sb) {
JS::AutoCheckCannotGC nogc;
if (string->hasLatin1Chars()) {
auto chars = mozilla::Span(string->latin1Range(nogc));
return EncodeForRegExpEscape(chars, sb);
return EncodeForRegExpEscape(cx, chars, sb);
}
auto chars = mozilla::Span(string->twoByteRange(nogc));
return EncodeForRegExpEscape(chars, sb);
return EncodeForRegExpEscape(cx, chars, sb);
}
/**
@ -1266,7 +1271,7 @@ static bool regexp_escape(JSContext* cx, unsigned argc, Value* vp) {
// Step 2-5.
JSStringBuilder sb(cx);
if (!EncodeForRegExpEscape(string, sb)) {
if (!EncodeForRegExpEscape(cx, string, sb)) {
return false;
}

View file

@ -6180,6 +6180,14 @@ static bool Deserialize(JSContext* cx, unsigned argc, Value* vp) {
}
}
if (scope > JS::StructuredCloneScope::SameProcess &&
(policy.areIntraClusterClonableSharedObjectsAllowed() ||
policy.areSharedMemoryObjectsAllowed())) {
JS_ReportErrorASCII(
cx, "deserialize in DifferentProcess scope cannot allow shared memory");
return false;
}
// Clone buffer was already consumed?
if (!obj->data()) {
JS_ReportErrorASCII(cx,

View file

@ -24,3 +24,47 @@ assertEq(ex.toString(),
`TypeError: The SharedArrayBuffer object cannot be serialized. The ` +
`Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy HTTP ` +
`headers can be used to enable this.`);
try {
const s = serialize([sab], undefined, { scope: "DifferentProcess", SharedArrayBuffer: "allow" });
deserialize(s, { scope: "DifferentProcess" });
assertEq("threw exception?", true);
} catch (e) {
ex = e;
}
assertEq(ex.toString().includes("Policy object must forbid cloning shared memory objects cross-process"), true);
// Can't deserialize a SameProcess buffer when only allowing DifferentProcess scope.
try {
const s = serialize([sab], undefined, { SharedArrayBuffer: "allow" });
deserialize(s, { scope: "DifferentProcess" });
assertEq("threw exception?", true);
} catch (e) {
ex = e;
}
assertEq(ex.toString().includes("incompatible structured clone scope"), true);
// If a buffer is tampered with, it can only be deserialized as DifferentProcess.
try {
const s = serialize([sab], undefined, { SharedArrayBuffer: "allow" });
const s2 = serialize([sab], undefined, { SharedArrayBuffer: "allow" });
const ta = new Uint32Array(s.arraybuffer);
ta[2] = 2; // DifferentProcess
// synthetic buffer! Forces scope to DifferentProcess despite what we say below.
s2.clonebuffer = ta.buffer;
const result = deserialize(s2, { SharedArrayBuffer: "allow", scope: "SameProcess" });
assertEq("threw exception?", true);
} catch (e) {
ex = e;
}
assertEq(ex.toString().includes("Cannot use less restrictive scope"), true);
// You can't deserialize with both scope=DifferentProcess and allowing shared memory.
try {
const s = serialize([sab], undefined, { SharedArrayBuffer: "allow" });
const result = deserialize(s, { SharedArrayBuffer: "allow", scope: "DifferentProcess" });
assertEq("threw exception?", true);
} catch (e) {
ex = e;
}
assertEq(ex.toString().includes("deserialize in DifferentProcess scope cannot allow shared memory"), true);

View file

@ -42,7 +42,7 @@ for ( let memtype of memtypes ) {
// Serialization and deserialization of shared memories work:
let mem2 = deserialize(serialize(mem1, [], {SharedArrayBuffer: 'allow'}), {SharedArrayBuffer: 'allow'});
let mem2 = deserialize(serialize(mem1, [], {SharedArrayBuffer: 'allow'}), {SharedArrayBuffer: 'allow', scope: 'SameProcess'});
assertEq(mem2 instanceof WebAssembly.Memory, true);
let buf2 = mem2.buffer;
assertEq(buf2 instanceof SharedArrayBuffer, true);
@ -102,6 +102,6 @@ for ( let memtype of memtypes ) {
let buf = mem.buffer;
let clonedbuf = serialize(buf, [], {SharedArrayBuffer: 'allow'});
mem.grow(Idx(memtype, 1));
let buf2 = deserialize(clonedbuf, {SharedArrayBuffer: 'allow'});
let buf2 = deserialize(clonedbuf, {SharedArrayBuffer: 'allow', scope: 'SameProcess'});
assertEq(buf.byteLength, buf2.byteLength);
}

View file

@ -2980,14 +2980,23 @@ void BaselineCacheIRCompiler::pushArguments(Register argcReg,
Register scratch, Register scratch2,
CallFlags flags, uint32_t argcFixed,
bool isJitCall) {
bool isConstructing = flags.isConstructing();
// Push the formal arguments, and possibly `this` and/or `callee`.
// There are three cases:
// 1. Non-scripted call: all arguments are pushed here.
// 2. Scripted call: all arguments except `callee` are pushed here. `callee`
// must be passed as a CalleeToken, and is pushed below.
// 3. Scripted constructor: only formal arguments are pushed here. We must
// push a new `this` value using createThis, and then push `callee` as
// a CalleeToken. Note that constructors must be Standard or Spread.
switch (flags.getArgFormat()) {
case CallFlags::Standard:
pushStandardArguments(argcReg, scratch, scratch2, argcFixed, isJitCall,
flags.isConstructing());
isConstructing);
break;
case CallFlags::Spread:
pushArrayArguments(argcReg, scratch, scratch2, isJitCall,
flags.isConstructing());
pushArrayArguments(argcReg, scratch, scratch2, isJitCall, isConstructing);
break;
case CallFlags::FunCall:
pushFunCallArguments(argcReg, calleeReg, scratch, scratch2, argcFixed,
@ -3006,6 +3015,16 @@ void BaselineCacheIRCompiler::pushArguments(Register argcReg,
default:
MOZ_CRASH("Invalid arg format");
}
if (isJitCall) {
if (isConstructing) {
createThis(argcReg, calleeReg, scratch, scratch2, flags);
}
// Note that we use Push, not push, so that callJit will align the stack
// properly on ARM.
masm.PushCalleeToken(calleeReg, isConstructing);
}
}
void BaselineCacheIRCompiler::pushStandardArguments(
@ -3013,11 +3032,16 @@ void BaselineCacheIRCompiler::pushStandardArguments(
bool isJitCall, bool isConstructing) {
MOZ_ASSERT(enteredStubFrame_);
// The arguments to the call IC are pushed on the stack left-to-right.
// Our calling conventions want them right-to-left in the callee, so
// we duplicate them on the stack in reverse order.
// The arguments to the call IC were pushed on the stack from left to right,
// meaning that the first argument is at the highest address and the last
// argument is at the lowest address. Our callee needs them to be in the
// opposite order, so we duplicate them now.
bool shouldCopyCallee = !isJitCall;
bool shouldCopyThis = shouldCopyCallee || !isConstructing;
bool shouldCopyNewTarget = isConstructing;
int additionalArgc = shouldCopyCallee + shouldCopyThis + shouldCopyNewTarget;
int additionalArgc = 1 + !isJitCall + isConstructing;
if (argcFixed < MaxUnrolledArgCopy) {
#ifdef DEBUG
Label ok;
@ -3029,7 +3053,8 @@ void BaselineCacheIRCompiler::pushStandardArguments(
size_t realArgc = argcFixed + additionalArgc;
if (isJitCall) {
masm.alignJitStackBasedOnNArgs(realArgc, /*countIncludesThis = */ true);
masm.alignJitStackBasedOnNArgs(realArgc,
/*countIncludesThis = */ shouldCopyThis);
}
for (size_t i = 0; i < realArgc; ++i) {
@ -3057,7 +3082,8 @@ void BaselineCacheIRCompiler::pushStandardArguments(
// Align the stack such that the JitFrameLayout is aligned on the
// JitStackAlignment.
if (isJitCall) {
masm.alignJitStackBasedOnNArgs(countReg, /*countIncludesThis = */ true);
masm.alignJitStackBasedOnNArgs(countReg,
/*countIncludesThis = */ shouldCopyThis);
}
// Push all values, starting at the last one.
@ -3120,15 +3146,15 @@ void BaselineCacheIRCompiler::pushArrayArguments(Register argcReg,
masm.jump(&copyStart);
masm.bind(&copyDone);
// Push |this|.
size_t thisvOffset =
BaselineStubFrameLayout::Size() + (1 + isConstructing) * sizeof(Value);
masm.pushValue(Address(FramePointer, thisvOffset));
bool shouldPushCallee = !isJitCall;
bool shouldPushThis = shouldPushCallee || !isConstructing;
// Push |callee| if needed.
if (!isJitCall) {
size_t calleeOffset =
BaselineStubFrameLayout::Size() + (2 + isConstructing) * sizeof(Value);
if (shouldPushThis) {
size_t thisvOffset = arrayOffset + sizeof(Value);
masm.pushValue(Address(FramePointer, thisvOffset));
}
if (shouldPushCallee) {
size_t calleeOffset = arrayOffset + 2 * sizeof(Value);
masm.pushValue(Address(FramePointer, calleeOffset));
}
}
@ -3355,14 +3381,7 @@ void BaselineCacheIRCompiler::pushBoundFunctionArguments(
}
}
if (isConstructing) {
// Push the |this| Value. This is either the object we allocated or the
// JS_UNINITIALIZED_LEXICAL magic value. It's stored in the BaselineFrame,
// so skip past the stub frame, (unbound) arguments and newTarget.
BaseValueIndex thisAddress(FramePointer, argcReg,
BaselineStubFrameLayout::Size() + sizeof(Value));
masm.pushValue(thisAddress, scratch);
} else {
if (!isConstructing) {
// Push the bound |this|.
Address boundThis(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot());
masm.pushValue(boundThis);
@ -3578,15 +3597,16 @@ bool BaselineCacheIRCompiler::emitCallClassHook(ObjOperandId calleeId,
// and unboxes an object from a specific slot.
void BaselineCacheIRCompiler::loadStackObject(ArgumentKind kind,
CallFlags flags, Register argcReg,
Register dest) {
Register dest,
uint32_t extraArgs) {
MOZ_ASSERT(enteredStubFrame_);
bool addArgc = false;
int32_t slotIndex = GetIndexOfArgument(kind, flags, &addArgc);
if (addArgc) {
int32_t slotOffset =
slotIndex * sizeof(JS::Value) + BaselineStubFrameLayout::Size();
int32_t slotOffset = (slotIndex - extraArgs) * sizeof(JS::Value) +
BaselineStubFrameLayout::Size();
BaseValueIndex slotAddr(FramePointer, argcReg, slotOffset);
masm.unboxObject(slotAddr, dest);
} else {
@ -3597,50 +3617,33 @@ void BaselineCacheIRCompiler::loadStackObject(ArgumentKind kind,
}
}
template <typename T>
void BaselineCacheIRCompiler::storeThis(const T& newThis, Register argcReg,
CallFlags flags) {
switch (flags.getArgFormat()) {
case CallFlags::Standard: {
BaseValueIndex thisAddress(
FramePointer,
argcReg, // Arguments
1 * sizeof(Value) + // NewTarget
BaselineStubFrameLayout::Size()); // Stub frame
masm.storeValue(newThis, thisAddress);
} break;
case CallFlags::Spread: {
Address thisAddress(FramePointer,
2 * sizeof(Value) + // Arg array, NewTarget
BaselineStubFrameLayout::Size()); // Stub frame
masm.storeValue(newThis, thisAddress);
} break;
default:
MOZ_CRASH("Invalid arg format for scripted constructor");
}
}
/*
* Scripted constructors require a |this| object to be created prior to the
* call. When this function is called, the stack looks like (bottom->top):
*
* [..., Callee, ThisV, Arg0V, ..., ArgNV, NewTarget, StubFrameHeader]
*
* At this point, |ThisV| is JSWhyMagic::JS_IS_CONSTRUCTING.
*
* This function calls CreateThis to generate a new |this| object, then
* overwrites the magic ThisV on the stack.
* call. This is called after we have pushed the formal arguments, but before
* pushing the callee token. When this is called, argcReg must contain the
* number of actual arguments (including bound or spread arguments; not
* including `undef` pushed in cases of argument underflow). calleeReg should
* contain the actual callee.
*/
void BaselineCacheIRCompiler::createThis(Register argcReg, Register calleeReg,
Register scratch, CallFlags flags,
bool isBoundFunction) {
Register scratch, Register scratch2,
CallFlags flags,
Maybe<uint32_t> numBoundArgs) {
MOZ_ASSERT(flags.isConstructing());
bool isBoundFunction = numBoundArgs.isSome();
// Derived constructors don't allocate a `this` object. They instead call
// `super`, and the base class constructor will allocate `this`.
if (flags.needsUninitializedThis()) {
storeThis(MagicValue(JS_UNINITIALIZED_LEXICAL), argcReg, flags);
masm.Push(MagicValue(JS_UNINITIALIZED_LEXICAL));
return;
}
// Save a reference to the start of the arguments, so that we can root
// them in CreateThisFromIC.
Register argvReg = scratch2;
masm.moveStackPtrTo(argvReg);
// Save live registers that don't have to be traced.
LiveGeneralRegisterSet liveNonGCRegs;
liveNonGCRegs.add(argcReg);
@ -3648,25 +3651,27 @@ void BaselineCacheIRCompiler::createThis(Register argcReg, Register calleeReg,
// CreateThis takes two arguments: callee, and newTarget.
// Push argv/argc for rooting in CreateThisFromIC
masm.push(argcReg);
masm.push(argvReg);
if (isBoundFunction) {
// Push the bound function's target as callee and newTarget.
Address boundTarget(calleeReg, BoundFunctionObject::offsetOfTargetSlot());
masm.unboxObject(boundTarget, scratch);
masm.push(scratch);
masm.push(scratch);
masm.push(calleeReg);
masm.push(calleeReg);
} else {
// Push newTarget:
loadStackObject(ArgumentKind::NewTarget, flags, argcReg, scratch);
masm.push(scratch);
// Push callee:
loadStackObject(ArgumentKind::Callee, flags, argcReg, scratch);
masm.push(scratch);
// Push callee.
masm.push(calleeReg);
}
// Call CreateThisFromIC.
using Fn =
bool (*)(JSContext*, HandleObject, HandleObject, MutableHandleValue);
bool (*)(JSContext*, HandleObject, HandleObject, Value*, uint32_t,
MutableHandleValue);
callVM<Fn, CreateThisFromIC>(masm);
#ifdef DEBUG
@ -3686,14 +3691,29 @@ void BaselineCacheIRCompiler::createThis(Register argcReg, Register calleeReg,
Address stubAddr(FramePointer, BaselineStubFrameLayout::ICStubOffsetFromFP);
masm.loadPtr(stubAddr, ICStubReg);
// Save |this| value back into pushed arguments on stack.
// Push |this|.
MOZ_ASSERT(!liveNonGCRegs.aliases(JSReturnOperand));
storeThis(JSReturnOperand, argcReg, flags);
masm.Push(TypedOrValueRegister(JSReturnOperand));
// Restore calleeReg. CreateThisFromIC may trigger a GC, so we reload the
// callee from the stub frame (which is traced) instead of spilling it to
// callee from the caller's frame (which is traced) instead of spilling it to
// the stack.
loadStackObject(ArgumentKind::Callee, flags, argcReg, calleeReg);
if (isBoundFunction) {
// Load the callee (which is a bound function).
// At this point, argcReg is the number of actual arguments being passed.
// For bound functions, this includes bound arguments. However, to compute
// the address of `callee` in the caller's frame, we need to know how many
// arguments were passed by the caller. This is argcReg - numBoundArgs.
// We pass in `numBoundArgs` so that loadStackObject can adjust accordingly.
loadStackObject(ArgumentKind::Callee, flags, argcReg, calleeReg,
*numBoundArgs);
// Load the target JSFunction.
Address boundTarget(calleeReg, BoundFunctionObject::offsetOfTargetSlot());
masm.unboxObject(boundTarget, calleeReg);
} else {
loadStackObject(ArgumentKind::Callee, flags, argcReg, calleeReg);
}
}
void BaselineCacheIRCompiler::updateReturnValue() {
@ -3753,11 +3773,6 @@ bool BaselineCacheIRCompiler::emitCallScriptedFunction(ObjOperandId calleeId,
masm.switchToObjectRealm(calleeReg, scratch);
}
if (isConstructing) {
createThis(argcReg, calleeReg, scratch, flags,
/* isBoundFunction = */ false);
}
pushArguments(argcReg, calleeReg, scratch, scratch2, flags, argcFixed,
/*isJitCall =*/true);
@ -3767,7 +3782,6 @@ bool BaselineCacheIRCompiler::emitCallScriptedFunction(ObjOperandId calleeId,
// Note that we use Push, not push, so that callJit will align the stack
// properly on ARM.
masm.PushCalleeToken(calleeReg, isConstructing);
masm.PushFrameDescriptorForJitCall(FrameType::BaselineStub, argcReg, scratch);
// Handle arguments underflow.
@ -3843,11 +3857,11 @@ bool BaselineCacheIRCompiler::emitCallInlinedFunction(ObjOperandId calleeId,
masm.switchToObjectRealm(calleeReg, scratch);
}
pushArguments(argcReg, calleeReg, scratch, scratch2, flags, argcFixed,
/*isJitCall =*/true);
Label baselineScriptDiscarded;
if (isConstructing) {
createThis(argcReg, calleeReg, scratch, flags,
/* isBoundFunction = */ false);
// CreateThisFromIC may trigger a GC and discard the BaselineScript.
// We have already called discardStack, so we can't use a FailurePath.
// Instead, we skip storing the ICScript in the JSContext and use a
@ -3868,12 +3882,8 @@ bool BaselineCacheIRCompiler::emitCallInlinedFunction(ObjOperandId calleeId,
masm.bind(&skip);
}
pushArguments(argcReg, calleeReg, scratch, scratch2, flags, argcFixed,
/*isJitCall =*/true);
// Note that we use Push, not push, so that callJit will align the stack
// properly on ARM.
masm.PushCalleeToken(calleeReg, isConstructing);
masm.PushFrameDescriptorForJitCall(FrameType::BaselineStub, argcReg, scratch);
// Handle arguments underflow.
@ -4035,34 +4045,26 @@ bool BaselineCacheIRCompiler::emitCallBoundScriptedFunction(
AutoStubFrame stubFrame(*this);
stubFrame.enter(masm, scratch);
Address boundTarget(calleeReg, BoundFunctionObject::offsetOfTargetSlot());
// If we're constructing, switch to the target's realm and create |this|. If
// we're not constructing, we switch to the target's realm after pushing the
// arguments and loading the target.
if (isConstructing) {
if (!isSameRealm) {
masm.unboxObject(boundTarget, scratch);
masm.switchToObjectRealm(scratch, scratch);
}
createThis(argcReg, calleeReg, scratch, flags,
/* isBoundFunction = */ true);
}
// Push all arguments, including |this|.
pushBoundFunctionArguments(argcReg, calleeReg, scratch, scratch2, flags,
numBoundArgs, /* isJitCall = */ true);
// Load the target JSFunction.
Address boundTarget(calleeReg, BoundFunctionObject::offsetOfTargetSlot());
masm.unboxObject(boundTarget, calleeReg);
if (!isConstructing && !isSameRealm) {
if (!isSameRealm) {
masm.switchToObjectRealm(calleeReg, scratch);
}
// Update argc.
masm.add32(Imm32(numBoundArgs), argcReg);
if (isConstructing) {
createThis(argcReg, calleeReg, scratch, scratch2, flags,
mozilla::Some(numBoundArgs));
}
// Load the start of the target JitCode.
Register code = scratch2;
masm.loadJitCodeRaw(calleeReg, code);

View file

@ -73,7 +73,7 @@ class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler {
bool updateArgc(CallFlags flags, Register argcReg, Register scratch);
void loadStackObject(ArgumentKind kind, CallFlags flags, Register argcReg,
Register dest);
Register dest, uint32_t extraArgs = 0);
void pushArguments(Register argcReg, Register calleeReg, Register scratch,
Register scratch2, CallFlags flags, uint32_t argcFixed,
bool isJitCall);
@ -93,9 +93,8 @@ class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler {
CallFlags flags, uint32_t numBoundArgs,
bool isJitCall);
void createThis(Register argcReg, Register calleeReg, Register scratch,
CallFlags flags, bool isBoundFunction);
template <typename T>
void storeThis(const T& newThis, Register argcReg, CallFlags flags);
Register scratch2, CallFlags flags,
mozilla::Maybe<uint32_t> numBoundArgs = mozilla::Nothing());
void updateReturnValue();
enum class NativeCallType { Native, ClassHook };

View file

@ -299,7 +299,9 @@ class ICCacheIRStub final : public ICStub {
void trace(JSTracer* trc);
bool traceWeak(JSTracer* trc);
ICCacheIRStub* clone(JSRuntime* rt, ICStubSpace& newSpace);
enum class ICScriptHandling { MarkActive, AssertActive };
ICCacheIRStub* clone(JSRuntime* rt, ICStubSpace& newSpace,
ICScriptHandling icScriptHandling);
// Returns true if this stub can call JS or VM code that can trigger a GC.
bool makesGCCalls() const;

View file

@ -243,6 +243,9 @@ uint32_t CacheIRCloner::getRawInt32Field(uint32_t stubOffset) {
const void* CacheIRCloner::getRawPointerField(uint32_t stubOffset) {
return reinterpret_cast<const void*>(readStubWord(stubOffset));
}
const ICScript* CacheIRCloner::getICScriptField(uint32_t stubOffset) {
return reinterpret_cast<const ICScript*>(readStubWord(stubOffset));
}
uint64_t CacheIRCloner::getRawInt64Field(uint32_t stubOffset) {
return static_cast<uint64_t>(readStubInt64(stubOffset));
}

View file

@ -239,6 +239,7 @@ class StubField {
// These fields take up a single word.
RawInt32,
RawPointer,
ICScript,
Shape,
WeakShape,
WeakGetterSetter,
@ -312,6 +313,8 @@ inline const char* StubFieldTypeName(StubField::Type ty) {
return "RawInt32";
case StubField::Type::RawPointer:
return "RawPointer";
case StubField::Type::ICScript:
return "ICScript";
case StubField::Type::Shape:
return "Shape";
case StubField::Type::WeakShape:

View file

@ -71,6 +71,7 @@ class MOZ_RAII CacheIRCloner {
JitCode* getJitCodeField(uint32_t stubOffset);
uint32_t getRawInt32Field(uint32_t stubOffset);
const void* getRawPointerField(uint32_t stubOffset);
const ICScript* getICScriptField(uint32_t stubOffset);
jsid getIdField(uint32_t stubOffset);
const Value getValueField(uint32_t stubOffset);
uint64_t getRawInt64Field(uint32_t stubOffset);

View file

@ -1119,6 +1119,7 @@ static void InitWordStubField(StubField::Type type, void* dest,
switch (type) {
case StubField::Type::RawInt32:
case StubField::Type::RawPointer:
case StubField::Type::ICScript:
case StubField::Type::AllocSite:
*static_cast<uintptr_t*>(dest) = value;
break;
@ -1179,6 +1180,7 @@ static void InitInt64StubField(StubField::Type type, void* dest,
break;
case StubField::Type::RawInt32:
case StubField::Type::RawPointer:
case StubField::Type::ICScript:
case StubField::Type::AllocSite:
case StubField::Type::Shape:
case StubField::Type::WeakShape:
@ -1209,7 +1211,8 @@ void CacheIRWriter::copyStubData(uint8_t* dest) const {
}
}
ICCacheIRStub* ICCacheIRStub::clone(JSRuntime* rt, ICStubSpace& newSpace) {
ICCacheIRStub* ICCacheIRStub::clone(JSRuntime* rt, ICStubSpace& newSpace,
ICScriptHandling icScriptHandling) {
const CacheIRStubInfo* info = stubInfo();
MOZ_ASSERT(info->makesGCCalls());
@ -1242,6 +1245,15 @@ ICCacheIRStub* ICCacheIRStub::clone(JSRuntime* rt, ICStubSpace& newSpace) {
InitWordStubField(type, dest, *srcField);
src += sizeof(uintptr_t);
dest += sizeof(uintptr_t);
if (type == StubField::Type::ICScript) {
auto* icScript = reinterpret_cast<ICScript*>(*srcField);
if (icScriptHandling == ICScriptHandling::MarkActive) {
icScript->setActive();
} else {
MOZ_ASSERT(icScriptHandling == ICScriptHandling::AssertActive);
MOZ_RELEASE_ASSERT(icScript->active());
}
}
} else {
const uint64_t* srcField = reinterpret_cast<const uint64_t*>(src);
InitInt64StubField(type, dest, *srcField);
@ -1278,6 +1290,7 @@ void jit::TraceCacheIRStub(JSTracer* trc, T* stub,
switch (fieldType) {
case Type::RawInt32:
case Type::RawPointer:
case Type::ICScript:
case Type::RawInt64:
case Type::Double:
break;
@ -1425,6 +1438,7 @@ bool jit::TraceWeakCacheIRStub(JSTracer* trc, T* stub,
return !isDead;
case Type::RawInt32:
case Type::RawPointer:
case Type::ICScript:
case Type::Shape:
case Type::JSObject:
case Type::Symbol:

View file

@ -1931,7 +1931,7 @@
receiver: ObjId
setter: ObjectField
rhs: ValId
icScript: RawPointerField
icScript: ICScriptField
sameRealm: BoolImm
nargsAndFlags: RawInt32Field
@ -2124,7 +2124,7 @@
args:
callee: ObjId
argc: Int32Id
icScript: RawPointerField
icScript: ICScriptField
flags: CallFlagsImm
argcFixed: UInt32Imm
@ -2558,7 +2558,7 @@
args:
receiver: ValId
getter: ObjectField
icScript: RawPointerField
icScript: ICScriptField
sameRealm: BoolImm
nargsAndFlags: RawInt32Field

View file

@ -238,6 +238,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
void writeRawPointerField(const void* ptr) {
addStubField(uintptr_t(ptr), StubField::Type::RawPointer);
}
void writeICScriptField(const ICScript* icScript) {
addStubField(uintptr_t(icScript), StubField::Type::ICScript);
}
void writeIdField(jsid id) {
addStubField(id.asRawBits(), StubField::Type::Id);
}

View file

@ -76,6 +76,7 @@ arg_writer_info = {
"JitCodeField": ("JitCode*", "writeJitCodeField"),
"RawInt32Field": ("uint32_t", "writeRawInt32Field"),
"RawPointerField": ("const void*", "writeRawPointerField"),
"ICScriptField": ("const ICScript*", "writeICScriptField"),
"IdField": ("jsid", "writeIdField"),
"ValueField": ("const Value&", "writeValueField"),
"RawInt64Field": ("uint64_t", "writeRawInt64Field"),
@ -181,6 +182,7 @@ arg_reader_info = {
"JitCodeField": ("uint32_t", "Offset", "reader.stubOffset()"),
"RawInt32Field": ("uint32_t", "Offset", "reader.stubOffset()"),
"RawPointerField": ("uint32_t", "Offset", "reader.stubOffset()"),
"ICScriptField": ("uint32_t", "Offset", "reader.stubOffset()"),
"IdField": ("uint32_t", "Offset", "reader.stubOffset()"),
"ValueField": ("uint32_t", "Offset", "reader.stubOffset()"),
"RawInt64Field": ("uint32_t", "Offset", "reader.stubOffset()"),
@ -276,6 +278,7 @@ arg_spewer_method = {
"JitCodeField": "spewField",
"RawInt32Field": "spewField",
"RawPointerField": "spewField",
"ICScriptField": "spewField",
"IdField": "spewField",
"ValueField": "spewField",
"RawInt64Field": "spewField",
@ -418,6 +421,7 @@ arg_length = {
"JitCodeField": 1,
"RawInt32Field": 1,
"RawPointerField": 1,
"ICScriptField": 1,
"RawInt64Field": 1,
"DoubleField": 1,
"IdField": 1,

View file

@ -1745,6 +1745,7 @@ bool SnapshotIterator::allocationReadable(const RValueAllocation& alloc,
case RValueAllocation::INT64_REG:
return hasRegister(alloc.reg());
case RValueAllocation::INT64_STACK:
case RValueAllocation::INT64_INT32_STACK:
return hasStack(alloc.stackOffset());
#endif
@ -1855,6 +1856,7 @@ Value SnapshotIterator::allocationValue(const RValueAllocation& alloc,
#elif defined(JS_PUNBOX64)
case RValueAllocation::INT64_REG:
case RValueAllocation::INT64_STACK:
case RValueAllocation::INT64_INT32_STACK:
#endif
MOZ_CRASH("Can't read Int64 as Value");
@ -1911,6 +1913,7 @@ bool SnapshotIterator::readMaybeUnpackedBigInt(JSContext* cx,
#elif defined(JS_PUNBOX64)
case RValueAllocation::INT64_REG:
case RValueAllocation::INT64_STACK:
case RValueAllocation::INT64_INT32_STACK:
#endif
{
auto* bigInt = JS::BigInt::createFromInt64(cx, allocationInt64(alloc));
@ -1978,6 +1981,9 @@ int64_t SnapshotIterator::allocationInt64(const RValueAllocation& alloc) {
case RValueAllocation::INT64_STACK: {
return static_cast<int64_t>(fromStack(alloc.stackOffset()));
}
case RValueAllocation::INT64_INT32_STACK: {
return static_cast<int64_t>(ReadFrameInt32Slot(fp_, alloc.stackOffset()));
}
#endif
default:
break;
@ -2051,6 +2057,7 @@ void SnapshotIterator::writeAllocationValuePayload(
#elif defined(JS_PUNBOX64)
case RValueAllocation::INT64_REG:
case RValueAllocation::INT64_STACK:
case RValueAllocation::INT64_INT32_STACK:
#endif
MOZ_CRASH("Not a GC thing: Unexpected write");
break;

View file

@ -532,7 +532,8 @@ void ICScript::purgeStubs(Zone* zone, ICStubSpace& newStubSpace) {
ICCacheIRStub* prev = nullptr;
ICStub* stub = entry.firstStub();
while (stub != fallback) {
ICCacheIRStub* clone = stub->toCacheIRStub()->clone(rt, newStubSpace);
ICCacheIRStub* clone = stub->toCacheIRStub()->clone(
rt, newStubSpace, ICCacheIRStub::ICScriptHandling::AssertActive);
if (prev) {
prev->setNext(clone);
} else {
@ -751,26 +752,15 @@ static void MarkActiveICScriptsAndCopyStubs(
ICCacheIRStub* stub = layout->maybeStubPtr()->toCacheIRStub();
auto lookup = alreadyClonedStubs.lookupForAdd(stub);
if (!lookup) {
ICCacheIRStub* newStub = stub->clone(cx->runtime(), newStubSpace);
ICCacheIRStub* newStub =
stub->clone(cx->runtime(), newStubSpace,
ICCacheIRStub::ICScriptHandling::MarkActive);
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!alreadyClonedStubs.add(lookup, stub, newStub)) {
oomUnsafe.crash("MarkActiveICScriptsAndCopyStubs");
}
}
layout->setStubPtr(lookup->value());
// If this is a trial-inlining call site, also preserve the callee
// ICScript. Inlined constructor calls invoke CreateThisFromIC (which
// can trigger GC) before using the inlined ICScript.
JSJitFrameIter parentFrame(frame);
++parentFrame;
BaselineFrame* blFrame = parentFrame.baselineFrame();
jsbytecode* pc;
parentFrame.baselineScriptAndPc(nullptr, &pc);
uint32_t pcOffset = blFrame->script()->pcToOffset(pc);
if (blFrame->icScript()->hasInlinedChild(pcOffset)) {
blFrame->icScript()->findInlinedChild(pcOffset)->setActive();
}
}
break;
}

View file

@ -154,6 +154,9 @@ using namespace js::jit;
// register/stack-offset correspond to the low 32-bits, and the
// second correspond to the high 32-bits.
//
// INT64_INT32_STACK [STACK_OFFSET]: (64-bit platform)
// Unpacked Int64 value stored in int32_t. Payload is stored at an
// offset on the stack.
const RValueAllocation::Layout& RValueAllocation::layoutFromMode(Mode mode) {
switch (mode) {
@ -308,6 +311,12 @@ const RValueAllocation::Layout& RValueAllocation::layoutFromMode(Mode mode) {
PAYLOAD_STACK_OFFSET, PAYLOAD_NONE, "unpacked int64"};
return layout;
}
case INT64_INT32_STACK: {
static const RValueAllocation::Layout layout = {
PAYLOAD_STACK_OFFSET, PAYLOAD_NONE, "unpacked int64 (int32)"};
return layout;
}
#endif
default: {

View file

@ -82,6 +82,7 @@ class RValueAllocation {
#elif defined(JS_PUNBOX64)
INT64_REG = 0x31,
INT64_STACK = 0x32,
INT64_INT32_STACK = 0x33,
#endif
// This mask can be used with any other valid mode. When this flag is
@ -348,6 +349,11 @@ class RValueAllocation {
static RValueAllocation Int64(int32_t stackOffset) {
return RValueAllocation(INT64_STACK, payloadOfStackOffset(stackOffset));
}
static RValueAllocation Int64Int32(int32_t stackOffset) {
return RValueAllocation(INT64_INT32_STACK,
payloadOfStackOffset(stackOffset));
}
#endif
void setNeedSideEffect() {

View file

@ -882,14 +882,23 @@ bool GetIntrinsicValue(JSContext* cx, Handle<PropertyName*> name,
return GlobalObject::getIntrinsicValue(cx, cx->global(), name, rval);
}
static uint32_t NumTraceableArgsForCreateThis(HandleFunction fun,
uint32_t argc) {
return argc + 1; // Add 1 for newTarget
}
bool CreateThisFromIC(JSContext* cx, HandleObject callee,
HandleObject newTarget, MutableHandleValue rval) {
HandleObject newTarget, Value* argv, uint32_t argc,
MutableHandleValue rval) {
HandleFunction fun = callee.as<JSFunction>();
MOZ_ASSERT(fun->isInterpreted());
MOZ_ASSERT(fun->isConstructor());
MOZ_ASSERT(cx->realm() == fun->realm(),
"Realm switching happens before creating this");
RootedExternalValueArray args(cx, NumTraceableArgsForCreateThis(fun, argc),
argv);
// CreateThis expects rval to be this magic value.
rval.set(MagicValue(JS_IS_CONSTRUCTING));

View file

@ -408,8 +408,8 @@ bool OperatorIn(JSContext* cx, HandleValue key, HandleObject obj, bool* out);
MutableHandleValue rval);
[[nodiscard]] bool CreateThisFromIC(JSContext* cx, HandleObject callee,
HandleObject newTarget,
MutableHandleValue rval);
HandleObject newTarget, Value* argv,
uint32_t argc, MutableHandleValue rval);
[[nodiscard]] bool CreateThisFromIon(JSContext* cx, HandleObject callee,
HandleObject newTarget,
MutableHandleValue rval);

View file

@ -1256,6 +1256,7 @@ bool WarpScriptOracle::replaceNurseryAndAllocSitePointers(
switch (fieldType) {
case StubField::Type::RawInt32:
case StubField::Type::RawPointer:
case StubField::Type::ICScript:
case StubField::Type::RawInt64:
case StubField::Type::Double:
break;

View file

@ -341,6 +341,7 @@ void WarpCacheIR::traceData(JSTracer* trc) {
switch (fieldType) {
case StubField::Type::RawInt32:
case StubField::Type::RawPointer:
case StubField::Type::ICScript:
case StubField::Type::RawInt64:
case StubField::Type::Double:
break;

View file

@ -4862,20 +4862,24 @@ void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index,
void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
Register64 boundsCheckLimit, Label* ok) {
Label notOk;
MOZ_ASSERT(cond == Assembler::AboveOrEqual || cond == Assembler::Below);
Label rejoin;
Label* failLabel = cond == Assembler::AboveOrEqual ? ok : &rejoin;
cmp32(index.high, Imm32(0));
j(Assembler::NonZero, &notOk);
j(Assembler::NonZero, failLabel);
wasmBoundsCheck32(cond, index.low, boundsCheckLimit.low, ok);
bind(&notOk);
bind(&rejoin);
}
void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
Address boundsCheckLimit, Label* ok) {
Label notOk;
MOZ_ASSERT(cond == Assembler::AboveOrEqual || cond == Assembler::Below);
Label rejoin;
Label* failLabel = cond == Assembler::AboveOrEqual ? ok : &rejoin;
cmp32(index.high, Imm32(0));
j(Assembler::NonZero, &notOk);
j(Assembler::NonZero, failLabel);
wasmBoundsCheck32(cond, index.low, boundsCheckLimit, ok);
bind(&notOk);
bind(&rejoin);
}
void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input,

View file

@ -599,9 +599,14 @@ void CodeGeneratorShared::encodeAllocation(LSnapshot* snapshot,
if (payload->isGeneralReg()) {
alloc = RValueAllocation::Int64(ToRegister(payload));
} else if (payload->isStackSlot()) {
MOZ_ASSERT(payload->toStackSlot()->width() ==
LStackSlot::width(LDefinition::GENERAL));
alloc = RValueAllocation::Int64(ToStackIndex(payload));
LStackSlot::Width width = payload->toStackSlot()->width();
MOZ_ASSERT(width == LStackSlot::width(LDefinition::GENERAL) ||
width == LStackSlot::width(LDefinition::INT32));
if (width == LStackSlot::width(LDefinition::GENERAL)) {
alloc = RValueAllocation::Int64(ToStackIndex(payload));
} else {
alloc = RValueAllocation::Int64Int32(ToStackIndex(payload));
}
} else {
MOZ_CRASH("Unexpected payload type.");
}

View file

@ -1882,20 +1882,24 @@ void MacroAssembler::patchNearAddressMove(CodeLocationLabel loc,
void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
Register64 boundsCheckLimit, Label* ok) {
Label notOk;
MOZ_ASSERT(cond == Assembler::AboveOrEqual || cond == Assembler::Below);
Label rejoin;
Label* failLabel = cond == Assembler::AboveOrEqual ? ok : &rejoin;
cmp32(index.high, Imm32(0));
j(Assembler::NonZero, &notOk);
j(Assembler::NonZero, failLabel);
wasmBoundsCheck32(cond, index.low, boundsCheckLimit.low, ok);
bind(&notOk);
bind(&rejoin);
}
void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
Address boundsCheckLimit, Label* ok) {
Label notOk;
MOZ_ASSERT(cond == Assembler::AboveOrEqual || cond == Assembler::Below);
Label rejoin;
Label* failLabel = cond == Assembler::AboveOrEqual ? ok : &rejoin;
cmp32(index.high, Imm32(0));
j(Assembler::NonZero, &notOk);
j(Assembler::NonZero, failLabel);
wasmBoundsCheck32(cond, index.low, boundsCheckLimit, ok);
bind(&notOk);
bind(&rejoin);
}
void MacroAssembler::wasmMarkCallAsSlow() {

View file

@ -1032,3 +1032,27 @@ BEGIN_TEST(testRootedTuple) {
return true;
}
END_TEST(testRootedTuple)
BEGIN_TEST(testRootedRealm) {
// Create a new global and use Rooted<Realm*> to keep it alive.
Rooted<Realm*> realm(cx);
{
JS::RealmOptions globalOptions;
JSObject* otherGlobal = JS_NewGlobalObject(
cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook, globalOptions);
CHECK(otherGlobal);
realm = JS::GetObjectRealmOrNull(otherGlobal);
CHECK(realm);
}
JS_GC(cx);
// Use the realm.
JSAutoRealm ar(cx, JS::GetRealmGlobalOrNull(realm));
JS::RootedValue v(cx);
EVAL("let x = -1234; Math.abs(x)", &v);
CHECK(v.toNumber() == 1234);
return true;
}
END_TEST(testRootedRealm)

View file

@ -336,6 +336,15 @@ BEGIN_TEST(testJitRValueAlloc_Int64Stack) {
return true;
}
END_TEST(testJitRValueAlloc_Int64Stack)
BEGIN_TEST(testJitRValueAlloc_Int64Int32Stack) {
for (auto i : Fibonacci{}) {
auto s = RValueAllocation::Int64Int32(i);
CHECK(s == Read(s));
}
return true;
}
END_TEST(testJitRValueAlloc_Int64Int32Stack)
#endif
BEGIN_TEST(testJitRValueAlloc_IntPtrCst) {

View file

@ -773,7 +773,7 @@ class ModuleEnvironmentObject : public EnvironmentObject {
// `env` may be a DebugEnvironmentProxy, but not a hollow environment.
static ModuleEnvironmentObject* find(JSObject* env);
uint32_t firstSyntheticValueSlot() { return RESERVED_SLOTS; }
uint32_t firstSyntheticValueSlot() { return RESERVED_SLOTS + 1; }
private:
static bool lookupProperty(JSContext* cx, HandleObject obj, HandleId id,

View file

@ -42,6 +42,7 @@
#include "vm/Watchtower.h"
#include "vm/NativeObject-inl.h"
#include "vm/PlainObject-inl.h" // js::PlainObject::createWithTemplate
#include "vm/Shape-inl.h" // js::GetPropertyAttributes
using namespace js;
@ -1798,25 +1799,23 @@ static bool SuppressDeletedProperty(JSContext* cx, NativeIterator* ni,
// Check whether another property along the prototype chain became
// visible as a result of this deletion.
RootedObject proto(cx);
if (!GetPrototype(cx, obj, &proto)) {
return false;
}
if (proto) {
RootedId id(cx);
RootedValue idv(cx, StringValue(*idp));
if (!PrimitiveValueToId<CanGC>(cx, idv, &id)) {
return false;
}
Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
RootedObject holder(cx);
if (!GetPropertyDescriptor(cx, proto, id, &desc, &holder)) {
return false;
}
if (desc.isSome() && desc->enumerable()) {
continue;
if (obj->hasStaticPrototype()) {
JSObject* proto = obj->staticPrototype();
if (proto) {
JSAtom* atom = AtomizeString(cx, str);
if (!atom) {
return false;
}
PropertyKey key = AtomToId(atom);
NativeObject* holder = nullptr;
PropertyResult prop;
if (LookupPropertyPure(cx, proto, key, &holder, &prop) &&
prop.isFound()) {
JS::PropertyAttributes attrs = GetPropertyAttributes(holder, prop);
if (attrs.enumerable()) {
continue;
}
}
}
}

View file

@ -1366,8 +1366,9 @@ static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module,
size_t* indexOut) {
// Step 1. If module is not a Cyclic Module Record, then
if (!module->hasCyclicModuleFields()) {
// Step 1.a. Perform ? module.Link(). (Skipped)
// Step 2.b. Return index.
// Step 1.a. Perform ? module.Link().
// (Skipped as we have already created the environment for these modules).
// Step 1.b. Return index.
*indexOut = index;
return true;
}

View file

@ -2355,8 +2355,11 @@ uint64_t ICInterpretOps(uint64_t arg0, uint64_t arg1, ICStub* stub,
ReservedRooted<JSObject*> calleeObj(&ctx.state.obj0, callee);
ReservedRooted<JSObject*> newTargetRooted(
&ctx.state.obj1, &origArgs[0].asValue().toObject());
ReservedRooted<Value> result(&ctx.state.value0);
if (!CreateThisFromIC(cx, calleeObj, newTargetRooted, &result)) {
ReservedRooted<Value> result(&ctx.state.value0,
MagicValue(JS_IS_CONSTRUCTING));
HandleFunction fun = calleeObj.as<JSFunction>();
if (!js::CreateThis(cx, fun, newTargetRooted, GenericObject,
&result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}

View file

@ -250,6 +250,12 @@ void Realm::traceGlobalData(JSTracer* trc) {
DebugAPI::traceFromRealm(trc, this);
}
void Realm::traceGlobalRoot(JSTracer* trc, const char* name) {
if (global_) {
TraceRoot(trc, global_.unbarrieredAddress(), name);
}
}
void ObjectRealm::trace(JSTracer* trc) {
if (objectMetadataTable) {
objectMetadataTable->trace(trc);
@ -272,8 +278,8 @@ void Realm::traceRoots(JSTracer* trc,
//
// If a realm is on-stack, we mark its global so that JSContext::global()
// remains valid.
if (shouldTraceGlobal() && global_) {
TraceRoot(trc, global_.unbarrieredAddress(), "on-stack realm global");
if (shouldTraceGlobal()) {
traceGlobalRoot(trc, "on-stack realm global");
}
// If the realm is still being initialized we set a flag so that it doesn't
@ -629,16 +635,16 @@ void AutoSetNewObjectMetadata::setPendingMetadata() {
(void)SetNewObjectMetadata(cx_, obj);
}
JS_PUBLIC_API void gc::TraceRealm(JSTracer* trc, JS::Realm* realm,
const char* name) {
// The way GC works with compartments is basically incomprehensible.
// For Realms, what we want is very simple: each Realm has a strong
// reference to its GlobalObject, and vice versa.
JS_PUBLIC_API void gc::TraceRealmRoot(JSTracer* trc, JS::Realm* realm,
const char* name) {
// Trace the realm's global object to keep the realm alive.
//
// Here we simply trace our side of that edge. During GC,
// GCRuntime::traceRuntimeCommon() marks all other realm roots, for
// all realms.
realm->traceGlobalData(trc);
// Note: this is called for Rooted<Realm*>. If a realm has been entered with
// AutoRealm, the global object is traced in Realm::traceRoots.
MOZ_RELEASE_ASSERT(realm->hasLiveGlobal(),
"we need to have a global to keep the realm alive");
gc::AssertRootMarkingPhase(trc);
realm->traceGlobalRoot(trc, "rooted realm");
}
JS_PUBLIC_API JS::Realm* JS::GetCurrentRealmOrNull(JSContext* cx) {

View file

@ -542,6 +542,8 @@ class JS::Realm : public JS::shadow::Realm {
*/
void traceGlobalData(JSTracer* trc);
void traceGlobalRoot(JSTracer* trc, const char* name);
void traceWeakGlobalEdge(JSTracer* trc);
/*

View file

@ -161,11 +161,22 @@ SharedShape* js::CreateEnvironmentShapeForSyntheticModule(
RootedId id(cx);
uint32_t slotIndex = numSlots;
auto addProperty = [&](PropertyName* name) {
id = NameToId(name);
return SharedPropMap::addPropertyWithKnownSlot(
cx, cls, &map, &mapLength, id, propFlags, slotIndex, &objectFlags);
};
// Add internal *namespace* property.
if (!addProperty(cx->names().star_namespace_star_)) {
return nullptr;
}
slotIndex++;
// Add synthetic exports.
for (JSAtom* exportName : module->syntheticExportNames()) {
id = NameToId(exportName->asPropertyName());
if (!SharedPropMap::addPropertyWithKnownSlot(cx, cls, &map, &mapLength, id,
propFlags, slotIndex,
&objectFlags)) {
if (!addProperty(exportName->asPropertyName())) {
return nullptr;
}
slotIndex++;

View file

@ -2564,6 +2564,10 @@ JSStructuredCloneReader::JSStructuredCloneReader(
callbacks(cb),
closure(cbClosure),
gcHeap(in.context()) {
// Readers should never enable SAB for a DifferentProcess scope.
MOZ_RELEASE_ASSERT(!(scope == JS::StructuredCloneScope::DifferentProcess &&
cloneDataPolicy.areSharedMemoryObjectsAllowed()));
// Avoid the need to bounds check by keeping a never-matching element at the
// base of the `objState` stack. This append() will always succeed because
// the objState vector has a nonzero MinInlineCapacity.
@ -3444,6 +3448,12 @@ bool JSStructuredCloneReader::readHeader() {
return false;
}
if (allowedScope == JS::StructuredCloneScope::DifferentProcess) {
MOZ_RELEASE_ASSERT(
!cloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed());
MOZ_RELEASE_ASSERT(!cloneDataPolicy.areSharedMemoryObjectsAllowed());
}
return true;
}

View file

@ -8816,27 +8816,25 @@ bool BaseCompiler::emitArrayFill() {
freePtr(RegPtr(PreBarrierReg));
}
// Perform an initialization loop using `numElements` as the loop variable,
// starting at `numElements` and counting down to zero.
// Perform the fill loop using `numElements` as the loop variable, counting
// down to zero.
Label done;
Label loop;
// Skip initialization if numElements = 0
masm.branch32(Assembler::Equal, numElements, Imm32(0), &done);
masm.bind(&loop);
// Move to the next element
masm.bind(&loop);
masm.sub32(Imm32(1), numElements);
// Assign value to rdata[numElements]. All registers are preserved.
if (!emitGcArraySet(rp, rdata, numElements, arrayType, value,
PreBarrierKind::None)) {
PreBarrierKind::Normal)) {
return false;
}
// Loop back if there are still elements to initialize
masm.branch32(Assembler::NotEqual, numElements, Imm32(0), &loop);
masm.bind(&done);
// Clean up
freePtr(rdata);
freeRef(rp);
freeI32(numElements);