Skip to content

Commit

Permalink
Support overriding ESM exports for already loaded modules
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarred-Sumner committed Oct 24, 2023
1 parent 60344b4 commit 00ae02e
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 2 deletions.
69 changes: 67 additions & 2 deletions Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "AbstractModuleRecord.h"
#include "JSCInlines.h"
#include "JSModuleEnvironment.h"
#include "JSModuleRecord.h"

namespace JSC {

Expand All @@ -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;
});

Expand Down Expand Up @@ -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<JSModuleNamespaceObject*>(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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<bool(JSGlobalObject* globalObject, const Identifier& exportName, JSC::JSValue& result)>& 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
8 changes: 8 additions & 0 deletions Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool(JSGlobalObject* globalObject, const Identifier& exportName, JSC::JSValue &result)>& iter);
#endif

inline static Structure* createStructure(VM&, JSGlobalObject*, JSValue);

AbstractModuleRecord* moduleRecord() { return m_moduleRecord.get(); }
Expand All @@ -84,6 +89,9 @@ class JSModuleNamespaceObject final : public JSNonFinalObject {
ExportMap m_exports;
FixedVector<Identifier> m_names;
WriteBarrier<AbstractModuleRecord> m_moduleRecord;
#if USE(BUN_JSC_ADDITIONS)
bool m_isOverridingValue = false;
#endif

friend size_t cellSize(JSCell*);
};
Expand Down

0 comments on commit 00ae02e

Please sign in to comment.