Mix 55 js engine backporting mostly bugfixes.

1369337 - Forcibly create length and @@iterator properties for arguments when redefining.

1369680 - Use HasOwnProperty when resolving lazy properties to avoid triggering proxy traps in the proto-chain.

1319443 - Fix GlobalScopes XDR to encode global function bindings.

1335571 - Use template craziness to allow covering more cases with compartment asserts.
This commit is contained in:
win7-7 2025-12-26 10:47:29 +02:00 committed by wuggy
commit d116bfdfef
7 changed files with 107 additions and 25 deletions

View file

@ -2611,7 +2611,7 @@ JS_ForwardSetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValu
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, id, receiver);
assertSameCompartment(cx, obj, id, v, receiver);
return SetProperty(cx, obj, id, v, receiver, result);
}
@ -2621,7 +2621,7 @@ JS_SetPropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, id);
assertSameCompartment(cx, obj, id, v);
RootedValue receiver(cx, ObjectValue(*obj));
ObjectOpResult ignored;
@ -2775,7 +2775,7 @@ JS_Enumerate(JSContext* cx, HandleObject obj, JS::MutableHandle<IdVector> props)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj);
assertSameCompartment(cx, obj, props);
MOZ_ASSERT(props.empty());
AutoIdVector ids(cx);

View file

@ -82,6 +82,25 @@ class CompartmentChecker
check(handle.get());
}
template<typename T>
void check(MutableHandle<T> handle) {
check(handle.get());
}
template <typename T>
void checkAtom(T* thing) {
static_assert(mozilla::IsSame<T, JSAtom>::value ||
mozilla::IsSame<T, JS::Symbol>::value,
"Should only be called with JSAtom* or JS::Symbol* argument");
#ifdef DEBUG
// Atoms which move across zone boundaries need to be marked in the new
// zone, see JS_MarkCrossZoneId.
if (compartment) {
JSRuntime* rt = compartment->runtimeFromAnyThread();
MOZ_ASSERT(rt->gc.atomMarking.atomIsMarked(compartment->zone(), thing));
}
#endif
}
void check(JSString* str) {
MOZ_ASSERT(!js::gc::detail::CellIsMarkedGray(str));
if (!str->isAtom())
@ -95,6 +114,20 @@ class CompartmentChecker
check(v.toString());
}
// Check the contents of any container class that supports the C++
// iteration protocol, eg GCVector<jsid>.
template <typename Container>
typename mozilla::EnableIf<
mozilla::IsSame<
decltype(((Container*)nullptr)->begin()),
decltype(((Container*)nullptr)->end())
>::value
>::Type
check(const Container& container) {
for (auto i : container)
check(i);
}
void check(const ValueArray& arr) {
for (size_t i = 0; i < arr.length; i++)
check(arr.array[i]);

View file

@ -82,16 +82,16 @@ fun_enumerate(JSContext* cx, HandleObject obj)
if (!obj->isBoundFunction() && !obj->as<JSFunction>().isArrow()) {
id = NameToId(cx->names().prototype);
if (!HasProperty(cx, obj, id, &found))
if (!HasOwnProperty(cx, obj, id, &found))
return false;
}
id = NameToId(cx->names().length);
if (!HasProperty(cx, obj, id, &found))
if (!HasOwnProperty(cx, obj, id, &found))
return false;
id = NameToId(cx->names().name);
if (!HasProperty(cx, obj, id, &found))
if (!HasOwnProperty(cx, obj, id, &found))
return false;
return true;

View file

@ -514,6 +514,34 @@ DefineArgumentsIterator(JSContext* cx, Handle<ArgumentsObject*> argsobj)
return NativeDefineProperty(cx, argsobj, iteratorId, val, nullptr, nullptr, JSPROP_RESOLVING);
}
/* static */ bool
ArgumentsObject::reifyLength(JSContext* cx, Handle<ArgumentsObject*> obj)
{
if (obj->hasOverriddenLength())
return true;
RootedId id(cx, NameToId(cx->names().length));
RootedValue val(cx, Int32Value(obj->initialLength()));
if (!NativeDefineProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_RESOLVING))
return false;
obj->markLengthOverridden();
return true;
}
/* static */ bool
ArgumentsObject::reifyIterator(JSContext* cx, Handle<ArgumentsObject*> obj)
{
if (obj->hasOverriddenIterator())
return true;
if (!DefineArgumentsIterator(cx, obj))
return false;
obj->markIteratorOverridden();
return true;
}
/* static */ bool
MappedArgumentsObject::obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
{
@ -567,20 +595,20 @@ MappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
// Trigger reflection.
id = NameToId(cx->names().length);
if (!HasProperty(cx, argsobj, id, &found))
if (!HasOwnProperty(cx, argsobj, id, &found))
return false;
id = NameToId(cx->names().callee);
if (!HasProperty(cx, argsobj, id, &found))
if (!HasOwnProperty(cx, argsobj, id, &found))
return false;
id = SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator);
if (!HasProperty(cx, argsobj, id, &found))
if (!HasOwnProperty(cx, argsobj, id, &found))
return false;
for (unsigned i = 0; i < argsobj->initialLength(); i++) {
id = INT_TO_JSID(i);
if (!HasProperty(cx, argsobj, id, &found))
if (!HasOwnProperty(cx, argsobj, id, &found))
return false;
}
@ -757,20 +785,20 @@ UnmappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
// Trigger reflection.
id = NameToId(cx->names().length);
if (!HasProperty(cx, argsobj, id, &found))
if (!HasOwnProperty(cx, argsobj, id, &found))
return false;
id = NameToId(cx->names().callee);
if (!HasProperty(cx, argsobj, id, &found))
if (!HasOwnProperty(cx, argsobj, id, &found))
return false;
id = SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator);
if (!HasProperty(cx, argsobj, id, &found))
if (!HasOwnProperty(cx, argsobj, id, &found))
return false;
for (unsigned i = 0; i < argsobj->initialLength(); i++) {
id = INT_TO_JSID(i);
if (!HasProperty(cx, argsobj, id, &found))
if (!HasOwnProperty(cx, argsobj, id, &found))
return false;
}

