diff options
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/bindings/JSMockFunction.cpp | 109 |
1 files changed, 90 insertions, 19 deletions
diff --git a/src/bun.js/bindings/JSMockFunction.cpp b/src/bun.js/bindings/JSMockFunction.cpp index eda0837c9..fcd80102b 100644 --- a/src/bun.js/bindings/JSMockFunction.cpp +++ b/src/bun.js/bindings/JSMockFunction.cpp @@ -18,6 +18,7 @@ #include <JavaScriptCore/GetterSetter.h> #include <JavaScriptCore/WeakMapImpl.h> #include <JavaScriptCore/WeakMapImplInlines.h> +#include <JavaScriptCore/FunctionPrototype.h> namespace Bun { @@ -207,6 +208,12 @@ public: JSC::Identifier spyIdentifier; unsigned spyAttributes = 0; + void setName(const WTF::String& name) + { + auto& vm = this->vm(); + this->putDirect(vm, vm.propertyNames->name, jsString(vm, name), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly); + } + void initMock() { mock.initLater( @@ -330,8 +337,8 @@ static void pushImplInternal(JSMockFunction* fn, JSGlobalObject* jsGlobalObject, { Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(jsGlobalObject); auto& vm = globalObject->vm(); - JSValue currentTail = fn->tail.get(); JSMockImplementation* impl = JSMockImplementation::create(globalObject, globalObject->mockModule.mockImplementationStructure.getInitializedOnMainThread(globalObject), kind, value, isOnce); + JSValue currentTail = fn->tail.get(); JSValue currentImpl = fn->implementation.get(); if (currentTail) { if (auto* current = jsDynamicCast<JSMockImplementation*>(currentTail)) { @@ -501,6 +508,21 @@ extern "C" EncodedJSValue JSMock__spyOn(JSC::JSGlobalObject* lexicalGlobalObject if (hasValue) attributes = slot.attributes(); + { + auto catcher = DECLARE_CATCH_SCOPE(vm); + WTF::String nameToUse; + if (auto* fn = jsDynamicCast<JSFunction*>(value)) { + nameToUse = fn->name(vm); + } else if (auto* fn = jsDynamicCast<InternalFunction*>(value)) { + nameToUse = fn->name(); + } + if (nameToUse.length()) { + mock->setName(nameToUse); + } + if (catcher.exception()) + catcher.clearException(); + } + attributes |= PropertyAttribute::Function; object->putDirect(vm, propertyKey, mock, attributes); RETURN_IF_EXCEPTION(scope, {}); @@ -538,7 +560,7 @@ JSMockModule JSMockModule::create(JSC::JSGlobalObject* globalObject) JSMockModule mock; mock.mockFunctionStructure.initLater( [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) { - auto* prototype = JSMockFunctionPrototype::create(init.vm, init.owner, JSMockFunctionPrototype::createStructure(init.vm, init.owner, jsNull())); + auto* prototype = JSMockFunctionPrototype::create(init.vm, init.owner, JSMockFunctionPrototype::createStructure(init.vm, init.owner, init.owner->functionPrototype())); init.set(JSMockFunction::createStructure(init.vm, init.owner, prototype)); }); @@ -842,6 +864,15 @@ JSC_DEFINE_CUSTOM_GETTER(jsMockFunctionGetter_protoImpl, (JSC::JSGlobalObject * return JSValue::encode(jsUndefined()); } +#define HANDLE_STATIC_CALL \ + if (!thisObject->implementation.get()) { \ + thisObject = JSMockFunction::create( \ + vm, \ + globalObject, \ + reinterpret_cast<Zig::GlobalObject*>(globalObject)->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject), \ + CallbackKind::Call); \ + } + extern "C" EncodedJSValue JSMockFunction__createObject(Zig::GlobalObject* globalObject) { return JSValue::encode( @@ -951,11 +982,25 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementation, (JSC::JSGlobalObject } if (callframe->argumentCount() > 0) { - JSValue arg = callframe->argument(0); - if (arg.isCallable()) { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, arg); + JSValue value = callframe->argument(0); + if (value.isCallable()) { + { + auto catcher = DECLARE_CATCH_SCOPE(vm); + WTF::String nameToUse; + if (auto* fn = jsDynamicCast<JSFunction*>(value)) { + nameToUse = fn->name(vm); + } else if (auto* fn = jsDynamicCast<InternalFunction*>(value)) { + nameToUse = fn->name(); + } + if (nameToUse.length()) { + thisObject->setName(nameToUse); + } + if (catcher.exception()) + catcher.clearException(); + } + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, value); } else { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, arg); + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, value); } } @@ -969,24 +1014,33 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementationOnce, (JSC::JSGlobalObj JSMockFunction* thisObject = jsDynamicCast<JSMockFunction*>(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); if (UNLIKELY(!thisObject)) { - thisObject = JSMockFunction::create( - vm, - globalObject, - globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject), - CallbackKind::Wrapper); - } - - if (UNLIKELY(!thisObject)) { throwOutOfMemoryError(globalObject, scope); return {}; } + HANDLE_STATIC_CALL; + if (callframe->argumentCount() > 0) { - JSValue arg = callframe->argument(0); - if (arg.isCallable()) { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, arg); + JSValue value = callframe->argument(0); + if (value.isCallable()) { + if (!thisObject->implementation) { + auto catcher = DECLARE_CATCH_SCOPE(vm); + WTF::String nameToUse; + if (auto* fn = jsDynamicCast<JSFunction*>(value)) { + nameToUse = fn->name(vm); + } else if (auto* fn = jsDynamicCast<InternalFunction*>(value)) { + nameToUse = fn->name(); + } + if (nameToUse.length()) { + thisObject->setName(nameToUse); + } + if (catcher.exception()) + catcher.clearException(); + } + + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, value); } else { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, arg); + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, value); } } @@ -1003,6 +1057,8 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionWithImplementation, (JSC::JSGlobalObject RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined())); } + HANDLE_STATIC_CALL; + JSValue arg = callframe->argument(0); if (callframe->argumentCount() < 1 || arg.isEmpty() || arg.isUndefined()) { @@ -1025,6 +1081,9 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockName, (JSC::JSGlobalObject * globalOb throwTypeError(globalObject, scope, "Expected Mock"_s); return {}; } + + HANDLE_STATIC_CALL; + if (callframe->argumentCount() > 0) { auto* newName = callframe->argument(0).toStringOrNull(globalObject); if (UNLIKELY(!newName)) { @@ -1046,6 +1105,7 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnThis, (JSC::JSGlobalObject * gl throwTypeError(globalObject, scope, "Expected Mock"_s); } + HANDLE_STATIC_CALL; pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnThis, jsUndefined()); RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); @@ -1076,6 +1136,7 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValueOnce, (JSC::JSGlobalObject throwTypeError(globalObject, scope, "Expected Mock"_s); } + HANDLE_STATIC_CALL; if (callframe->argumentCount() < 1) { pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, jsUndefined()); } else { @@ -1084,15 +1145,19 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValueOnce, (JSC::JSGlobalObject RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); } -JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callframe)) { + auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject); JSMockFunction* thisObject = jsDynamicCast<JSMockFunction*>(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); if (UNLIKELY(!thisObject)) { throwTypeError(globalObject, scope, "Expected Mock"_s); } + HANDLE_STATIC_CALL; + if (callframe->argumentCount() < 1) { pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, jsUndefined())); } else { @@ -1110,6 +1175,8 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValueOnce, (JSC::JSGlobalObje throwTypeError(globalObject, scope, "Expected Mock"_s); } + HANDLE_STATIC_CALL; + if (callframe->argumentCount() < 1) { pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, jsUndefined())); } else { @@ -1127,6 +1194,8 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValue, (JSC::JSGlobalObject * throwTypeError(globalObject, scope, "Expected Mock"_s); } + HANDLE_STATIC_CALL; + if (callframe->argumentCount() < 1) { pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::rejectedPromise(globalObject, jsUndefined())); } else { @@ -1144,6 +1213,8 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValueOnce, (JSC::JSGlobalObje throwTypeError(globalObject, scope, "Expected Mock"_s); } + HANDLE_STATIC_CALL; + if (callframe->argumentCount() < 1) { pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::rejectedPromise(globalObject, jsUndefined())); } else { |