diff options
author | 2023-06-15 01:18:23 -0700 | |
---|---|---|
committer | 2023-06-15 01:18:23 -0700 | |
commit | dc06caccaa6bd8fd273e16cff2c2e0c10f32c58e (patch) | |
tree | ee62eff2b60d567a337d3442ed703cf6f547bd76 | |
parent | e6d4b3a89ac6631f54276a21d82d41f91fd41c76 (diff) | |
download | bun-dc06caccaa6bd8fd273e16cff2c2e0c10f32c58e.tar.gz bun-dc06caccaa6bd8fd273e16cff2c2e0c10f32c58e.tar.zst bun-dc06caccaa6bd8fd273e16cff2c2e0c10f32c58e.zip |
Tweak CommonJS output (#3320)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r-- | bench/snippets/module-exports-putter.cjs | 65 | ||||
-rw-r--r-- | src/bun.js/bindings/CommonJSModuleRecord.cpp | 497 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 13 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.h | 1 | ||||
-rw-r--r-- | src/js_parser.zig | 14 |
5 files changed, 322 insertions, 268 deletions
diff --git a/bench/snippets/module-exports-putter.cjs b/bench/snippets/module-exports-putter.cjs new file mode 100644 index 000000000..9bef17b90 --- /dev/null +++ b/bench/snippets/module-exports-putter.cjs @@ -0,0 +1,65 @@ +// This is a stress test of some internals in How Bun does the module.exports assignment. +// If it crashes or throws then this fails +import("./runner.mjs").then(({ bench, run }) => { + bench("Object.defineProperty(module, 'exports', { get() { return 42; } })", () => { + Object.defineProperty(module, "exports", { + get() { + return 42; + }, + set() { + throw new Error("bad"); + }, + configurable: true, + }); + if (module.exports !== 42) throw new Error("bad"); + if (!Object.getOwnPropertyDescriptor(module, "exports").get) throw new Error("bad"); + }); + + bench("Object.defineProperty(module.exports = {})", () => { + Object.defineProperty(module, "exports", { + value: { abc: 123 }, + }); + + if (!module.exports.abc) throw new Error("bad"); + if (Object.getOwnPropertyDescriptor(module, "exports").value !== module.exports) throw new Error("bad"); + }); + + bench("module.exports = {}", () => { + module.exports = { abc: 123 }; + + if (!module.exports.abc) throw new Error("bad"); + if (Object.getOwnPropertyDescriptor(module, "exports").value !== module.exports) throw new Error("bad"); + }); + + run().then(() => { + module.exports = { + a: 1, + }; + + console.log( + module?.exports, + require.cache[module.id].exports, + module?.exports === require.cache[module.id], + __dirname, + Object.keys(require(module.id)), + require(module.id), + ); + + module.exports = function lol() { + return 42; + }; + + console.log(module.exports, module.exports()); + + queueMicrotask(() => { + console.log( + module?.exports, + require.cache[module.id].exports, + module?.exports === require.cache[module.id]?.exports, + __dirname, + Object.keys(require(module.id)), + require(module.id), + ); + }); + }); +}); diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index 31c24bb66..1cee1091b 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -68,9 +68,6 @@ namespace Bun { using namespace JSC; -static Structure* internalCreateCommonJSModuleStructure( - Zig::GlobalObject* globalObject); - class JSCommonJSModule final : public JSC::JSNonFinalObject { public: using Base = JSC::JSNonFinalObject; @@ -78,15 +75,13 @@ public: mutable JSC::WriteBarrier<JSC::Unknown> m_exportsObject; mutable JSC::WriteBarrier<JSC::JSString> m_id; - mutable JSC::WriteBarrier<JSC::EvalExecutable> m_executable; - void finishCreation(JSC::VM& vm, JSC::JSValue exportsObject, JSC::JSString* id, JSC::JSString* filename, JSC::JSValue requireFunction, JSC::EvalExecutable* executable) + void finishCreation(JSC::VM& vm, JSC::JSValue exportsObject, JSC::JSString* id, JSC::JSString* filename, JSC::JSString* dirname, JSC::JSValue requireFunction) { Base::finishCreation(vm); ASSERT(inherits(vm, info())); m_exportsObject.set(vm, this, exportsObject); m_id.set(vm, this, id); - m_executable.set(vm, this, executable); this->putDirectOffset( vm, @@ -101,15 +96,83 @@ public: this->putDirectOffset( vm, 2, - id); + filename); + this->putDirectOffset( vm, 3, - filename); + jsBoolean(false)); + this->putDirectOffset( vm, 4, - requireFunction); + dirname); + + this->putDirectOffset( + vm, + 5, + jsUndefined()); + } + + static JSC::Structure* createStructure( + JSC::JSGlobalObject* globalObject) + { + auto& vm = globalObject->vm(); + JSC::Structure* structure = JSC::Structure::create( + vm, + globalObject, + globalObject->objectPrototype(), + JSC::TypeInfo(JSC::ObjectType, JSCommonJSModule::StructureFlags), + JSCommonJSModule::info(), + JSC::NonArray, + 6); + + JSC::PropertyOffset offset; + auto clientData = WebCore::clientData(vm); + + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "exports"_s), + 0, + offset); + + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "id"_s), + 0, + offset); + + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "filename"_s), + 0, + offset); + + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "loaded"_s), + 0, + offset); + + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "path"_s), + 0, + offset); + + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "require"_s), + 0, + offset); + + return structure; } static JSCommonJSModule* create( @@ -118,11 +181,11 @@ public: JSC::JSValue exportsObject, JSC::JSString* id, JSC::JSString* filename, - JSC::JSValue requireFunction, - JSC::EvalExecutable* executable) + JSC::JSString* dirname, + JSC::JSValue requireFunction) { JSCommonJSModule* cell = new (NotNull, JSC::allocateCell<JSCommonJSModule>(vm)) JSCommonJSModule(vm, structure); - cell->finishCreation(vm, exportsObject, id, filename, requireFunction, executable); + cell->finishCreation(vm, exportsObject, id, filename, dirname, requireFunction); return cell; } @@ -145,34 +208,34 @@ public: JSC::JSValue value, JSC::PutPropertySlot& slot) { - JSCommonJSModule* thisObject = jsCast<JSCommonJSModule*>(cell); - ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + auto& vm = globalObject->vm(); + auto* clientData = WebCore::clientData(vm); auto throwScope = DECLARE_THROW_SCOPE(vm); - auto* clientData = WebCore::clientData(vm); - bool result = Base::put(thisObject, globalObject, propertyName, value, slot); - if (result) { - // Whenever you call module.exports = ... in a module, we need to: - // - // - Update the internal exports object - // - Update the require map - // - if (propertyName == clientData->builtinNames().exportsPublicName()) { - thisObject->m_exportsObject.set(vm, thisObject, value); - Zig::GlobalObject* zigGlobalObject = jsCast<Zig::GlobalObject*>(globalObject); - zigGlobalObject->requireMap()->set(globalObject, thisObject->id(), value); - RETURN_IF_EXCEPTION(throwScope, false); + if (propertyName == clientData->builtinNames().exportsPublicName()) { + JSCommonJSModule* thisObject = jsCast<JSCommonJSModule*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + // It will crash if we attempt to assign Object.defineProperty() result to a JSMap*. + if (UNLIKELY(slot.thisValue() != thisObject)) + RELEASE_AND_RETURN(throwScope, JSObject::definePropertyOnReceiver(globalObject, propertyName, value, slot)); + + JSValue prevValue = thisObject->m_exportsObject.get(); + + // TODO: refactor this to not go through ESM path and we don't need to do this check. + // IF we do this on every call, it causes GC to happen in a place that it may not be able to. + // This breaks loading Bluebird in some cases, for example. + // We need to update the require map "live" because otherwise the code in Discord.js will break + // The bug is something to do with exception handling which causes GC to happen in the error path and then boom. + if (prevValue != value && (!prevValue.isCell() || !value.isCell() || prevValue.asCell()->type() != value.asCell()->type())) { + jsCast<Zig::GlobalObject*>(globalObject)->requireMap()->set(globalObject, thisObject->id(), value); } + + thisObject->m_exportsObject.set(vm, thisObject, value); } - RELEASE_AND_RETURN(throwScope, result); - } - - static JSC::Structure* createStructure( - JSC::JSGlobalObject* globalObject) - { - return internalCreateCommonJSModuleStructure(reinterpret_cast<Zig::GlobalObject*>(globalObject)); + RELEASE_AND_RETURN(throwScope, Base::put(cell, globalObject, propertyName, value, slot)); } DECLARE_INFO; @@ -200,53 +263,6 @@ Structure* createCommonJSModuleStructure( return JSCommonJSModule::createStructure(globalObject); } -static Structure* internalCreateCommonJSModuleStructure( - Zig::GlobalObject* globalObject) -{ - auto& vm = globalObject->vm(); - JSC::Structure* structure = JSC::Structure::create( - vm, - globalObject, - globalObject->objectPrototype(), - JSC::TypeInfo(JSC::ObjectType, JSCommonJSModule::StructureFlags), - JSCommonJSModule::info(), - JSC::NonArray, - 4); - - JSC::PropertyOffset offset; - auto clientData = WebCore::clientData(vm); - - structure = structure->addPropertyTransition( - vm, - structure, - JSC::Identifier::fromString(vm, "exports"_s), - 0, - offset); - - structure = structure->addPropertyTransition( - vm, - structure, - JSC::Identifier::fromString(vm, "id"_s), - 0, - offset); - - structure = structure->addPropertyTransition( - vm, - structure, - JSC::Identifier::fromString(vm, "filename"_s), - 0, - offset); - - structure = structure->addPropertyTransition( - vm, - structure, - JSC::Identifier::fromString(vm, "require"_s), - JSC::PropertyAttribute::Builtin | JSC::PropertyAttribute::Function | 0, - offset); - - return structure; -} - template<typename Visitor> void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor) { @@ -255,34 +271,11 @@ void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor) Base::visitChildren(thisObject, visitor); visitor.append(thisObject->m_exportsObject); visitor.append(thisObject->m_id); - visitor.append(thisObject->m_executable); } DEFINE_VISIT_CHILDREN(JSCommonJSModule); const JSC::ClassInfo JSCommonJSModule::s_info = { "Module"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCommonJSModule) }; -JSCommonJSModule* createCommonJSModuleObject( - Zig::GlobalObject* globalObject, - const ResolvedSource& source, - const WTF::String& sourceURL, - JSC::JSValue exportsObjectValue, - JSC::JSValue requireFunctionValue, - JSC::EvalExecutable* executable, - JSC::JSString* filename) -{ - auto& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - auto* jsSourceURL = JSC::jsString(vm, sourceURL); - - JSCommonJSModule* moduleObject = JSCommonJSModule::create( - vm, - globalObject->CommonJSModuleObjectStructure(), - exportsObjectValue, - jsSourceURL, filename, requireFunctionValue, executable); - - return moduleObject; -} - static bool canPerformFastEnumeration(Structure* s) { if (s->typeInfo().overridesGetOwnPropertySlot()) @@ -300,172 +293,155 @@ static bool canPerformFastEnumeration(Structure* s) return true; } -JSC::SourceCode createCommonJSModule( +JSValue evaluateCommonJSModule( Zig::GlobalObject* globalObject, + Ref<Zig::SourceProvider> sourceProvider, + const WTF::String& sourceURL, ResolvedSource source) { - auto sourceURL = Zig::toStringCopy(source.source_url); - auto sourceProvider = Zig::SourceProvider::create(globalObject, source, JSC::SourceProviderSourceType::Program); + auto& vm = globalObject->vm(); - return JSC::SourceCode( - JSC::SyntheticSourceProvider::create( - [source, sourceProvider = WTFMove(sourceProvider), sourceURL](JSC::JSGlobalObject* lexicalGlobalObject, - JSC::Identifier moduleKey, - Vector<JSC::Identifier, 4>& exportNames, - JSC::MarkedArgumentBuffer& exportValues) -> void { - auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject); - auto& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* requireMapKey = jsString(vm, sourceURL); + + JSC::JSObject* exportsObject = source.commonJSExportsLen < 64 + ? JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), source.commonJSExportsLen) + : JSC::constructEmptyObject(globalObject, globalObject->objectPrototype()); + auto index = sourceURL.reverseFind('/', sourceURL.length()); + JSString* dirname = jsEmptyString(vm); + JSString* filename = requireMapKey; + if (index != WTF::notFound) { + dirname = JSC::jsSubstring(globalObject, requireMapKey, 0, index); + } - auto throwScope = DECLARE_THROW_SCOPE(vm); - auto* requireMapKey = jsString(vm, sourceURL); - - JSC::JSObject* exportsObject = source.commonJSExportsLen < 64 - ? JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), source.commonJSExportsLen) - : JSC::constructEmptyObject(globalObject, globalObject->objectPrototype()); - auto index = sourceURL.reverseFind('/', sourceURL.length()); - JSString* dirname = jsEmptyString(vm); - JSString* filename = requireMapKey; - if (index != WTF::notFound) { - dirname = JSC::jsSubstring(globalObject, requireMapKey, 0, index); - } + globalObject->requireMap()->set(globalObject, requireMapKey, exportsObject); + auto* requireFunction = Zig::ImportMetaObject::createRequireFunction(vm, globalObject, sourceURL); - globalObject->requireMap()->set(globalObject, requireMapKey, exportsObject); - JSC::SourceCode inputSource( - WTFMove(sourceProvider)); + JSC::SourceCode inputSource( + WTFMove(sourceProvider)); - JSC::Structure* scopeExtensionObjectStructure = globalObject->commonJSFunctionArgumentsStructure(); - JSC::JSObject* scopeExtensionObject = JSC::constructEmptyObject( - vm, - scopeExtensionObjectStructure); + auto* moduleObject = JSCommonJSModule::create( + vm, + globalObject->CommonJSModuleObjectStructure(), + exportsObject, + requireMapKey, filename, dirname, requireFunction); - auto* requireFunction = Zig::ImportMetaObject::createRequireFunction(vm, globalObject, sourceURL); - auto* executable = JSC::DirectEvalExecutable::create( - globalObject, inputSource, DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None, - false, false, EvalContextType::None, nullptr, nullptr, ECMAMode::sloppy()); + if (UNLIKELY(throwScope.exception())) { + globalObject->requireMap()->remove(globalObject, requireMapKey); + RELEASE_AND_RETURN(throwScope, JSValue()); + } - if (UNLIKELY(!executable && !throwScope.exception())) { - // I'm not sure if this case happens, but it's better to be safe than sorry. - throwSyntaxError(globalObject, throwScope, "Failed to compile CommonJS module."_s); - } + JSC::Structure* thisObjectStructure = globalObject->commonJSFunctionArgumentsStructure(); + JSC::JSObject* thisObject = JSC::constructEmptyObject( + vm, + thisObjectStructure); + thisObject->putDirectOffset( + vm, + 0, + moduleObject); - if (UNLIKELY(throwScope.exception())) { - globalObject->requireMap()->remove(globalObject, requireMapKey); - throwScope.release(); - return; - } + thisObject->putDirectOffset( + vm, + 1, + exportsObject); - auto* moduleObject = createCommonJSModuleObject(globalObject, - source, - sourceURL, - exportsObject, - requireFunction, executable, filename); - - scopeExtensionObject->putDirectOffset( - vm, - 0, - moduleObject); - - scopeExtensionObject->putDirectOffset( - vm, - 1, - exportsObject); - - scopeExtensionObject->putDirectOffset( - vm, - 2, - dirname); - - scopeExtensionObject->putDirectOffset( - vm, - 3, - filename); - - scopeExtensionObject->putDirectOffset( - vm, - 4, - requireFunction); - - if (UNLIKELY(throwScope.exception())) { - globalObject->requireMap()->remove(globalObject, requireMapKey); - throwScope.release(); - return; - } + thisObject->putDirectOffset( + vm, + 2, + dirname); - auto catchScope = DECLARE_CATCH_SCOPE(vm); - - // Where the magic happens. - // - // A `with` scope is created containing { module, exports, require }. - // We eval() the CommonJS module code - // with that scope. - // - // Doing it that way saves us a roundtrip through C++ <> JS. - // - // Sidenote: another implementation could use - // FunctionExecutable. It looks like there are lots of arguments - // to pass to that and it isn't used directly much, so that - // seems harder to do correctly. - { - // We must use a global scope extension or else the JSWithScope will be collected unexpectedly. - // https://github.com/oven-sh/bun/issues/3161 - globalObject->clearGlobalScopeExtension(); - - JSWithScope* withScope = JSWithScope::create(vm, globalObject, globalObject->globalScope(), scopeExtensionObject); - globalObject->setGlobalScopeExtension(withScope); - vm.interpreter.executeEval(executable, globalObject, globalObject->globalScope()); - globalObject->clearGlobalScopeExtension(); - - if (UNLIKELY(catchScope.exception())) { - auto returnedException = catchScope.exception(); - catchScope.clearException(); - JSC::throwException(globalObject, throwScope, returnedException); - } - } + thisObject->putDirectOffset( + vm, + 3, + filename); - if (throwScope.exception()) { - globalObject->requireMap()->remove(globalObject, requireMapKey); - throwScope.release(); - return; - } + thisObject->putDirectOffset( + vm, + 4, + requireFunction); - JSValue result = moduleObject->exportsObject(); - - // The developer can do something like: - // - // Object.defineProperty(module, 'exports', {get: getter}) - // - // In which case, the exports object is now a GetterSetter object. - // - // We can't return a GetterSetter object to ESM code, so we need to call it. - if (!result.isEmpty() && (result.isGetterSetter() || result.isCustomGetterSetter())) { - auto* clientData = WebCore::clientData(vm); - - // TODO: is there a faster way to call these getters? We shouldn't need to do a full property lookup. - // - // we use getIfPropertyExists just incase a pathological devleoper did: - // - // - Object.defineProperty(module, 'exports', {get: getter}) - // - delete module.exports - // - if (result.isGetterSetter()) { - JSC::GetterSetter* getter = jsCast<JSC::GetterSetter*>(result); - result = getter->callGetter(globalObject, moduleObject); - } else { - result = moduleObject->getIfPropertyExists(globalObject, clientData->builtinNames().exportsPublicName()); - } + { + WTF::NakedPtr<Exception> exception; + globalObject->m_BunCommonJSModuleValue.set(vm, globalObject, thisObject); + JSC::evaluate(globalObject, inputSource, globalObject->globalThis(), exception); + + if (exception.get()) { + throwScope.throwException(globalObject, exception->value()); + exception.clear(); + RELEASE_AND_RETURN(throwScope, JSValue()); + } + } - if (UNLIKELY(throwScope.exception())) { - // Unlike getters on properties of the exports object - // When the exports object itself is a getter and it throws - // There's not a lot we can do - // so we surface that error - globalObject->requireMap()->remove(globalObject, requireMapKey); - throwScope.release(); - return; - } + if (UNLIKELY(throwScope.exception())) { + globalObject->requireMap()->remove(globalObject, requireMapKey); + RELEASE_AND_RETURN(throwScope, JSValue()); + } + + JSValue result = moduleObject->exportsObject(); + + // The developer can do something like: + // + // Object.defineProperty(module, 'exports', {get: getter}) + // + // In which case, the exports object is now a GetterSetter object. + // + // We can't return a GetterSetter object to ESM code, so we need to call it. + if (!result.isEmpty() && (result.isGetterSetter() || result.isCustomGetterSetter())) { + auto* clientData = WebCore::clientData(vm); + + // TODO: is there a faster way to call these getters? We shouldn't need to do a full property lookup. + // + // we use getIfPropertyExists just incase a pathological devleoper did: + // + // - Object.defineProperty(module, 'exports', {get: getter}) + // - delete module.exports + // + if (result.isGetterSetter()) { + JSC::GetterSetter* getter = jsCast<JSC::GetterSetter*>(result); + result = getter->callGetter(globalObject, moduleObject); + } else { + result = moduleObject->getIfPropertyExists(globalObject, clientData->builtinNames().exportsPublicName()); + } + + if (UNLIKELY(throwScope.exception())) { + // Unlike getters on properties of the exports object + // When the exports object itself is a getter and it throws + // There's not a lot we can do + // so we surface that error + globalObject->requireMap()->remove(globalObject, requireMapKey); + RELEASE_AND_RETURN(throwScope, JSValue()); + } + } + + globalObject->requireMap()->set(globalObject, requireMapKey, result); + + return result; +} + +JSC::SourceCode createCommonJSModule( + Zig::GlobalObject* globalObject, + ResolvedSource source) +{ + auto sourceURL = Zig::toStringCopy(source.source_url); + auto sourceProvider = Zig::SourceProvider::create(globalObject, source, JSC::SourceProviderSourceType::Program); + + return JSC::SourceCode( + JSC::SyntheticSourceProvider::create( + [source, sourceProvider = WTFMove(sourceProvider), sourceURL](JSC::JSGlobalObject* globalObject, + JSC::Identifier moduleKey, + Vector<JSC::Identifier, 4>& exportNames, + JSC::MarkedArgumentBuffer& exportValues) -> void { + JSValue result = evaluateCommonJSModule( + jsCast<Zig::GlobalObject*>(globalObject), + WTFMove(sourceProvider), + sourceURL, + source); + + if (!result) { + return; } - globalObject->requireMap()->set(globalObject, requireMapKey, result); + auto& vm = globalObject->vm(); exportNames.append(vm.propertyNames->defaultKeyword); exportValues.append(result); @@ -474,9 +450,8 @@ JSC::SourceCode createCommonJSModule( exportNames.append(Identifier::fromUid(vm.symbolRegistry().symbolForKey("CommonJS"_s))); exportValues.append(jsNumber(0)); - moduleObject->m_executable.clear(); - if (result.isObject()) { + DeferGCForAWhile deferGC(vm); auto* exports = asObject(result); auto* structure = exports->structure(); @@ -498,22 +473,20 @@ JSC::SourceCode createCommonJSModule( return true; }); } else { + auto catchScope = DECLARE_CATCH_SCOPE(vm); JSC::PropertyNameArray properties(vm, JSC::PropertyNameMode::Strings, JSC::PrivateSymbolMode::Exclude); exports->methodTable()->getOwnPropertyNames(exports, globalObject, properties, DontEnumPropertiesMode::Exclude); - if (throwScope.exception()) { - throwScope.release(); + if (catchScope.exception()) { + catchScope.clearExceptionExceptTermination(); return; } for (auto property : properties) { - if (UNLIKELY(property.isEmpty() || property.isNull())) + if (UNLIKELY(property.isEmpty() || property.isNull() || property.isPrivateName() || property.isSymbol())) continue; // ignore constructor - if (property == vm.propertyNames->constructor) - continue; - - if (property.isSymbol() || property.isPrivateName() || property == vm.propertyNames->defaultKeyword) + if (property == vm.propertyNames->constructor || property == vm.propertyNames->defaultKeyword) continue; JSC::PropertySlot slot(exports, PropertySlot::InternalMethodType::Get); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index a2849939e..299ad7a8c 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -3168,6 +3168,15 @@ extern "C" void Bun__setOnEachMicrotaskTick(JSC::VM* vm, void* ptr, void (*callb }); } +JSC_DEFINE_CUSTOM_GETTER(BunCommonJSModule_getter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName)) +{ + Zig::GlobalObject* bunGlobalObject = jsCast<Zig::GlobalObject*>(globalObject); + JSValue returnValue = bunGlobalObject->m_BunCommonJSModuleValue.get(); + if (!returnValue) { + returnValue = jsUndefined(); + } + return JSValue::encode(returnValue); +} // This implementation works the same as setTimeout(myFunction, 0) // TODO: make it more efficient // https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate @@ -3526,6 +3535,9 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm) putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "CloseEvent"_s), JSC::CustomGetterSetter::create(vm, JSCloseEvent_getter, nullptr), JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); + putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "$_BunCommonJSModule_$"_s), JSC::CustomGetterSetter::create(vm, BunCommonJSModule_getter, nullptr), + JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); + auto bufferAccessor = JSC::CustomGetterSetter::create(vm, JSBuffer_getter, JSBuffer_setter); auto realBufferAccessor = JSC::CustomGetterSetter::create(vm, JSBuffer_privateGetter, nullptr); @@ -3935,6 +3947,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) } thisObject->visitGeneratedLazyClasses<Visitor>(thisObject, visitor); + visitor.append(thisObject->m_BunCommonJSModuleValue); ScriptExecutionContext* context = thisObject->scriptExecutionContext(); visitor.addOpaqueRoot(context); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index d5f933540..dda1c8330 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -363,6 +363,7 @@ public: mutable WriteBarrier<Unknown> m_JSURLSearchParamsSetterValue; mutable WriteBarrier<Unknown> m_JSWebSocketSetterValue; mutable WriteBarrier<Unknown> m_JSDOMFormDataSetterValue; + mutable WriteBarrier<Unknown> m_BunCommonJSModuleValue; mutable WriteBarrier<JSFunction> m_thenables[promiseFunctionsSize + 1]; diff --git a/src/js_parser.zig b/src/js_parser.zig index 971abd32e..a64e32114 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -21180,22 +21180,24 @@ fn NewParser_( }, logger.Loc.Empty, ); + const cjsGlobal = p.newSymbol(.unbound, "$_BunCommonJSModule_$") catch unreachable; var call_args = allocator.alloc(Expr, 6) catch unreachable; const this_module = p.newExpr( E.Dot{ .name = "module", - .target = p.newExpr(E.This{}, logger.Loc.Empty), + .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty), .name_loc = logger.Loc.Empty, }, logger.Loc.Empty, ); + // // (function(module, exports, require, __dirname, __filename) {}).call(this.exports, this.module, this.exports, this.require, __dirname, __filename) call_args[0..6].* = .{ p.newExpr( E.Dot{ .name = "exports", - .target = p.newExpr(E.This{}, logger.Loc.Empty), + .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty), .name_loc = logger.Loc.Empty, }, logger.Loc.Empty, @@ -21204,7 +21206,7 @@ fn NewParser_( p.newExpr( E.Dot{ .name = "exports", - .target = p.newExpr(E.This{}, logger.Loc.Empty), + .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty), .name_loc = logger.Loc.Empty, }, logger.Loc.Empty, @@ -21223,7 +21225,7 @@ fn NewParser_( .right = p.newExpr( E.Dot{ .name = "require", - .target = p.newExpr(E.This{}, logger.Loc.Empty), + .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty), .name_loc = logger.Loc.Empty, }, logger.Loc.Empty, @@ -21234,7 +21236,7 @@ fn NewParser_( p.newExpr( E.Dot{ .name = "__dirname", - .target = p.newExpr(E.This{}, logger.Loc.Empty), + .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty), .name_loc = logger.Loc.Empty, }, logger.Loc.Empty, @@ -21242,7 +21244,7 @@ fn NewParser_( p.newExpr( E.Dot{ .name = "__filename", - .target = p.newExpr(E.This{}, logger.Loc.Empty), + .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty), .name_loc = logger.Loc.Empty, }, logger.Loc.Empty, |