From b36b7fee16a5239cfb50cfde94ed871d60032b3c Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Tue, 9 Aug 2022 01:33:12 -0700 Subject: [node compat] Implement `require.resolve` --- src/bun.js/bindings/ImportMetaObject.cpp | 108 ++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 3 deletions(-) (limited to 'src/bun.js/bindings/ImportMetaObject.cpp') diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index dc4290c91..8c0f573f3 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -35,6 +35,8 @@ #include "JSBufferConstructorBuiltins.h" #include "JavaScriptCore/JSBase.h" +#include "JavaScriptCore/JSNativeStdFunction.h" + namespace Zig { using namespace JSC; using namespace WebCore; @@ -42,6 +44,104 @@ using namespace WebCore; extern "C" JSC__JSValue Bun__resolve(JSC::JSGlobalObject* global, JSC__JSValue specifier, JSC__JSValue from); extern "C" JSC__JSValue Bun__resolveSync(JSC::JSGlobalObject* global, JSC__JSValue specifier, JSC__JSValue from); +static EncodedJSValue functionRequireResolve(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame, JSC::EncodedJSValue from) +{ + 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, "require.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, "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 (JSC::JSArray* array = JSC::jsDynamicCast(fromValue.getObject()->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "paths"_s)))) { + if (array->length() > 0) { + fromValue = array->getIndex(globalObject, 0); + } + } + } + } + + auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), from); + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + + 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_DEFINE_CUSTOM_SETTER(functionRequireResolveLazySetter, + (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, + JSC::EncodedJSValue value, JSC::PropertyName)) +{ + Zig::GlobalObject* global = static_cast(JSC::jsCast(JSC::JSValue::decode(thisValue))); + JSC::VM& vm = globalObject->vm(); + JSC::JSFunction* require = JSC::jsCast(JSC::JSValue::decode(thisValue)); + auto clientData = WebCore::clientData(vm); + return require->putDirect(vm, PropertyName(clientData->builtinNames().resolvePrivateName()), JSValue::decode(value), 0); +} + +JSC_DEFINE_CUSTOM_GETTER(functionRequireResolveLazyGetter, + (JSC::JSGlobalObject * _globalObject, JSC::EncodedJSValue thisValue, + JSC::PropertyName)) +{ + Zig::GlobalObject* globalObject = reinterpret_cast(_globalObject); + + JSC::VM& vm = globalObject->vm(); + auto clientData = WebCore::clientData(vm); + auto& builtinNames = clientData->builtinNames(); + + JSC::JSFunction* require = JSC::jsCast(JSC::JSValue::decode(thisValue)); + + if (JSC::JSValue resolveFunctionValue = require->getIfPropertyExists(globalObject, PropertyName(builtinNames.resolvePrivateName()))) { + return JSValue::encode(resolveFunctionValue); + } + + JSValue pathStringValue = require->get(globalObject, PropertyName(builtinNames.pathPrivateName())); + JSC::Strong pathString = JSC::Strong(vm, pathStringValue.toStringOrNull(globalObject)); + + JSC::JSFunction* resolverFunction + = JSC::JSNativeStdFunction::create( + globalObject->vm(), globalObject, 1, "resolve"_s, [pathString_ = WTFMove(pathString)](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> const JSC::EncodedJSValue { + return functionRequireResolve(globalObject, callFrame, JSValue::encode(pathString_.get())); + }); + require->putDirect(vm, builtinNames.resolvePrivateName(), resolverFunction, 0); + return JSValue::encode(JSValue(resolverFunction)); +} + +JSObject* Zig::ImportMetaObject::createRequireFunction(VM& vm, JSGlobalObject* globalObject, WTF::String& pathString) +{ + JSFunction* requireFunction = JSFunction::create(vm, importMetaObjectRequireCodeGenerator(vm), globalObject); + auto clientData = WebCore::clientData(vm); + requireFunction->putDirectCustomAccessor(vm, clientData->builtinNames().resolvePublicName(), JSC::CustomGetterSetter::create(vm, functionRequireResolveLazyGetter, functionRequireResolveLazySetter), 0); + requireFunction->putDirect(vm, clientData->builtinNames().pathPrivateName(), jsOwnedString(vm, pathString), JSC::PropertyAttribute::DontEnum | 0); + return requireFunction; +} + static JSC_DECLARE_HOST_FUNCTION(functionImportMeta__resolveSync); static JSC_DEFINE_HOST_FUNCTION(functionImportMeta__resolveSync, @@ -204,8 +304,10 @@ void ImportMetaObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObj this->putDirect(vm, clientData->builtinNames().pathPublicName(), jsEmptyString(vm), 0); this->putDirect(vm, clientData->builtinNames().urlPublicName(), jsEmptyString(vm), 0); this->putDirect(vm, clientData->builtinNames().mainPublicName(), jsBoolean(false), 0); - this->putDirectBuiltinFunction(vm, globalObject, - clientData->builtinNames().requirePublicName(), importMetaObjectRequireCodeGenerator(vm), 0); + + String requireString = "[[require]]"_s; + this->putDirect(vm, clientData->builtinNames().requirePublicName(), Zig::ImportMetaObject::createRequireFunction(vm, globalObject, requireString), PropertyAttribute::Builtin | PropertyAttribute::Function | 0); + this->putDirectBuiltinFunction(vm, globalObject, clientData->builtinNames().loadModulePublicName(), importMetaObjectLoadModuleCodeGenerator(vm), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0); this->putDirectBuiltinFunction(vm, globalObject, @@ -221,6 +323,7 @@ void ImportMetaObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObj functionImportMeta__resolveSync, NoIntrinsic, JSC::PropertyAttribute::Function | 0); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } @@ -245,5 +348,4 @@ const JSC::ClassInfo ImportMetaObjectPrototype::s_info = { "ImportMeta"_s, &Base const JSC::ClassInfo ImportMetaObject::s_info = { "ImportMeta"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ImportMetaObject) }; - } -- cgit v1.2.3