#include "ZigGlobalObject.h" #include "helpers.h" #include "ZigConsoleClient.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include #include #include #include "ZigSourceProvider.h" using JSGlobalObject = JSC::JSGlobalObject; using Exception = JSC::Exception; using JSValue = JSC::JSValue; using JSString = JSC::JSString; using JSModuleLoader = JSC::JSModuleLoader; using JSModuleRecord = JSC::JSModuleRecord; using Identifier = JSC::Identifier; using SourceOrigin = JSC::SourceOrigin; using JSObject = JSC::JSObject; using JSNonFinalObject = JSC::JSNonFinalObject; namespace JSCastingHelpers = JSC::JSCastingHelpers; extern "C" JSC__JSGlobalObject *Zig__GlobalObject__create(JSClassRef *globalObjectClass, int count, void *console_client) { JSC::Options::useSourceProviderCache() = true; JSC::Options::useUnlinkedCodeBlockJettisoning() = false; JSC::Options::useTopLevelAwait() = true; JSC::Options::exposeInternalModuleLoader() = true; std::set_terminate([]() { Zig__GlobalObject__onCrash(); }); WTF::initializeMainThread(); JSC::initialize(); // JSC::Options::useCodeCache() = false; JSC::VM &vm = JSC::VM::create(JSC::LargeHeap).leakRef(); vm.heap.acquireAccess(); #if ENABLE(WEBASSEMBLY) JSC::Wasm::enableFastMemory(); #endif JSC::JSLockHolder locker(vm); Zig::GlobalObject *globalObject = Zig::GlobalObject::create(vm, Zig::GlobalObject::createStructure(vm, JSC::jsNull())); globalObject->setConsole(globalObject); if (count > 0) { globalObject->installAPIGlobals(globalObjectClass, count); } JSC::gcProtect(globalObject); vm.ref(); return globalObject; } namespace Zig { const JSC::ClassInfo GlobalObject::s_info = {"GlobalObject", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(GlobalObject)}; const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, nullptr, // queueTaskToEventLoop nullptr, // &shouldInterruptScriptBeforeTimeout, &moduleLoaderImportModule, // moduleLoaderImportModule &moduleLoaderResolve, // moduleLoaderResolve &moduleLoaderFetch, // moduleLoaderFetch &moduleLoaderCreateImportMetaProperties, // moduleLoaderCreateImportMetaProperties &moduleLoaderEvaluate, // moduleLoaderEvaluate &promiseRejectionTracker, // promiseRejectionTracker &reportUncaughtExceptionAtEventLoop, ¤tScriptExecutionOwner, &scriptExecutionStatus, nullptr, // defaultLanguage nullptr, // compileStreaming nullptr, // instantiateStreaming }; void GlobalObject::reportUncaughtExceptionAtEventLoop(JSGlobalObject *globalObject, Exception *exception) { Zig__GlobalObject__reportUncaughtException(globalObject, exception); } void GlobalObject::promiseRejectionTracker(JSGlobalObject *obj, JSC::JSPromise *prom, JSC::JSPromiseRejectionOperation reject) { Zig__GlobalObject__promiseRejectionTracker( obj, prom, reject == JSC::JSPromiseRejectionOperation::Reject ? 0 : 1); } static Zig::ConsoleClient *m_console; void GlobalObject::setConsole(void *console) { m_console = new Zig::ConsoleClient(console); this->setConsoleClient(makeWeakPtr(m_console)); } // This is not a publicly exposed API currently. // This is used by the bundler to make Response, Request, FetchEvent, // and any other objects available globally. void GlobalObject::installAPIGlobals(JSClassRef *globals, int count) { WTF::Vector extraStaticGlobals; extraStaticGlobals.reserveCapacity((size_t)count + 1); // This is not nearly a complete implementation. It's just enough to make some npm packages that // were compiled with Webpack to run without crashing in this environment. JSC::JSObject *process = JSC::constructEmptyObject(this, this->objectPrototype(), 4); // The transpiler inlines all defined process.env vars & dead code eliminates as relevant // so this is just to return undefined for any missing ones and not crash if something tries to // modify it or it wasn't statically analyzable JSC::JSObject *processDotEnv = JSC::constructEmptyObject(this, this->objectPrototype(), 0); process->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "env"), processDotEnv); // this should be transpiled out, but just incase process->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "browser"), JSC::JSValue(false)); // this gives some way of identifying at runtime whether the SSR is happening in node or not. // this should probably be renamed to what the name of the bundler is, instead of "notNodeJS" // but it must be something that won't evaluate to truthy in Node.js process->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "notNodeJS"), JSC::JSValue(true)); #if defined(__APPLE__) process->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "platform"), JSC::jsString(this->vm(), WTF::String("darwin"))); #else process->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "platform"), JSC::jsString(this->vm(), WTF::String("linux"))); #endif for (int i = 0; i < count; i++) { auto jsClass = globals[i]; JSC::JSCallbackObject *object = JSC::JSCallbackObject::create(this, this->callbackObjectStructure(), jsClass, nullptr); if (JSObject *prototype = jsClass->prototype(this)) object->setPrototypeDirect(vm(), prototype); extraStaticGlobals.uncheckedAppend( GlobalPropertyInfo{JSC::Identifier::fromString(vm(), jsClass->className()), JSC::JSValue(object), JSC::PropertyAttribute::DontDelete | 0}); } extraStaticGlobals.uncheckedAppend( GlobalPropertyInfo{JSC::Identifier::fromString(vm(), "process"), JSC::JSValue(process), JSC::PropertyAttribute::DontDelete | 0}); this->addStaticGlobals(extraStaticGlobals.data(), extraStaticGlobals.size()); extraStaticGlobals.releaseBuffer(); } JSC::Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject *globalObject, JSModuleLoader *loader, JSValue key, JSValue referrer, JSValue origin) { ErrorableZigString res; res.success = false; Zig__GlobalObject__resolve(&res, globalObject, toZigString(key, globalObject), referrer.isString() ? toZigString(referrer, globalObject) : ZigStringEmpty); if (res.success) { return toIdentifier(res.result.value, globalObject); } else { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); throwException(scope, res.result.err, globalObject); return globalObject->vm().propertyNames->emptyIdentifier; } } JSC::JSInternalPromise *GlobalObject::moduleLoaderImportModule(JSGlobalObject *globalObject, JSModuleLoader *, JSString *moduleNameValue, JSValue parameters, const SourceOrigin &sourceOrigin) { JSC::VM &vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto *promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure()); RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); auto sourceURL = sourceOrigin.url(); ErrorableZigString resolved; resolved.success = false; Zig__GlobalObject__resolve(&resolved, globalObject, toZigString(moduleNameValue, globalObject), sourceURL.isEmpty() ? ZigStringCwd : toZigString(sourceURL.fileSystemPath())); if (!resolved.success) { throwException(scope, resolved.result.err, globalObject); return promise->rejectWithCaughtException(globalObject, scope); } auto result = JSC::importModule(globalObject, toIdentifier(resolved.result.value, globalObject), parameters, JSC::jsUndefined()); RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); return result; } extern "C" void *Zig__GlobalObject__getModuleRegistryMap(JSC__JSGlobalObject *arg0) { if (JSC::JSObject *loader = JSC::jsDynamicCast(arg0->vm(), arg0->moduleLoader())) { JSC::JSMap *map = JSC::jsDynamicCast( arg0->vm(), loader->getDirect(arg0->vm(), JSC::Identifier::fromString(arg0->vm(), "registry"))); JSC::JSMap *cloned = map->clone(arg0, arg0->vm(), arg0->mapStructure()); JSC::gcProtect(cloned); return cloned; } return nullptr; } extern "C" bool Zig__GlobalObject__resetModuleRegistryMap(JSC__JSGlobalObject *globalObject, void *map_ptr) { if (map_ptr == nullptr) return false; JSC::JSMap *map = reinterpret_cast(map_ptr); JSC::VM &vm = globalObject->vm(); if (JSC::JSObject *obj = JSC::jsDynamicCast(globalObject->vm(), globalObject->moduleLoader())) { auto identifier = JSC::Identifier::fromString(globalObject->vm(), "registry"); if (JSC::JSMap *oldMap = JSC::jsDynamicCast( globalObject->vm(), obj->getDirect(globalObject->vm(), identifier))) { oldMap->clear(globalObject); // vm.finalizeSynchronousJSExecution(); obj->putDirect(globalObject->vm(), identifier, map->clone(globalObject, globalObject->vm(), globalObject->mapStructure())); // vm.deleteAllLinkedCode(JSC::DeleteAllCodeEffort::DeleteAllCodeIfNotCollecting); // JSC::Heap::PreventCollectionScope(vm.heap); // vm.heap.completeAllJITPlans(); // vm.forEachScriptExecutableSpace([&](auto &spaceAndSet) { // JSC::HeapIterationScope heapIterationScope(vm.heap); // auto &set = spaceAndSet.set; // set.forEachLiveCell([&](JSC::HeapCell *cell, JSC::HeapCell::Kind) { // if (JSC::ModuleProgramExecutable *executable = // JSC::jsDynamicCast(cell)) { // executable->clearCode(set); // } // }); // }); } // globalObject->vm().heap.deleteAllUnlinkedCodeBlocks( // JSC::DeleteAllCodeEffort::PreventCollectionAndDeleteAllCode); // vm.whenIdle([globalObject, oldMap, map]() { // auto recordIdentifier = JSC::Identifier::fromString(globalObject->vm(), "module"); // JSC::JSModuleRecord *record; // JSC::JSValue key; // JSC::JSValue value; // JSC::JSObject *mod; // JSC::JSObject *nextObject; // JSC::forEachInIterable( // globalObject, oldMap, // [&](JSC::VM &vm, JSC::JSGlobalObject *globalObject, JSC::JSValue nextValue) { // nextObject = JSC::jsDynamicCast(vm, nextValue); // key = nextObject->getIndex(globalObject, static_cast(0)); // if (!map->has(globalObject, key)) { // value = nextObject->getIndex(globalObject, static_cast(1)); // mod = JSC::jsDynamicCast(vm, value); // if (mod) { // record = JSC::jsDynamicCast( // vm, mod->getDirect(vm, recordIdentifier)); // if (record) { // auto code = &record->sourceCode(); // if (code) { // Zig::SourceProvider *provider = // reinterpret_cast(code->provider()); // // code->~SourceCode(); // if (provider) { provider->freeSourceCode(); } // } // } // } // } // }); // oldMap->clear(globalObject); // } // } // map } return true; } JSC::JSInternalPromise *GlobalObject::moduleLoaderFetch(JSGlobalObject *globalObject, JSModuleLoader *loader, JSValue key, JSValue value1, JSValue value2) { JSC::VM &vm = globalObject->vm(); JSC::JSInternalPromise *promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure()); auto scope = DECLARE_THROW_SCOPE(vm); auto rejectWithError = [&](JSC::JSValue error) { promise->reject(globalObject, error); return promise; }; auto moduleKey = key.toWTFString(globalObject); RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); auto moduleKeyZig = toZigString(moduleKey); ErrorableResolvedSource res; res.success = false; res.result.err.code = 0; res.result.err.ptr = nullptr; Zig__GlobalObject__fetch(&res, globalObject, moduleKeyZig, Zig::toZigString(value1, globalObject)); if (!res.success) { throwException(scope, res.result.err, globalObject); RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); } auto provider = Zig::SourceProvider::create(res.result.value); auto jsSourceCode = JSC::JSSourceCode::create(vm, JSC::SourceCode(provider)); if (provider.ptr()->isBytecodeCacheEnabled()) { provider.ptr()->readOrGenerateByteCodeCache(vm, jsSourceCode->sourceCode()); } scope.release(); promise->resolve(globalObject, jsSourceCode); globalObject->vm().drainMicrotasks(); return promise; } JSC::JSObject *GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObject *globalObject, JSModuleLoader *loader, JSValue key, JSModuleRecord *record, JSValue val) { ZigString specifier = Zig::toZigString(key, globalObject); JSC::VM &vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSC::JSObject *metaProperties = JSC::constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure()); RETURN_IF_EXCEPTION(scope, nullptr); metaProperties->putDirect(vm, Identifier::fromString(vm, "filePath"), key); RETURN_IF_EXCEPTION(scope, nullptr); // metaProperties->putDirect(vm, Identifier::fromString(vm, "resolve"), // globalObject->globalThis() // ->get(vm, Identifier::fromString("Bun")) // .getObject() // ->get(vm, Identifier::fromString("resolve"))); ); // RETURN_IF_EXCEPTION(scope, nullptr); return metaProperties; } JSC::JSValue GlobalObject::moduleLoaderEvaluate(JSGlobalObject *globalObject, JSModuleLoader *moduleLoader, JSValue key, JSValue moduleRecordValue, JSValue scriptFetcher, JSValue sentValue, JSValue resumeMode) { // VM& vm = globalObject->vm(); return moduleLoader->evaluateNonVirtual(globalObject, key, moduleRecordValue, scriptFetcher, sentValue, resumeMode); } } // namespace Zig