diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index 3bbbd19d3b61d..85065e26c8963 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -2948,6 +2948,10 @@ void JSObject::reifyAllStaticProperties(JSGlobalObject* globalObject) if (!structure()->isDictionary()) convertToDictionary(vm); + // A PropertyCallback builder can enter JS; defer termination (like + // LazyProperty::callFunc) so it can't return with one pending. + auto scope = DECLARE_THROW_SCOPE(vm); + DeferTerminationForAWhile deferScope(vm); for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { const HashTable* hashTable = info->staticPropHashTable; if (!hashTable) @@ -2957,8 +2961,11 @@ void JSObject::reifyAllStaticProperties(JSGlobalObject* globalObject) unsigned attributes; auto key = Identifier::fromString(vm, value.m_key); PropertyOffset offset = getDirectOffset(vm, key, attributes); - if (!isValidOffset(offset)) + if (!isValidOffset(offset)) { reifyStaticProperty(vm, hashTable->classForThis, key, value, *this); + // Leave the rest lazy on throw; the caller propagates. + RETURN_IF_EXCEPTION(scope, void()); + } } } diff --git a/Source/JavaScriptCore/runtime/Lookup.cpp b/Source/JavaScriptCore/runtime/Lookup.cpp index d0f861c34d3bc..fd4b6e3273316 100644 --- a/Source/JavaScriptCore/runtime/Lookup.cpp +++ b/Source/JavaScriptCore/runtime/Lookup.cpp @@ -20,6 +20,7 @@ #include "config.h" #include "Lookup.h" +#include "DeferTermination.h" #include "GetterSetter.h" #include "JSCInlines.h" #include @@ -58,7 +59,17 @@ bool setUpStaticFunctionSlot(VM& vm, const ClassInfo* classInfo, const HashTable if (thisObject->staticPropertiesReified()) return false; - reifyStaticProperty(vm, classInfo, propertyName, *entry, *thisObject); + auto scope = DECLARE_THROW_SCOPE(vm); + { + // A PropertyCallback builder can enter JS; defer termination (like + // LazyProperty::callFunc) so it can't return with one pending. + DeferTerminationForAWhile deferScope(vm); + reifyStaticProperty(vm, classInfo, propertyName, *entry, *thisObject); + } + // The builder may still throw a non-termination exception; report the + // slot as not found so JSValue::get / getOwnPropertyDescriptor's + // EXCEPTION_ASSERT(!scope.exception() || !result) holds. + RETURN_IF_EXCEPTION(scope, false); offset = thisObject->getDirectOffset(vm, propertyName, attributes); if (!isValidOffset(offset)) { diff --git a/Source/JavaScriptCore/runtime/Lookup.h b/Source/JavaScriptCore/runtime/Lookup.h index e2a95e996d1e1..a7134ccf7e0b9 100644 --- a/Source/JavaScriptCore/runtime/Lookup.h +++ b/Source/JavaScriptCore/runtime/Lookup.h @@ -538,6 +538,11 @@ inline void reifyStaticProperty(VM& vm, const ClassInfo* classInfo, const Proper if (value.attributes() & PropertyAttribute::PropertyCallback) { JSValue result = value.lazyPropertyCallback()(vm, &thisObj); + // A callback that enters JS may return empty with an exception pending; + // the two callers (setUpStaticFunctionSlot / reifyAllStaticProperties) + // check and propagate, so don't put an empty value in the slot here. + if (!result) [[unlikely]] + return; thisObj.putDirect(vm, propertyName, result, attributesForStructure(value.attributes())); return; }