aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/bindings/JSMockFunction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/bindings/JSMockFunction.cpp')
-rw-r--r--src/bun.js/bindings/JSMockFunction.cpp1154
1 files changed, 1154 insertions, 0 deletions
diff --git a/src/bun.js/bindings/JSMockFunction.cpp b/src/bun.js/bindings/JSMockFunction.cpp
new file mode 100644
index 000000000..6aeb44ded
--- /dev/null
+++ b/src/bun.js/bindings/JSMockFunction.cpp
@@ -0,0 +1,1154 @@
+#include "root.h"
+
+#include "JSMockFunction.h"
+#include <JavaScriptCore/JSPromise.h>
+#include "ZigGlobalObject.h"
+#include <JavaScriptCore/InternalFunction.h>
+#include "JavaScriptCore/Completion.h"
+#include "JavaScriptCore/ObjectConstructor.h"
+#include "ExtendedDOMClientIsoSubspaces.h"
+#include "ExtendedDOMIsoSubspaces.h"
+#include "BunClientData.h"
+#include "JavaScriptCore/LazyProperty.h"
+#include "JavaScriptCore/JSCJSValueInlines.h"
+#include "JavaScriptCore/JSInternalPromise.h"
+#include "JavaScriptCore/LazyPropertyInlines.h"
+#include "JavaScriptCore/VMTrapsInlines.h"
+#include <JavaScriptCore/Weak.h>
+#include <JavaScriptCore/GetterSetter.h>
+#include <JavaScriptCore/WeakMapImpl.h>
+
+namespace Bun {
+
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionCall);
+JSC_DECLARE_CUSTOM_GETTER(jsMockFunctionGetter_protoImpl);
+JSC_DECLARE_CUSTOM_GETTER(jsMockFunctionGetter_mock);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionGetMockImplementation);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionGetMockName);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockClear);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockReset);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockRestore);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockImplementation);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockImplementationOnce);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockName);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockReturnThis);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockReturnValue);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockReturnValueOnce);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockResolvedValue);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockResolvedValueOnce);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockRejectedValue);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockRejectedValueOnce);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionWithImplementation);
+JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockImplementationOnce);
+
+// This is taken from JSWeakSet
+// We only want to hold onto the list of active spies which haven't already been collected
+// So we use a WeakSet
+// Unlike using WeakSet from JS, we are able to iterate through the WeakSet.
+class ActiveSpySet final : public WeakMapImpl<WeakMapBucket<WeakMapBucketDataKey>> {
+public:
+ using Base = WeakMapImpl<WeakMapBucket<WeakMapBucketDataKey>>;
+
+ DECLARE_EXPORT_INFO;
+
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+ {
+ return Structure::create(vm, globalObject, prototype, TypeInfo(JSWeakSetType, StructureFlags), info());
+ }
+
+ static ActiveSpySet* create(VM& vm, Structure* structure)
+ {
+ ActiveSpySet* instance = new (NotNull, allocateCell<ActiveSpySet>(vm)) ActiveSpySet(vm, structure);
+ instance->finishCreation(vm);
+ return instance;
+ }
+
+private:
+ ActiveSpySet(VM& vm, Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+};
+
+static_assert(std::is_final<ActiveSpySet>::value, "Required for JSType based casting");
+const ClassInfo ActiveSpySet::s_info = { "ActiveSpySet"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ActiveSpySet) };
+
+class JSMockImplementation final : public JSNonFinalObject {
+public:
+ enum class Kind : uint8_t {
+ Call,
+ Promise,
+ ReturnValue,
+ ThrowValue,
+ ReturnThis,
+ };
+
+ static JSMockImplementation* create(JSC::JSGlobalObject* globalObject, JSC::Structure* structure, Kind kind, JSC::JSValue heldValue, bool isOnce)
+ {
+ auto& vm = globalObject->vm();
+ JSMockImplementation* impl = new (NotNull, allocateCell<JSMockImplementation>(vm)) JSMockImplementation(vm, structure, kind);
+ impl->finishCreation(vm, heldValue, isOnce ? jsNumber(1) : jsUndefined());
+ return impl;
+ }
+
+ using Base = JSC::JSNonFinalObject;
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+ {
+ return Structure::create(vm, globalObject, prototype, TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return WebCore::subspaceForImpl<JSMockImplementation, UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForJSMockImplementation.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSMockImplementation = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForJSMockImplementation.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForJSMockImplementation = std::forward<decltype(space)>(space); });
+ }
+
+ static constexpr unsigned numberOfInternalFields = 2;
+
+ mutable JSC::WriteBarrier<Unknown> internalFields[2];
+
+ DECLARE_EXPORT_INFO;
+ DECLARE_VISIT_CHILDREN;
+
+ Kind kind { Kind::ReturnValue };
+
+ bool isOnce()
+ {
+ auto secondField = internalFields[1].get();
+ if (secondField.isNumber() && secondField.asInt32() == 1) {
+ return true;
+ }
+ return jsDynamicCast<JSMockImplementation*>(secondField.asCell());
+ }
+
+ JSMockImplementation(JSC::VM& vm, JSC::Structure* structure, Kind kind)
+ : Base(vm, structure)
+ , kind(kind)
+ {
+ }
+
+ void finishCreation(JSC::VM& vm, JSC::JSValue first, JSC::JSValue second)
+ {
+ Base::finishCreation(vm);
+ this->internalFields[0].set(vm, this, first);
+ this->internalFields[1].set(vm, this, second);
+ }
+};
+
+template<typename Visitor>
+void JSMockImplementation::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+ JSMockImplementation* fn = jsCast<JSMockImplementation*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(fn, info());
+ Base::visitChildren(fn, visitor);
+
+ visitor.append(fn->internalFields[0]);
+ visitor.append(fn->internalFields[1]);
+}
+
+DEFINE_VISIT_CHILDREN(JSMockImplementation);
+
+enum class CallbackKind : uint8_t {
+ Wrapper,
+ Call,
+ GetterSetter,
+};
+
+static NativeFunction jsMockFunctionForCallbackKind(CallbackKind kind)
+{
+ switch (kind) {
+ // return jsMockFunctionGetterSetter;
+ case CallbackKind::Wrapper:
+ return jsMockFunctionMockImplementation;
+ case CallbackKind::GetterSetter:
+ case CallbackKind::Call:
+ return jsMockFunctionCall;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
+const ClassInfo JSMockImplementation::s_info = { "MockImpl"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMockImplementation) };
+
+class JSMockFunction : public JSC::InternalFunction {
+public:
+ using Base = JSC::InternalFunction;
+ static constexpr unsigned StructureFlags = Base::StructureFlags;
+
+ static JSMockFunction* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, CallbackKind kind = CallbackKind::Call)
+ {
+ JSMockFunction* function = new (NotNull, JSC::allocateCell<JSMockFunction>(vm)) JSMockFunction(vm, structure, kind);
+ function->finishCreation(vm);
+ return function;
+ }
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+ {
+ return Structure::create(vm, globalObject, prototype, TypeInfo(InternalFunctionType, StructureFlags), info());
+ }
+
+ DECLARE_INFO;
+ DECLARE_VISIT_CHILDREN;
+
+ JSC::LazyProperty<JSMockFunction, JSObject> mock;
+ mutable JSC::WriteBarrier<JSC::Unknown> implementation;
+ mutable JSC::WriteBarrier<JSC::JSArray> calls;
+ mutable JSC::WriteBarrier<JSC::JSArray> contexts;
+ mutable JSC::WriteBarrier<JSC::JSArray> instances;
+ mutable JSC::WriteBarrier<JSC::JSArray> returnValues;
+ mutable JSC::WriteBarrier<JSC::Unknown> tail;
+
+ JSC::Weak<JSObject> spyTarget;
+ JSC::Identifier spyIdentifier;
+ unsigned spyAttributes = 0;
+
+ void initMock()
+ {
+ mock.initLater(
+ [](const JSC::LazyProperty<JSMockFunction, JSObject>::Initializer& init) {
+ JSMockFunction* mock = init.owner;
+ Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(mock->globalObject());
+ JSC::Structure* structure = globalObject->mockModule.mockObjectStructure.getInitializedOnMainThread(globalObject);
+ JSObject* object = JSC::constructEmptyObject(init.vm, structure);
+ object->putDirectOffset(init.vm, 0, mock->getCalls());
+ object->putDirectOffset(init.vm, 1, mock->getContexts());
+ object->putDirectOffset(init.vm, 2, mock->getInstances());
+ object->putDirectOffset(init.vm, 3, mock->getReturnValues());
+ init.set(object);
+ });
+ }
+
+ void reset()
+ {
+ this->calls.clear();
+ this->instances.clear();
+ this->returnValues.clear();
+ this->contexts.clear();
+
+ if (this->mock.isInitialized()) {
+ this->initMock();
+ }
+ }
+
+ void clearSpy()
+ {
+ if (auto* target = this->spyTarget.get()) {
+ JSValue implValue = jsUndefined();
+ if (auto* impl = jsDynamicCast<JSMockImplementation*>(this->implementation.get())) {
+ implValue = impl->internalFields[0].get();
+ }
+
+ // Reset the spy back to the original value.
+ target->putDirect(this->vm(), this->spyIdentifier, implValue, this->spyAttributes);
+ }
+
+ this->spyTarget.clear();
+ this->spyIdentifier = JSC::Identifier();
+ this->spyAttributes = 0;
+ }
+
+ JSArray* getCalls() const
+ {
+ JSArray* val = calls.get();
+ if (!val) {
+ val = JSC::constructEmptyArray(globalObject(), nullptr, 0);
+ this->calls.set(vm(), this, val);
+ }
+ return val;
+ }
+ JSArray* getContexts() const
+ {
+ JSArray* val = contexts.get();
+ if (!val) {
+ val = JSC::constructEmptyArray(globalObject(), nullptr, 0);
+ this->contexts.set(vm(), this, val);
+ }
+ return val;
+ }
+ JSArray* getInstances() const
+ {
+ JSArray* val = instances.get();
+ if (!val) {
+ val = JSC::constructEmptyArray(globalObject(), nullptr, 0);
+ this->instances.set(vm(), this, val);
+ }
+ return val;
+ }
+ JSArray* getReturnValues() const
+ {
+ JSArray* val = returnValues.get();
+ if (!val) {
+ val = JSC::constructEmptyArray(globalObject(), nullptr, 0);
+ this->returnValues.set(vm(), this, val);
+ }
+ return val;
+ }
+
+ template<typename, JSC::SubspaceAccess mode>
+ static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return WebCore::subspaceForImpl<JSMockFunction, UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForJSMockFunction.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSMockFunction = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForJSMockFunction.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForJSMockFunction = std::forward<decltype(space)>(space); });
+ }
+
+ JSMockFunction(JSC::VM& vm, JSC::Structure* structure, CallbackKind wrapKind)
+ : Base(vm, structure, jsMockFunctionForCallbackKind(wrapKind), jsMockFunctionForCallbackKind(wrapKind))
+ {
+ initMock();
+ }
+};
+
+template<typename Visitor>
+void JSMockFunction::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+ JSMockFunction* fn = jsCast<JSMockFunction*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(fn, info());
+ Base::visitChildren(fn, visitor);
+
+ visitor.append(fn->implementation);
+ visitor.append(fn->calls);
+ visitor.append(fn->contexts);
+ visitor.append(fn->instances);
+ visitor.append(fn->returnValues);
+ visitor.append(fn->tail);
+ fn->mock.visit(visitor);
+}
+DEFINE_VISIT_CHILDREN(JSMockFunction);
+
+static void pushImplInternal(JSMockFunction* fn, JSGlobalObject* jsGlobalObject, JSMockImplementation::Kind kind, JSValue value, bool isOnce)
+{
+ 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 currentImpl = fn->implementation.get();
+ if (currentTail) {
+ if (auto* current = jsDynamicCast<JSMockImplementation*>(currentTail)) {
+ current->internalFields[1].set(vm, current, impl);
+ }
+ }
+ fn->tail.set(vm, fn, impl);
+ if (!currentImpl || !currentImpl.inherits<JSMockImplementation>()) {
+ fn->implementation.set(vm, fn, impl);
+ }
+}
+
+static void pushImpl(JSMockFunction* fn, JSGlobalObject* globalObject, JSMockImplementation::Kind kind, JSValue value)
+{
+ pushImplInternal(fn, globalObject, kind, value, false);
+}
+
+static void pushImplOnce(JSMockFunction* fn, JSGlobalObject* globalObject, JSMockImplementation::Kind kind, JSValue value)
+{
+ pushImplInternal(fn, globalObject, kind, value, true);
+}
+
+class JSMockFunctionPrototype final : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+
+ static JSMockFunctionPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSMockFunctionPrototype* ptr = new (NotNull, JSC::allocateCell<JSMockFunctionPrototype>(vm)) JSMockFunctionPrototype(vm, globalObject, structure);
+ ptr->finishCreation(vm, globalObject);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, JSC::SubspaceAccess>
+ 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:
+ JSMockFunctionPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
+};
+
+static const HashTableValue JSMockFunctionPrototypeTableValues[] = {
+ { "mock"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsMockFunctionGetter_mock, 0 } },
+ { "_protoImpl"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, jsMockFunctionGetter_protoImpl, 0 } },
+ { "getMockImplementation"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionGetMockImplementation, 0 } },
+ { "getMockName"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionGetMockName, 0 } },
+ { "mockClear"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockClear, 0 } },
+ { "mockReset"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockReset, 0 } },
+ { "mockRestore"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockRestore, 0 } },
+ { "mockImplementation"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockImplementation, 1 } },
+ { "mockImplementationOnce"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockImplementationOnce, 1 } },
+ { "withImplementation"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionWithImplementation, 1 } },
+ { "mockName"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockName, 1 } },
+ { "mockReturnThis"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockReturnThis, 1 } },
+ { "mockReturnValue"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockReturnValue, 1 } },
+ { "mockReturnValueOnce"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockReturnValueOnce, 1 } },
+ { "mockResolvedValue"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockResolvedValue, 1 } },
+ { "mockResolvedValueOnce"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockResolvedValueOnce, 1 } },
+ { "mockRejectedValueOnce"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockRejectedValue, 1 } },
+ { "mockRejectedValue"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockRejectedValueOnce, 1 } },
+};
+
+const ClassInfo JSMockFunction::s_info = { "Mock"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMockFunction) };
+
+class SpyWeakHandleOwner final : public JSC::WeakHandleOwner {
+public:
+ void finalize(JSC::Handle<JSC::Unknown>, void* context) final {}
+};
+
+static SpyWeakHandleOwner& weakValueHandleOwner()
+{
+ static NeverDestroyed<SpyWeakHandleOwner> jscWeakValueHandleOwner;
+ return jscWeakValueHandleOwner;
+}
+
+const ClassInfo JSMockFunctionPrototype::s_info = { "Mock"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMockFunctionPrototype) };
+
+extern "C" void JSMock__resetSpies(Zig::GlobalObject* globalObject)
+{
+ if (!globalObject->mockModule.activeSpies) {
+ return;
+ }
+ auto spiesValue = globalObject->mockModule.activeSpies.get();
+
+ ActiveSpySet* activeSpies = jsCast<ActiveSpySet*>(spiesValue);
+ MarkedArgumentBuffer active;
+ activeSpies->takeSnapshot(active);
+ size_t size = active.size();
+
+ for (size_t i = 0; i < size; ++i) {
+ JSValue spy = active.at(i);
+ if (!spy.isObject())
+ continue;
+
+ auto* spyObject = jsCast<JSMockFunction*>(spy);
+ spyObject->reset();
+ spyObject->clearSpy();
+ }
+ globalObject->mockModule.activeSpies.clear();
+}
+
+extern "C" EncodedJSValue jsFunctionResetSpies(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe)
+{
+ JSMock__resetSpies(jsCast<Zig::GlobalObject*>(globalObject));
+ return JSValue::encode(jsUndefined());
+}
+
+extern "C" EncodedJSValue JSMock__spyOn(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callframe)
+{
+ auto& vm = lexicalGlobalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto* globalObject = jsDynamicCast<Zig::GlobalObject*>(lexicalGlobalObject);
+ if (UNLIKELY(!globalObject)) {
+ throwVMError(globalObject, scope, "Cannot run spyOn from a different global context"_s);
+ return {};
+ }
+
+ JSValue objectValue = callframe->argument(0);
+ JSValue propertyKeyValue = callframe->argument(1);
+
+ if (callframe->argumentCount() < 2 || !objectValue.isObject()) {
+ throwVMError(globalObject, scope, "spyOn(target, prop) expects a target object and a property key"_s);
+ return {};
+ }
+
+ PropertyName propertyKey = propertyKeyValue.toPropertyKey(globalObject);
+ RETURN_IF_EXCEPTION(scope, {});
+
+ if (propertyKey.isNull()) {
+ throwVMError(globalObject, scope, "spyOn(target, prop) expects a property key"_s);
+ return {};
+ }
+
+ JSC::JSObject* object = objectValue.getObject();
+ if (object->type() == JSC::JSType::GlobalProxyType)
+ object = jsCast<JSC::JSGlobalProxy*>(object)->target();
+
+ JSC::PropertySlot slot(object, JSC::PropertySlot::InternalMethodType::HasProperty);
+ bool hasValue = object->getPropertySlot(globalObject, propertyKey, slot);
+
+ // easymode: regular property or missing property
+ if (!hasValue || slot.isValue()) {
+ auto* mock = JSMockFunction::create(vm, globalObject, globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject), CallbackKind::GetterSetter);
+ mock->spyTarget = JSC::Weak<JSObject>(object, &weakValueHandleOwner(), nullptr);
+ mock->spyIdentifier = propertyKey.isSymbol() ? Identifier::fromUid(vm, propertyKey.uid()) : Identifier::fromString(vm, propertyKey.publicName());
+ mock->spyAttributes = hasValue ? slot.attributes() : 0;
+ unsigned attributes = 0;
+ JSValue value = jsUndefined();
+
+ if (hasValue)
+ value = slot.getValue(globalObject, propertyKey);
+
+ if (hasValue && ((slot.attributes() & PropertyAttribute::Function) != 0 || (value.isCell() && value.isCallable()))) {
+ if (hasValue)
+ attributes = slot.attributes();
+
+ attributes |= PropertyAttribute::Function;
+ object->putDirect(vm, propertyKey, mock, attributes);
+ RETURN_IF_EXCEPTION(scope, {});
+
+ pushImpl(mock, globalObject, JSMockImplementation::Kind::Call, value);
+ } else {
+ if (hasValue)
+ attributes = slot.attributes();
+
+ attributes |= PropertyAttribute::Accessor;
+ object->putDirect(vm, propertyKey, JSC::GetterSetter::create(vm, globalObject, mock, mock), attributes);
+ RETURN_IF_EXCEPTION(scope, {});
+
+ pushImpl(mock, globalObject, JSMockImplementation::Kind::ReturnValue, value);
+ }
+
+ if (!globalObject->mockModule.activeSpies) {
+ ActiveSpySet* activeSpies = ActiveSpySet::create(vm, globalObject->mockModule.activeSpySetStructure.getInitializedOnMainThread(globalObject));
+ globalObject->mockModule.activeSpies.set(vm, activeSpies);
+ }
+
+ ActiveSpySet* activeSpies = jsCast<ActiveSpySet*>(globalObject->mockModule.activeSpies.get());
+ activeSpies->add(vm, mock, mock);
+
+ return JSValue::encode(mock);
+ }
+
+ // hardmode: accessor property
+ throwVMError(globalObject, scope, "spyOn(target, prop) does not support accessor properties yet"_s);
+ return {};
+}
+
+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()));
+
+ init.set(JSMockFunction::createStructure(init.vm, init.owner, prototype));
+ });
+ mock.mockResultStructure.initLater(
+ [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) {
+ Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(init.owner);
+ JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(
+ globalObject,
+ globalObject->objectPrototype(),
+ 2);
+ JSC::PropertyOffset offset;
+
+ structure = structure->addPropertyTransition(
+ init.vm,
+ structure,
+ JSC::Identifier::fromString(init.vm, "type"_s),
+ 0,
+ offset);
+
+ structure = structure->addPropertyTransition(
+ init.vm,
+ structure,
+ JSC::Identifier::fromString(init.vm, "value"_s),
+
+ 0,
+ offset);
+
+ init.set(structure);
+ });
+ mock.activeSpySetStructure.initLater([](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) {
+ Structure* implementation = ActiveSpySet::createStructure(init.vm, init.owner, jsNull());
+ init.set(implementation);
+ });
+ mock.mockImplementationStructure.initLater(
+ [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) {
+ Structure* implementation = JSMockImplementation::createStructure(init.vm, init.owner, jsNull());
+ init.set(implementation);
+ });
+ mock.mockObjectStructure.initLater(
+ [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) {
+ Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(init.owner);
+ JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(
+ globalObject,
+ globalObject->objectPrototype(),
+ 4);
+ JSC::PropertyOffset offset;
+ structure = structure->addPropertyTransition(
+ init.vm,
+ structure,
+ JSC::Identifier::fromString(init.vm, "calls"_s),
+ JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly,
+ offset);
+ structure = structure->addPropertyTransition(
+ init.vm,
+ structure,
+ JSC::Identifier::fromString(init.vm, "contexts"_s),
+ JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly,
+ offset);
+ structure = structure->addPropertyTransition(
+ init.vm,
+ structure,
+ JSC::Identifier::fromString(init.vm, "instances"_s),
+ JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly,
+ offset);
+ structure = structure->addPropertyTransition(
+ init.vm,
+ structure,
+ JSC::Identifier::fromString(init.vm, "results"_s),
+ JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly,
+ offset);
+
+ init.set(structure);
+ });
+ return mock;
+}
+
+extern Structure* createMockResultStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
+{
+ JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(
+ globalObject,
+ globalObject->objectPrototype(),
+ 2);
+ JSC::PropertyOffset offset;
+
+ structure = structure->addPropertyTransition(
+ vm,
+ structure,
+ JSC::Identifier::fromString(vm, "type"_s),
+ 0,
+ offset);
+
+ structure = structure->addPropertyTransition(
+ vm,
+ structure,
+ JSC::Identifier::fromString(vm, "value"_s),
+ 0, offset);
+ return structure;
+}
+
+static JSValue createMockResult(JSC::VM& vm, Zig::GlobalObject* globalObject, const WTF::String& type, JSC::JSValue value)
+{
+ JSC::Structure* structure = globalObject->mockModule.mockResultStructure.getInitializedOnMainThread(globalObject);
+
+ JSC::JSObject* result = JSC::constructEmptyObject(vm, structure);
+ result->putDirectOffset(vm, 0, jsString(vm, type));
+ result->putDirectOffset(vm, 1, value);
+ return result;
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObject, CallFrame* callframe))
+{
+ Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
+ auto& vm = globalObject->vm();
+ JSMockFunction* fn = jsDynamicCast<JSMockFunction*>(callframe->jsCallee());
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ if (UNLIKELY(!fn)) {
+ throwTypeError(globalObject, scope, "Expected callee to be mock function"_s);
+ return {};
+ }
+
+ JSC::ArgList args = JSC::ArgList(callframe);
+ JSValue thisValue = callframe->thisValue();
+ JSC::JSArray* argumentsArray = nullptr;
+ {
+ JSC::ObjectInitializationScope object(vm);
+ argumentsArray = JSC::JSArray::tryCreateUninitializedRestricted(
+ object,
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
+ callframe->argumentCount());
+ for (size_t i = 0; i < args.size(); i++) {
+ argumentsArray->initializeIndex(object, i, args.at(i));
+ }
+ }
+
+ JSC::JSArray* calls = fn->calls.get();
+ if (calls) {
+ calls->push(globalObject, argumentsArray);
+ } else {
+ JSC::ObjectInitializationScope object(vm);
+ calls = JSC::JSArray::tryCreateUninitializedRestricted(
+ object,
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
+ 1);
+ calls->initializeIndex(object, 0, argumentsArray);
+ }
+ fn->calls.set(vm, fn, calls);
+
+ JSC::JSArray* contexts = fn->contexts.get();
+ if (contexts) {
+ contexts->push(globalObject, thisValue);
+ } else {
+ JSC::ObjectInitializationScope object(vm);
+ contexts = JSC::JSArray::tryCreateUninitializedRestricted(
+ object,
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
+ 1);
+ contexts->initializeIndex(object, 0, thisValue);
+ }
+ fn->contexts.set(vm, fn, contexts);
+
+ JSValue implementationValue = fn->implementation.get();
+ if (!implementationValue)
+ implementationValue = jsUndefined();
+
+ if (auto* impl = jsDynamicCast<JSMockImplementation*>(implementationValue)) {
+ if (JSValue nextValue = impl->internalFields[1].get()) {
+ if (nextValue.inherits<JSMockImplementation>() || (nextValue.isInt32() && nextValue.asInt32() == 1)) {
+ fn->implementation.set(vm, fn, nextValue);
+ }
+ }
+
+ unsigned int returnValueIndex = 0;
+ auto setReturnValue = [&](JSC::JSValue value) -> void {
+ if (auto* returnValuesArray = fn->returnValues.get()) {
+ returnValuesArray->push(globalObject, value);
+ returnValueIndex = returnValuesArray->length() - 1;
+ } else {
+ JSC::ObjectInitializationScope object(vm);
+ returnValuesArray = JSC::JSArray::tryCreateUninitializedRestricted(
+ object,
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
+ 1);
+ returnValuesArray->initializeIndex(object, 0, value);
+ fn->returnValues.set(vm, fn, returnValuesArray);
+ }
+ };
+
+ switch (impl->kind) {
+ case JSMockImplementation::Kind::Call: {
+ JSValue result = impl->internalFields[0].get();
+ JSC::CallData callData = JSC::getCallData(result);
+ if (UNLIKELY(callData.type == JSC::CallData::Type::None)) {
+ throwTypeError(globalObject, scope, "Expected mock implementation to be callable"_s);
+ return {};
+ }
+
+ setReturnValue(createMockResult(vm, globalObject, "incomplete"_s, jsUndefined()));
+
+ WTF::NakedPtr<JSC::Exception> exception;
+
+ JSValue returnValue = call(globalObject, result, callData, thisValue, args, exception);
+
+ if (auto* exc = exception.get()) {
+ if (auto* returnValuesArray = fn->returnValues.get()) {
+ returnValuesArray->putDirectIndex(globalObject, returnValueIndex, createMockResult(vm, globalObject, "throw"_s, exc->value()));
+ fn->returnValues.set(vm, fn, returnValuesArray);
+ JSC::throwException(globalObject, scope, exc);
+ return {};
+ }
+ }
+
+ if (UNLIKELY(!returnValue)) {
+ returnValue = jsUndefined();
+ }
+
+ if (auto* returnValuesArray = fn->returnValues.get()) {
+ returnValuesArray->putDirectIndex(globalObject, returnValueIndex, createMockResult(vm, globalObject, "return"_s, returnValue));
+ fn->returnValues.set(vm, fn, returnValuesArray);
+ }
+
+ return JSValue::encode(returnValue);
+ }
+ case JSMockImplementation::Kind::ReturnValue:
+ case JSMockImplementation::Kind::Promise: {
+ JSValue returnValue = impl->internalFields[0].get();
+ setReturnValue(createMockResult(vm, globalObject, "return"_s, returnValue));
+ return JSValue::encode(returnValue);
+ }
+ case JSMockImplementation::Kind::ReturnThis: {
+ setReturnValue(createMockResult(vm, globalObject, "return"_s, thisValue));
+ return JSValue::encode(thisValue);
+ }
+ default: {
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ }
+ }
+
+ return JSValue::encode(jsUndefined());
+}
+
+void JSMockFunctionPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
+{
+ Base::finishCreation(vm);
+ reifyStaticProperties(vm, JSMockFunction::info(), JSMockFunctionPrototypeTableValues, *this);
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+
+ this->putDirect(vm, Identifier::fromString(vm, "_isMockFunction"_s), jsBoolean(true), 0);
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetMockImplementation, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ JSValue impl = thisObject->implementation.get();
+ if (auto* implementation = jsDynamicCast<JSMockImplementation*>(impl)) {
+ if (implementation->kind == JSMockImplementation::Kind::Call) {
+ RELEASE_AND_RETURN(scope, JSValue::encode(implementation->internalFields[0].get()));
+ }
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsMockFunctionGetter_mock, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
+{
+ Bun::JSMockFunction* thisObject = jsDynamicCast<Bun::JSMockFunction*>(JSValue::decode(thisValue));
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ if (UNLIKELY(!thisObject)) {
+ throwTypeError(globalObject, scope, "Expected Mock"_s);
+ return {};
+ }
+
+ return JSValue::encode(thisObject->mock.getInitializedOnMainThread(thisObject));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsMockFunctionGetter_protoImpl, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
+{
+ Bun::JSMockFunction* thisObject = jsDynamicCast<Bun::JSMockFunction*>(JSValue::decode(thisValue));
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ if (UNLIKELY(!thisObject)) {
+ throwTypeError(globalObject, scope, "Expected Mock"_s);
+ return {};
+ }
+
+ if (auto implValue = thisObject->implementation.get()) {
+ if (auto* impl = jsDynamicCast<JSMockImplementation*>(implValue)) {
+ if (impl->kind == JSMockImplementation::Kind::Call) {
+ return JSValue::encode(impl->internalFields[0].get());
+ }
+
+ return JSValue::encode(jsUndefined());
+ }
+ }
+
+ return JSValue::encode(jsUndefined());
+}
+
+extern "C" EncodedJSValue JSMockFunction__createObject(Zig::GlobalObject* globalObject)
+{
+ return JSValue::encode(
+ JSMockFunction::create(globalObject->vm(), globalObject, globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject), CallbackKind::Wrapper));
+}
+
+extern "C" EncodedJSValue JSMockFunction__getCalls(EncodedJSValue encodedValue)
+{
+ JSValue value = JSValue::decode(encodedValue);
+ if (value) {
+ if (auto* mock = jsDynamicCast<JSMockFunction*>(value)) {
+ return JSValue::encode(mock->getCalls());
+ }
+ }
+
+ return JSValue::encode({});
+}
+extern "C" EncodedJSValue JSMockFunction__getReturns(EncodedJSValue encodedValue)
+{
+ JSValue value = JSValue::decode(encodedValue);
+ if (value) {
+ if (auto* mock = jsDynamicCast<JSMockFunction*>(value)) {
+ return JSValue::encode(mock->getReturnValues());
+ }
+ }
+
+ return JSValue::encode({});
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetMockName, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ JSValue implValue = thisObject->implementation.get();
+ if (!implValue) {
+ implValue = jsUndefined();
+ }
+
+ if (auto* impl = jsDynamicCast<JSMockImplementation*>(implValue)) {
+ if (impl->kind == JSMockImplementation::Kind::Call) {
+ JSObject* object = impl->internalFields[0].get().asCell()->getObject();
+ if (auto nameValue = object->getIfPropertyExists(globalObject, PropertyName(vm.propertyNames->name))) {
+ RELEASE_AND_RETURN(scope, JSValue::encode(nameValue));
+ }
+
+ RETURN_IF_EXCEPTION(scope, {});
+ }
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(jsEmptyString(vm)));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockClear, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ thisObject->reset();
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReset, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ thisObject->reset();
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRestore, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementation, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callframe))
+{
+ auto& vm = lexicalGlobalObject->vm();
+ auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSMockFunction* thisObject = JSMockFunction::create(
+ vm,
+ globalObject,
+ globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject));
+
+ if (UNLIKELY(!thisObject)) {
+ throwOutOfMemoryError(globalObject, scope);
+ return {};
+ }
+
+ if (callframe->argumentCount() > 0) {
+ JSValue arg = callframe->argument(0);
+ if (arg.isCallable()) {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, arg);
+ } else {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, arg);
+ }
+ }
+
+ return JSValue::encode(thisObject);
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementationOnce, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callframe))
+{
+ auto& vm = lexicalGlobalObject->vm();
+ auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ 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 {};
+ }
+
+ if (callframe->argumentCount() > 0) {
+ JSValue arg = callframe->argument(0);
+ if (arg.isCallable()) {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, arg);
+ } else {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, arg);
+ }
+ }
+
+ return JSValue::encode(thisObject);
+}
+
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionWithImplementation, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
+ }
+
+ JSValue arg = callframe->argument(0);
+
+ if (callframe->argumentCount() < 1 || arg.isEmpty() || arg.isUndefined()) {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, jsUndefined());
+ } else if (arg.isCallable()) {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, arg);
+ } else {
+ throwTypeError(globalObject, scope, "Expected a function or undefined"_s);
+ RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockName, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ return {};
+ }
+ if (callframe->argumentCount() > 0) {
+ auto* newName = callframe->argument(0).toStringOrNull(globalObject);
+ if (UNLIKELY(!newName)) {
+ return {};
+ }
+
+ thisObject->putDirect(vm, vm.propertyNames->name, newName, 0);
+ RELEASE_AND_RETURN(scope, JSValue::encode(newName));
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(jsString(vm, thisObject->calculatedDisplayName(vm))));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnThis, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnThis, jsUndefined());
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ if (callframe->argumentCount() < 1) {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, jsUndefined());
+ } else {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, callframe->argument(0));
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValueOnce, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ if (callframe->argumentCount() < 1) {
+ pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, jsUndefined());
+ } else {
+ pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, callframe->argument(0));
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ if (callframe->argumentCount() < 1) {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, jsUndefined()));
+ } else {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0)));
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValueOnce, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ if (callframe->argumentCount() < 1) {
+ pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, jsUndefined()));
+ } else {
+ pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0)));
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ if (callframe->argumentCount() < 1) {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::rejectedPromise(globalObject, jsUndefined()));
+ } else {
+ pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::rejectedPromise(globalObject, callframe->argument(0)));
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValueOnce, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
+{
+ 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);
+ }
+
+ if (callframe->argumentCount() < 1) {
+ pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::rejectedPromise(globalObject, jsUndefined()));
+ } else {
+ pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0)));
+ }
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(thisObject));
+}
+} \ No newline at end of file