icecat: add release icecat-140.8.0-2 for aramo
This commit is contained in:
parent
d9a6c0aa96
commit
d570f39e11
616 changed files with 39955 additions and 33937 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(©Start);
|
||||
masm.bind(©Done);
|
||||
|
||||
// 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);
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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, ¬Ok);
|
||||
j(Assembler::NonZero, failLabel);
|
||||
wasmBoundsCheck32(cond, index.low, boundsCheckLimit.low, ok);
|
||||
bind(¬Ok);
|
||||
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, ¬Ok);
|
||||
j(Assembler::NonZero, failLabel);
|
||||
wasmBoundsCheck32(cond, index.low, boundsCheckLimit, ok);
|
||||
bind(¬Ok);
|
||||
bind(&rejoin);
|
||||
}
|
||||
|
||||
void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input,
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ¬Ok);
|
||||
j(Assembler::NonZero, failLabel);
|
||||
wasmBoundsCheck32(cond, index.low, boundsCheckLimit.low, ok);
|
||||
bind(¬Ok);
|
||||
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, ¬Ok);
|
||||
j(Assembler::NonZero, failLabel);
|
||||
wasmBoundsCheck32(cond, index.low, boundsCheckLimit, ok);
|
||||
bind(¬Ok);
|
||||
bind(&rejoin);
|
||||
}
|
||||
|
||||
void MacroAssembler::wasmMarkCallAsSlow() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue