#include "InternalModuleRegistry.h" #include "ZigGlobalObject.h" #include "JavaScriptCore/BuiltinUtils.h" #include "JavaScriptCore/JSFunction.h" #include "JavaScriptCore/LazyProperty.h" #include "JavaScriptCore/LazyPropertyInlines.h" #include "JavaScriptCore/VMTrapsInlines.h" #include "JavaScriptCore/JSModuleLoader.h" #include "InternalModuleRegistryConstants.h" namespace Bun { extern "C" bool BunTest__shouldGenerateCodeCoverage(BunString sourceURL); extern "C" void ByteRangeMapping__generate(BunString sourceURL, BunString code, int sourceID); static void maybeAddCodeCoverage(JSC::VM& vm, const JSC::SourceCode& code) { #ifdef BUN_DEBUG bool isCodeCoverageEnabled = !!vm.controlFlowProfiler(); bool shouldGenerateCodeCoverage = isCodeCoverageEnabled && BunTest__shouldGenerateCodeCoverage(Bun::toString(code.provider()->sourceURL())); if (shouldGenerateCodeCoverage) { ByteRangeMapping__generate(Bun::toString(code.provider()->sourceURL()), Bun::toString(code.provider()->source().toStringWithoutCopying()), code.provider()->asID()); } #endif } // The `INTERNAL_MODULE_REGISTRY_GENERATE` macro handles inlining code to compile and run a // JS builtin that acts as a module. In debug mode, we use a different implementation that reads // from the developer's filesystem. This allows reloading code without recompiling bindings. #define INTERNAL_MODULE_REGISTRY_GENERATE_(globalObject, vm, SOURCE, moduleName, urlString) \ auto throwScope = DECLARE_THROW_SCOPE(vm); \ auto&& origin = SourceOrigin(WTF::URL(urlString)); \ SourceCode source = JSC::makeSource(SOURCE, origin, \ JSC::SourceTaintedOrigin::Untainted, \ moduleName); \ maybeAddCodeCoverage(vm, source); \ JSFunction* func \ = JSFunction::create( \ vm, \ createBuiltinExecutable( \ vm, source, \ Identifier(), \ ImplementationVisibility::Public, \ ConstructorKind::None, \ ConstructAbility::CannotConstruct) \ ->link(vm, nullptr, source), \ static_cast(globalObject)); \ \ RETURN_IF_EXCEPTION(throwScope, {}); \ \ JSC::MarkedArgumentBuffer argList; \ JSValue result = JSC::profiledCall( \ globalObject, \ ProfilingReason::Other, \ func, \ JSC::getCallData(func), \ globalObject, JSC::MarkedArgumentBuffer()); \ \ RETURN_IF_EXCEPTION(throwScope, {}); \ ASSERT_INTERNAL_MODULE(result, moduleName); \ return result; #if BUN_DEBUG #include "../../src/js/out/DebugPath.h" #define ASSERT_INTERNAL_MODULE(result, moduleName) \ if (!result || !result.isCell() || !jsDynamicCast(result)) { \ printf("Expected \"%s\" to export a JSObject. Bun is going to crash.", moduleName.utf8().data()); \ } JSValue initializeInternalModuleFromDisk( JSGlobalObject* globalObject, VM& vm, WTF::String moduleName, WTF::String fileBase, WTF::String fallback, WTF::String urlString) { WTF::String file = makeString(BUN_DYNAMIC_JS_LOAD_PATH, "modules_dev/"_s, fileBase); if (auto contents = WTF::FileSystemImpl::readEntireFile(file)) { auto string = WTF::String::fromUTF8(contents.value()); INTERNAL_MODULE_REGISTRY_GENERATE_(globalObject, vm, string, moduleName, urlString); } else { printf("bun-debug failed to load bundled version of \"%s\" at \"%s\" (was it deleted?)\n" "Please run `make js` to rebundle these builtins.\n", moduleName.utf8().data(), file.utf8().data()); // Fallback to embedded source INTERNAL_MODULE_REGISTRY_GENERATE_(globalObject, vm, fallback, moduleName, urlString); } } #define INTERNAL_MODULE_REGISTRY_GENERATE(globalObject, vm, moduleId, filename, SOURCE, urlString) \ return initializeInternalModuleFromDisk(globalObject, vm, moduleId, filename, SOURCE, urlString) #else #define ASSERT_INTERNAL_MODULE(result, moduleName) \ { \ } #define INTERNAL_MODULE_REGISTRY_GENERATE(globalObject, vm, moduleId, filename, SOURCE, urlString) \ INTERNAL_MODULE_REGISTRY_GENERATE_(globalObject, vm, SOURCE, moduleId, urlString) #endif const ClassInfo InternalModuleRegistry::s_info = { "InternalModuleRegistry"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(InternalModuleRegistry) }; InternalModuleRegistry::InternalModuleRegistry(VM& vm, Structure* structure) : Base(vm, structure) { } template void InternalModuleRegistry::visitChildrenImpl(JSCell* cell, Visitor& visitor) { auto* thisObject = jsCast(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); Base::visitChildren(thisObject, visitor); } DEFINE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE, InternalModuleRegistry); InternalModuleRegistry* InternalModuleRegistry::create(VM& vm, Structure* structure) { InternalModuleRegistry* registry = new (NotNull, allocateCell(vm)) InternalModuleRegistry(vm, structure); for (uint8_t i = 0; i < BUN_INTERNAL_MODULE_COUNT; i++) { registry->internalField(static_cast(i)) .set(vm, registry, jsUndefined()); } return registry; } Structure* InternalModuleRegistry::createStructure(VM& vm, JSGlobalObject* globalObject) { return Structure::create(vm, globalObject, jsNull(), TypeInfo(InternalFieldTupleType, StructureFlags), info(), 0, 48); } JSValue InternalModuleRegistry::requireId(JSGlobalObject* globalObject, VM& vm, Field id) { auto value = internalField(id).get(); if (!value || value.isUndefined()) { value = createInternalModuleById(globalObject, vm, id); internalField(id).set(vm, this, value); } return value; } #include "../../../src/js/out/InternalModuleRegistry+createInternalModuleById.h" // This is called like @getInternalField(@internalModuleRegistry, 1) ?? @createInternalModuleById(1) // so we want to write it to the internal field when loaded. JSC_DEFINE_HOST_FUNCTION(InternalModuleRegistry::jsCreateInternalModuleById, (JSGlobalObject * lexicalGlobalObject, CallFrame* callframe)) { auto& vm = lexicalGlobalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); auto id = callframe->argument(0).toUInt32(lexicalGlobalObject); auto registry = static_cast(lexicalGlobalObject)->internalModuleRegistry(); auto mod = registry->createInternalModuleById(lexicalGlobalObject, vm, static_cast(id)); RETURN_IF_EXCEPTION(throwScope, {}); registry->internalField(static_cast(id)).set(vm, registry, mod); return JSValue::encode(mod); } } // namespace Bun #undef INTERNAL_MODULE_REGISTRY_GENERATE_ #undef INTERNAL_MODULE_REGISTRY_GENERATE