#include "root.h" #include "headers.h" #include "ImportMetaObject.h" #include "ZigGlobalObject.h" #include "ActiveDOMObject.h" #include "ExtendedDOMClientIsoSubspaces.h" #include "ExtendedDOMIsoSubspaces.h" #include "IDLTypes.h" // #include "JSBlob.h" #include "JSDOMAttribute.h" #include "JSDOMBinding.h" #include "JSDOMConstructor.h" #include "JSDOMConvertBase.h" #include "JSDOMConvertInterface.h" #include "JSDOMConvertStrings.h" #include "JSDOMExceptionHandling.h" #include "JSDOMGlobalObject.h" #include "JSDOMGlobalObjectInlines.h" #include "JSDOMOperation.h" #include "JSDOMWrapperCache.h" #include "ScriptExecutionContext.h" #include "WebCoreJSClientData.h" #include "JavaScriptCore/FunctionPrototype.h" #include "JavaScriptCore/HeapAnalyzer.h" #include "JavaScriptCore/JSDestructibleObjectHeapCellType.h" #include "JavaScriptCore/SlotVisitorMacros.h" #include "JavaScriptCore/SubspaceInlines.h" #include "wtf/GetPtr.h" #include "wtf/PointerPreparations.h" #include "wtf/URL.h" #include "JavaScriptCore/BuiltinNames.h" #include "JSBufferEncodingType.h" #include "JavaScriptCore/JSBase.h" #include "JSDOMURL.h" #include "JavaScriptCore/JSNativeStdFunction.h" #include "JavaScriptCore/GetterSetter.h" namespace Zig { using namespace JSC; using namespace WebCore; static EncodedJSValue functionRequireResolve(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame, const WTF::String& fromStr) { JSC::VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); switch (callFrame->argumentCount()) { case 0: { // not "requires" because "require" could be confusing JSC::throwTypeError(globalObject, scope, "require.resolve needs 1 argument (a string)"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } default: { JSC::JSValue moduleName = callFrame->argument(0); auto doIt = [&](const WTF::String& fromStr) -> JSC::EncodedJSValue { BunString from = Bun::toString(fromStr); auto result = Bun__resolveSyncWithSource(globalObject, JSC::JSValue::encode(moduleName), &from, false); if (!JSC::JSValue::decode(result).isString()) { JSC::throwException(globalObject, scope, JSC::JSValue::decode(result)); return JSC::JSValue::encode(JSValue {}); } scope.release(); return result; }; if (moduleName.isUndefinedOrNull()) { JSC::throwTypeError(globalObject, scope, "require.resolve expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } if (callFrame->argumentCount() > 1) { JSC::JSValue fromValue = callFrame->argument(1); // require.resolve also supports a paths array // we only support a single path if (!fromValue.isUndefinedOrNull() && fromValue.isObject()) { if (JSValue pathsValue = fromValue.getObject()->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "paths"_s))) { if (JSC::JSArray* array = JSC::jsDynamicCast(pathsValue)) { if (array->length() > 0) { fromValue = array->getIndex(globalObject, 0); } } } } if (fromValue.isString()) { WTF::String str = fromValue.toWTFString(globalObject); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::JSValue {})); return doIt(str); } } return doIt(fromStr); } } } Zig::ImportMetaObject* Zig::ImportMetaObject::create(JSC::JSGlobalObject* globalObject, JSValue key) { if (WebCore::DOMURL* domURL = WebCoreCast(JSValue::encode(key))) { return create(globalObject, JSC::jsString(globalObject->vm(), domURL->href().fileSystemPath())); } auto* keyString = key.toStringOrNull(globalObject); if (UNLIKELY(!keyString)) { return nullptr; } if (keyString->value(globalObject).startsWith("file://"_s)) { return create(globalObject, JSC::jsString(globalObject->vm(), WTF::URL(keyString->value(globalObject)).fileSystemPath())); } return create(globalObject, keyString); } JSC_DECLARE_HOST_FUNCTION(jsFunctionRequireResolve); class JSRequireResolveFunctionPrototype final : public JSC::InternalFunction { public: using Base = JSC::InternalFunction; static JSRequireResolveFunctionPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject) { auto* structure = createStructure(vm, globalObject, globalObject->functionPrototype()); JSRequireResolveFunctionPrototype* function = new (NotNull, JSC::allocateCell(vm)) JSRequireResolveFunctionPrototype(vm, structure); function->finishCreation(vm); return function; } static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) { return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); } DECLARE_INFO; static JSC::EncodedJSValue pathsFunction(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { return JSValue::encode(JSC::constructEmptyArray(globalObject, nullptr)); } private: JSRequireResolveFunctionPrototype(JSC::VM& vm, JSC::Structure* structure) : JSC::InternalFunction(vm, structure, jsFunctionRequireResolve, jsFunctionRequireResolve) { } void finishCreation(JSC::VM& vm) { this->putDirectNativeFunction(vm, globalObject(), Identifier::fromString(vm, "paths"_s), 0, pathsFunction, ImplementationVisibility::Public, NoIntrinsic, 0); Base::finishCreation(vm, 2, "resolve"_s, PropertyAdditionMode::WithoutStructureTransition); } }; const JSC::ClassInfo JSRequireResolveFunctionPrototype::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSRequireResolveFunctionPrototype) }; class JSRequireResolveFunction final : public JSC::InternalFunction { public: using Base = JSC::InternalFunction; static JSRequireResolveFunction* create(JSC::VM& vm, JSC::Structure* structure, const WTF::String& from) { JSRequireResolveFunction* function = new (NotNull, JSC::allocateCell(vm)) JSRequireResolveFunction(vm, structure, from); function->finishCreation(vm); return function; } static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) { return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); } DECLARE_INFO; WTF::String from; template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { if constexpr (mode == JSC::SubspaceAccess::Concurrently) return nullptr; return WebCore::subspaceForImpl( vm, [](auto& spaces) { return spaces.m_clientSubspaceForRequireResolveFunction.get(); }, [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForRequireResolveFunction = std::forward(space); }, [](auto& spaces) { return spaces.m_subspaceForRequireResolveFunction.get(); }, [](auto& spaces, auto&& space) { spaces.m_subspaceForRequireResolveFunction = std::forward(space); }); } private: JSRequireResolveFunction(JSC::VM& vm, JSC::Structure* structure, const WTF::String& from_) : JSC::InternalFunction(vm, structure, jsFunctionRequireResolve, jsFunctionRequireResolve) , from(from_) { } void finishCreation(JSC::VM& vm) { Base::finishCreation(vm); } }; const JSC::ClassInfo JSRequireResolveFunction::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSRequireResolveFunction) }; JSC_DEFINE_HOST_FUNCTION(jsFunctionRequireResolve, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { JSRequireResolveFunction* thisObject = JSC::jsCast(callFrame->jsCallee()); return functionRequireResolve(globalObject, callFrame, thisObject->from); } JSValue Zig::ImportMetaObject::createResolveFunctionPrototype(JSC::VM& vm, Zig::GlobalObject* globalObject) { return JSRequireResolveFunctionPrototype::create(vm, globalObject); } JSC::Structure* Zig::ImportMetaObject::createResolveFunctionStructure(JSC::VM& vm, Zig::GlobalObject* globalObject) { JSValue prototype = globalObject->requireResolveFunctionPrototype(); return JSRequireResolveFunction::createStructure(vm, globalObject, prototype); } JSC_DEFINE_CUSTOM_GETTER(jsRequireCacheGetter, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { Zig::GlobalObject* thisObject = jsCast(globalObject); return JSValue::encode(thisObject->lazyRequireCacheObject()); } JSC_DEFINE_CUSTOM_SETTER(jsRequireCacheSetter, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) { JSObject* thisObject = jsDynamicCast(JSValue::decode(thisValue)); if (!thisObject) return false; thisObject->putDirect(globalObject->vm(), propertyName, JSValue::decode(value), 0); return true; } JSObject* Zig::ImportMetaObject::createRequireFunction(VM& vm, JSGlobalObject* lexicalGlobalObject, const WTF::String& pathString) { Zig::GlobalObject* globalObject = static_cast(lexicalGlobalObject); JSFunction* requireFunction = JSFunction::create(vm, importMetaObjectRequireCodeGenerator(vm), globalObject); auto* resolveFunction = JSRequireResolveFunction::create(vm, globalObject->requireResolveFunctionStructure(), pathString); auto clientData = WebCore::clientData(vm); requireFunction->putDirect(vm, clientData->builtinNames().pathPublicName(), jsString(vm, pathString), PropertyAttribute::DontEnum | 0); requireFunction->putDirect(vm, clientData->builtinNames().resolvePublicName(), resolveFunction, PropertyAttribute::Function | PropertyAttribute::DontDelete | 0); requireFunction->putDirectCustomAccessor(vm, Identifier::fromString(vm, "cache"_s), JSC::CustomGetterSetter::create(vm, jsRequireCacheGetter, jsRequireCacheSetter), 0); return requireFunction; } extern "C" EncodedJSValue functionImportMeta__resolveSync(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { JSC::VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); switch (callFrame->argumentCount()) { case 0: { // not "requires" because "require" could be confusing JSC::throwTypeError(globalObject, scope, "import.meta.resolveSync needs 1 argument (a string)"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } default: { JSC::JSValue moduleName = callFrame->argument(0); if (moduleName.isUndefinedOrNull()) { JSC::throwTypeError(globalObject, scope, "import.meta.resolveSync expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } JSC__JSValue from; bool isESM = true; if (callFrame->argumentCount() > 1) { JSC::JSValue fromValue = callFrame->argument(1); // require.resolve also supports a paths array // we only support a single path if (!fromValue.isUndefinedOrNull() && fromValue.isObject()) { if (JSC::JSArray* array = JSC::jsDynamicCast(fromValue.getObject()->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "paths"_s)))) { if (array->length() > 0) { fromValue = array->getIndex(globalObject, 0); } } if (callFrame->argumentCount() > 2) { JSC::JSValue isESMValue = callFrame->argument(2); if (isESMValue.isBoolean()) { isESM = isESMValue.toBoolean(globalObject); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::JSValue {})); } } } else if (fromValue.isBoolean()) { isESM = fromValue.toBoolean(globalObject); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::JSValue {})); } from = JSC::JSValue::encode(fromValue); } else { JSC::JSObject* thisObject = JSC::jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); JSC::throwTypeError(globalObject, scope, "import.meta.resolveSync must be bound to an import.meta object"_s); return JSC::JSValue::encode(JSC::JSValue {}); } auto clientData = WebCore::clientData(vm); from = JSC::JSValue::encode(thisObject->get(globalObject, clientData->builtinNames().pathPublicName())); } auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), from, isESM); if (!JSC::JSValue::decode(result).isString()) { JSC::throwException(globalObject, scope, JSC::JSValue::decode(result)); return JSC::JSValue::encode(JSC::JSValue {}); } scope.release(); return result; } } } JSC_DECLARE_HOST_FUNCTION(functionImportMeta__resolve); JSC_DEFINE_HOST_FUNCTION(functionImportMeta__resolve, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { JSC::VM& vm = globalObject->vm(); switch (callFrame->argumentCount()) { case 0: { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); // not "requires" because "require" could be confusing JSC::throwTypeError(globalObject, scope, "import.meta.resolve needs 1 argument (a string)"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } default: { JSC::JSValue moduleName = callFrame->argument(0); if (moduleName.isUndefinedOrNull()) { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); JSC::throwTypeError(globalObject, scope, "import.meta.resolve expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } JSC__JSValue from; if (callFrame->argumentCount() > 1) { from = JSC::JSValue::encode(callFrame->argument(1)); } else { JSC::JSObject* thisObject = JSC::jsDynamicCast(callFrame->thisValue()); if (UNLIKELY(!thisObject)) { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); JSC::throwTypeError(globalObject, scope, "import.meta.resolve must be bound to an import.meta object"_s); return JSC::JSValue::encode(JSC::JSValue {}); } auto clientData = WebCore::clientData(vm); from = JSC::JSValue::encode(thisObject->get(globalObject, clientData->builtinNames().pathPublicName())); } return Bun__resolve(globalObject, JSC::JSValue::encode(moduleName), from, true); } } } class ImportMetaObjectPrototype final : public JSC::JSNonFinalObject { public: using Base = JSC::JSNonFinalObject; static ImportMetaObjectPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) { ImportMetaObjectPrototype* ptr = new (NotNull, JSC::allocateCell(vm)) ImportMetaObjectPrototype(vm, globalObject, structure); ptr->finishCreation(vm, globalObject); return ptr; } DECLARE_INFO; template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { return &vm.plainObjectSpace(); } static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) { return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); } private: ImportMetaObjectPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) : Base(vm, structure) { } void finishCreation(JSC::VM&, JSC::JSGlobalObject*); }; STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(ImportMetaObjectPrototype, ImportMetaObjectPrototype::Base); JSObject* ImportMetaObject::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) { return ImportMetaObjectPrototype::create(vm, &globalObject, ImportMetaObjectPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype())); } void ImportMetaObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject_) { Base::finishCreation(vm); auto* globalObject = reinterpret_cast(globalObject_); auto clientData = WebCore::clientData(vm); auto& builtinNames = clientData->builtinNames(); this->putDirect(vm, builtinNames.filePublicName(), jsEmptyString(vm), 0); this->putDirect(vm, builtinNames.dirPublicName(), jsEmptyString(vm), 0); this->putDirect(vm, builtinNames.pathPublicName(), jsEmptyString(vm), 0); this->putDirect(vm, builtinNames.urlPublicName(), jsEmptyString(vm), 0); this->putDirect( vm, builtinNames.mainPublicName(), GetterSetter::create(vm, globalObject, JSFunction::create(vm, importMetaObjectMainCodeGenerator(vm), globalObject), nullptr), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin | 0); this->putDirect(vm, Identifier::fromString(vm, "primordials"_s), jsUndefined(), JSC::PropertyAttribute::DontEnum | 0); String requireString = "[[require]]"_s; this->putDirect(vm, builtinNames.requirePublicName(), Zig::ImportMetaObject::createRequireFunction(vm, globalObject, requireString), PropertyAttribute::Builtin | PropertyAttribute::Function | 0); this->putDirectNativeFunction(vm, globalObject, builtinNames.resolvePublicName(), 1, functionImportMeta__resolve, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::Function | 0); this->putDirectNativeFunction( vm, globalObject, builtinNames.resolveSyncPublicName(), 1, functionImportMeta__resolveSync, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::Function | 0); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } void ImportMetaObject::finishCreation(VM& vm) { Base::finishCreation(vm); ASSERT(inherits(info())); } void ImportMetaObject::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) { auto* thisObject = jsCast(cell); // if (void* wrapped = thisObject->wrapped()) { // if (thisObject->scriptExecutionContext()) // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); // } Base::analyzeHeap(cell, analyzer); } const JSC::ClassInfo ImportMetaObjectPrototype::s_info = { "ImportMeta"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ImportMetaObjectPrototype) }; const JSC::ClassInfo ImportMetaObject::s_info = { "ImportMeta"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ImportMetaObject) }; }