From 00ae02e4c661357fd30a8153c58905eccc57be78 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Tue, 24 Oct 2023 02:14:29 -0700 Subject: [PATCH] Support overriding ESM exports for already loaded modules --- .../runtime/JSModuleNamespaceObject.cpp | 69 ++++++++++++++++++- .../runtime/JSModuleNamespaceObject.h | 8 +++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp b/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp index b52bb4d46f020..158bb8a4a3cbc 100644 --- a/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp +++ b/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp @@ -29,6 +29,7 @@ #include "AbstractModuleRecord.h" #include "JSCInlines.h" #include "JSModuleEnvironment.h" +#include "JSModuleRecord.h" namespace JSC { @@ -53,7 +54,7 @@ void JSModuleNamespaceObject::finishCreation(JSGlobalObject* globalObject, Abstr // The list is ordered as if an Array of those String values had been sorted using Array.prototype.sort using SortCompare as comparator. // // Sort the exported names by the code point order. - std::sort(resolutions.begin(), resolutions.end(), [] (const auto& lhs, const auto& rhs) { + std::sort(resolutions.begin(), resolutions.end(), [](const auto& lhs, const auto& rhs) { return codePointCompare(lhs.first.impl(), rhs.first.impl()) < 0; }); @@ -191,11 +192,18 @@ bool JSModuleNamespaceObject::getOwnPropertySlotByIndex(JSObject* cell, JSGlobal return thisObject->getOwnPropertySlotCommon(globalObject, Identifier::from(vm, propertyName), slot); } -bool JSModuleNamespaceObject::put(JSCell*, JSGlobalObject* globalObject, PropertyName, JSValue, PutPropertySlot& slot) +bool JSModuleNamespaceObject::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName, JSValue, PutPropertySlot& slot) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); +#if USE(BUN_JSC_ADDITIONS) + auto* thisObject = jsCast(cell); + if (thisObject->m_isOverridingValue) { + return true; + } +#endif + // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-set-p-v-receiver if (slot.isStrictMode()) throwTypeError(globalObject, scope, ReadonlyPropertyWriteError); @@ -269,6 +277,12 @@ bool JSModuleNamespaceObject::defineOwnProperty(JSObject* cell, JSGlobalObject* bool isCurrentDefined = thisObject->getOwnPropertyDescriptor(globalObject, propertyName, current); RETURN_IF_EXCEPTION(scope, false); +#if USE(BUN_JSC_ADDITIONS) + if (thisObject->m_isOverridingValue) { + return true; + } +#endif + // 3. If current is undefined, return false. if (!isCurrentDefined) { if (shouldThrow) @@ -319,4 +333,55 @@ bool JSModuleNamespaceObject::defineOwnProperty(JSObject* cell, JSGlobalObject* return true; } +#if USE(BUN_JSC_ADDITIONS) + +void JSModuleNamespaceObject::overrideExports(JSGlobalObject* globalObject, const WTF::Function& iter) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + for (auto& pair : m_exports) { + JSValue value = jsUndefined(); + const Identifier& name = pair.value.localName; + + if (iter(globalObject, name, value)) { + auto* moduleNamespaceObject = pair.value.moduleRecord->getModuleNamespace(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + auto* moduleEnvironment = pair.value.moduleRecord->moduleEnvironment(); + bool putResult = false; + moduleNamespaceObject->m_isOverridingValue = true; + symbolTablePutTouchWatchpointSet(moduleEnvironment, globalObject, name, value, false, true, putResult); + JSC::PutPropertySlot putter = JSC::PutPropertySlot(moduleNamespaceObject, false); + moduleNamespaceObject->put(moduleNamespaceObject, globalObject, name, value, putter); + moduleNamespaceObject->m_isOverridingValue = false; + } + } +} + +bool JSModuleNamespaceObject::overrideExportValue(JSGlobalObject* globalObject, PropertyName name, JSValue value) +{ + auto iterator = m_exports.find(name.uid()); + if (iterator == m_exports.end()) + return false; + + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + ExportEntry& exportEntry = iterator->value; + auto* moduleNamespaceObject = exportEntry.moduleRecord->getModuleNamespace(globalObject); + RETURN_IF_EXCEPTION(scope, false); + + JSModuleEnvironment* moduleEnvironment = exportEntry.moduleRecord->moduleEnvironment(); + + bool putResult = false; + moduleNamespaceObject->m_isOverridingValue = true; + symbolTablePutTouchWatchpointSet(moduleEnvironment, globalObject, name, value, false, true, putResult); + JSC::PutPropertySlot putter = JSC::PutPropertySlot(moduleNamespaceObject, false); + putResult = moduleNamespaceObject->put(moduleNamespaceObject, globalObject, name, value, putter); + moduleNamespaceObject->m_isOverridingValue = false; + return putResult; +} + +#endif + } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h b/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h index f8aa1feebc6ea..99d62c3a715f5 100644 --- a/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h +++ b/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h @@ -64,6 +64,11 @@ class JSModuleNamespaceObject final : public JSNonFinalObject { DECLARE_EXPORT_INFO; +#if USE(BUN_JSC_ADDITIONS) + bool overrideExportValue(JSGlobalObject*, PropertyName, JSValue); + void overrideExports(JSGlobalObject* globalObject, const WTF::Function& iter); +#endif + inline static Structure* createStructure(VM&, JSGlobalObject*, JSValue); AbstractModuleRecord* moduleRecord() { return m_moduleRecord.get(); } @@ -84,6 +89,9 @@ class JSModuleNamespaceObject final : public JSNonFinalObject { ExportMap m_exports; FixedVector m_names; WriteBarrier m_moduleRecord; +#if USE(BUN_JSC_ADDITIONS) + bool m_isOverridingValue = false; +#endif friend size_t cellSize(JSCell*); };