View file

@ -232,6 +232,11 @@ class ArgumentsObject : public NativeObject
setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
}
/*
* Create the default "length" property and set LENGTH_OVERRIDDEN_BIT.
*/
static bool reifyLength(JSContext* cx, Handle<ArgumentsObject*> obj);
/* True iff arguments[@@iterator] has been assigned or its attributes
* changed. */
bool hasOverriddenIterator() const {
@ -243,6 +248,11 @@ class ArgumentsObject : public NativeObject
uint32_t v = getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | ITERATOR_OVERRIDDEN_BIT;
setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
}
/*
* Create the default @@iterator property and set ITERATOR_OVERRIDDEN_BIT.
*/
static bool reifyIterator(JSContext* cx, Handle<ArgumentsObject*> obj);
/* True iff any element has been assigned or its attributes
* changed. */

View file

@ -1453,21 +1453,26 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
return DefineTypedArrayElement(cx->asJSContext(), obj, index, desc_, result);
}
} else if (obj->is<ArgumentsObject>()) {
Rooted<ArgumentsObject*> argsobj(cx, &obj->as<ArgumentsObject>());
if (id == NameToId(cx->names().length)) {
// Either we are resolving the .length property on this object, or
// redefining it. In the latter case only, we must set a bit. To
// distinguish the two cases, we note that when resolving, the
// property won't already exist; whereas the first time it is
// redefined, it will.
if ((desc_.attributes() & JSPROP_RESOLVING) == 0)
obj->as<ArgumentsObject>().markLengthOverridden();
// Either we are resolving the .length property on this object,
// or redefining it. In the latter case only, we must reify the
// property. To distinguish the two cases, we note that when
// resolving, the JSPROP_RESOLVING mask is set; whereas the first
// time it is redefined, it isn't set.
if ((desc_.attributes() & JSPROP_RESOLVING) == 0) {
if (!ArgumentsObject::reifyLength(cx, argsobj))
return false;
}
} else if (JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator) {
// Do same thing as .length for [@@iterator].
if ((desc_.attributes() & JSPROP_RESOLVING) == 0)
obj->as<ArgumentsObject>().markIteratorOverridden();
if ((desc_.attributes() & JSPROP_RESOLVING) == 0) {
if (!ArgumentsObject::reifyIterator(cx, argsobj))
return false;
}
} else if (JSID_IS_INT(id)) {
if ((desc_.attributes() & JSPROP_RESOLVING) == 0)
obj->as<ArgumentsObject>().markElementOverridden();
argsobj->markElementOverridden();
}
}

View file

@ -972,6 +972,8 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
if (mode == XDR_DECODE)
uniqueData.emplace(cx, data);
if (!xdr->codeUint32(&data->varStart))
return false;
if (!xdr->codeUint32(&data->letStart))
return false;
if (!xdr->codeUint32(&data->constStart))
@ -979,6 +981,7 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
if (mode == XDR_DECODE) {
if (!data->length) {
MOZ_ASSERT(!data->varStart);
MOZ_ASSERT(!data->letStart);
MOZ_ASSERT(!data->constStart);
}
@ -1389,7 +1392,10 @@ js::DumpBindings(JSContext* cx, Scope* scopeArg)
fprintf(stderr, "%s %s ", BindingKindString(bi.kind()), bytes.ptr());
switch (bi.location().kind()) {
case BindingLocation::Kind::Global:
fprintf(stderr, "global\n");
if (bi.isTopLevelFunction())
fprintf(stderr, "global function\n");
else
fprintf(stderr, "global\n");
break;
case BindingLocation::Kind::Argument:
fprintf(stderr, "arg slot %u\n", bi.location().argumentSlot());