From 29b22175bf6fc726d0b028c7bf5619ab89fca09a Mon Sep 17 00:00:00 2001 From: dave caruso Date: Fri, 15 Sep 2023 11:22:06 -0400 Subject: feat(runtime): add `process.binding` `uv`/`natives`/`config` + make global object properties lazy (#5355) * binding uv * we did that * some more bindings * fix doc * fix uv * yo * static hash table nonsense <3 * huge refactor to the global object i am not ready for merge conflicts * it works part 3 * lose --------- Co-authored-by: Jarred Sumner --- .../ZigGeneratedClasses+lazyStructureImpl.h | 100 ++++++++++----------- 1 file changed, 50 insertions(+), 50 deletions(-) (limited to 'src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h') diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h index cb4c47ccb..b51752180 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h +++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h @@ -303,54 +303,54 @@ void GlobalObject::initGeneratedLazyClasses() { template void GlobalObject::visitGeneratedLazyClasses(GlobalObject *thisObject, Visitor& visitor) { - thisObject->m_JSAttributeIterator.visit(visitor); visitor.append(thisObject->m_JSAttributeIteratorSetterValue); - thisObject->m_JSBigIntStats.visit(visitor); visitor.append(thisObject->m_JSBigIntStatsSetterValue); - thisObject->m_JSBlob.visit(visitor); visitor.append(thisObject->m_JSBlobSetterValue); - thisObject->m_JSBuildArtifact.visit(visitor); visitor.append(thisObject->m_JSBuildArtifactSetterValue); - thisObject->m_JSBuildMessage.visit(visitor); visitor.append(thisObject->m_JSBuildMessageSetterValue); - thisObject->m_JSComment.visit(visitor); visitor.append(thisObject->m_JSCommentSetterValue); - thisObject->m_JSCrypto.visit(visitor); visitor.append(thisObject->m_JSCryptoSetterValue); - thisObject->m_JSCryptoHasher.visit(visitor); visitor.append(thisObject->m_JSCryptoHasherSetterValue); - thisObject->m_JSDebugHTTPSServer.visit(visitor); visitor.append(thisObject->m_JSDebugHTTPSServerSetterValue); - thisObject->m_JSDebugHTTPServer.visit(visitor); visitor.append(thisObject->m_JSDebugHTTPServerSetterValue); - thisObject->m_JSDirent.visit(visitor); visitor.append(thisObject->m_JSDirentSetterValue); - thisObject->m_JSDocEnd.visit(visitor); visitor.append(thisObject->m_JSDocEndSetterValue); - thisObject->m_JSDocType.visit(visitor); visitor.append(thisObject->m_JSDocTypeSetterValue); - thisObject->m_JSElement.visit(visitor); visitor.append(thisObject->m_JSElementSetterValue); - thisObject->m_JSEndTag.visit(visitor); visitor.append(thisObject->m_JSEndTagSetterValue); - thisObject->m_JSExpect.visit(visitor); visitor.append(thisObject->m_JSExpectSetterValue); - thisObject->m_JSExpectAny.visit(visitor); visitor.append(thisObject->m_JSExpectAnySetterValue); - thisObject->m_JSExpectAnything.visit(visitor); visitor.append(thisObject->m_JSExpectAnythingSetterValue); - thisObject->m_JSExpectStringContaining.visit(visitor); visitor.append(thisObject->m_JSExpectStringContainingSetterValue); - thisObject->m_JSExpectStringMatching.visit(visitor); visitor.append(thisObject->m_JSExpectStringMatchingSetterValue); - thisObject->m_JSFFI.visit(visitor); visitor.append(thisObject->m_JSFFISetterValue); - thisObject->m_JSFSWatcher.visit(visitor); visitor.append(thisObject->m_JSFSWatcherSetterValue); - thisObject->m_JSFileSystemRouter.visit(visitor); visitor.append(thisObject->m_JSFileSystemRouterSetterValue); - thisObject->m_JSHTMLRewriter.visit(visitor); visitor.append(thisObject->m_JSHTMLRewriterSetterValue); - thisObject->m_JSHTTPSServer.visit(visitor); visitor.append(thisObject->m_JSHTTPSServerSetterValue); - thisObject->m_JSHTTPServer.visit(visitor); visitor.append(thisObject->m_JSHTTPServerSetterValue); - thisObject->m_JSListener.visit(visitor); visitor.append(thisObject->m_JSListenerSetterValue); - thisObject->m_JSMD4.visit(visitor); visitor.append(thisObject->m_JSMD4SetterValue); - thisObject->m_JSMD5.visit(visitor); visitor.append(thisObject->m_JSMD5SetterValue); - thisObject->m_JSMatchedRoute.visit(visitor); visitor.append(thisObject->m_JSMatchedRouteSetterValue); - thisObject->m_JSNodeJSFS.visit(visitor); visitor.append(thisObject->m_JSNodeJSFSSetterValue); - thisObject->m_JSRequest.visit(visitor); visitor.append(thisObject->m_JSRequestSetterValue); - thisObject->m_JSResolveMessage.visit(visitor); visitor.append(thisObject->m_JSResolveMessageSetterValue); - thisObject->m_JSResponse.visit(visitor); visitor.append(thisObject->m_JSResponseSetterValue); - thisObject->m_JSSHA1.visit(visitor); visitor.append(thisObject->m_JSSHA1SetterValue); - thisObject->m_JSSHA224.visit(visitor); visitor.append(thisObject->m_JSSHA224SetterValue); - thisObject->m_JSSHA256.visit(visitor); visitor.append(thisObject->m_JSSHA256SetterValue); - thisObject->m_JSSHA384.visit(visitor); visitor.append(thisObject->m_JSSHA384SetterValue); - thisObject->m_JSSHA512.visit(visitor); visitor.append(thisObject->m_JSSHA512SetterValue); - thisObject->m_JSSHA512_256.visit(visitor); visitor.append(thisObject->m_JSSHA512_256SetterValue); - thisObject->m_JSServerWebSocket.visit(visitor); visitor.append(thisObject->m_JSServerWebSocketSetterValue); - thisObject->m_JSStatWatcher.visit(visitor); visitor.append(thisObject->m_JSStatWatcherSetterValue); - thisObject->m_JSStats.visit(visitor); visitor.append(thisObject->m_JSStatsSetterValue); - thisObject->m_JSSubprocess.visit(visitor); visitor.append(thisObject->m_JSSubprocessSetterValue); - thisObject->m_JSTCPSocket.visit(visitor); visitor.append(thisObject->m_JSTCPSocketSetterValue); - thisObject->m_JSTLSSocket.visit(visitor); visitor.append(thisObject->m_JSTLSSocketSetterValue); - thisObject->m_JSTextChunk.visit(visitor); visitor.append(thisObject->m_JSTextChunkSetterValue); - thisObject->m_JSTextDecoder.visit(visitor); visitor.append(thisObject->m_JSTextDecoderSetterValue); - thisObject->m_JSTimeout.visit(visitor); visitor.append(thisObject->m_JSTimeoutSetterValue); - thisObject->m_JSTranspiler.visit(visitor); visitor.append(thisObject->m_JSTranspilerSetterValue); + thisObject->m_JSAttributeIterator.visit(visitor); + thisObject->m_JSBigIntStats.visit(visitor); + thisObject->m_JSBlob.visit(visitor); + thisObject->m_JSBuildArtifact.visit(visitor); + thisObject->m_JSBuildMessage.visit(visitor); + thisObject->m_JSComment.visit(visitor); + thisObject->m_JSCrypto.visit(visitor); + thisObject->m_JSCryptoHasher.visit(visitor); + thisObject->m_JSDebugHTTPSServer.visit(visitor); + thisObject->m_JSDebugHTTPServer.visit(visitor); + thisObject->m_JSDirent.visit(visitor); + thisObject->m_JSDocEnd.visit(visitor); + thisObject->m_JSDocType.visit(visitor); + thisObject->m_JSElement.visit(visitor); + thisObject->m_JSEndTag.visit(visitor); + thisObject->m_JSExpect.visit(visitor); + thisObject->m_JSExpectAny.visit(visitor); + thisObject->m_JSExpectAnything.visit(visitor); + thisObject->m_JSExpectStringContaining.visit(visitor); + thisObject->m_JSExpectStringMatching.visit(visitor); + thisObject->m_JSFFI.visit(visitor); + thisObject->m_JSFSWatcher.visit(visitor); + thisObject->m_JSFileSystemRouter.visit(visitor); + thisObject->m_JSHTMLRewriter.visit(visitor); + thisObject->m_JSHTTPSServer.visit(visitor); + thisObject->m_JSHTTPServer.visit(visitor); + thisObject->m_JSListener.visit(visitor); + thisObject->m_JSMD4.visit(visitor); + thisObject->m_JSMD5.visit(visitor); + thisObject->m_JSMatchedRoute.visit(visitor); + thisObject->m_JSNodeJSFS.visit(visitor); + thisObject->m_JSRequest.visit(visitor); + thisObject->m_JSResolveMessage.visit(visitor); + thisObject->m_JSResponse.visit(visitor); + thisObject->m_JSSHA1.visit(visitor); + thisObject->m_JSSHA224.visit(visitor); + thisObject->m_JSSHA256.visit(visitor); + thisObject->m_JSSHA384.visit(visitor); + thisObject->m_JSSHA512.visit(visitor); + thisObject->m_JSSHA512_256.visit(visitor); + thisObject->m_JSServerWebSocket.visit(visitor); + thisObject->m_JSStatWatcher.visit(visitor); + thisObject->m_JSStats.visit(visitor); + thisObject->m_JSSubprocess.visit(visitor); + thisObject->m_JSTCPSocket.visit(visitor); + thisObject->m_JSTLSSocket.visit(visitor); + thisObject->m_JSTextChunk.visit(visitor); + thisObject->m_JSTextDecoder.visit(visitor); + thisObject->m_JSTimeout.visit(visitor); + thisObject->m_JSTranspiler.visit(visitor); } \ No newline at end of file -- cgit v1.2.3 From c7de270bbba05e0fc850290b27d617bda1df2206 Mon Sep 17 00:00:00 2001 From: WingLim Date: Mon, 18 Sep 2023 17:59:09 +0800 Subject: feat(test): Implement `arrayContaining` (#5572) * feat(test): implement `arrayContaining` * feat: early return when expectedArray is empty * feat: add test for toEqual * chore: use `JSC::isArray` * chore: use getIndex for performance * fix: use deepEqual --------- Co-authored-by: Jarred Sumner --- .../ZigGeneratedClasses+DOMClientIsoSubspaces.h | 1 + .../bindings/ZigGeneratedClasses+DOMIsoSubspaces.h | 1 + .../ZigGeneratedClasses+lazyStructureHeader.h | 4 + .../ZigGeneratedClasses+lazyStructureImpl.h | 9 +- src/bun.js/bindings/ZigGeneratedClasses.cpp | 168 +++++++++++++++++++++ src/bun.js/bindings/ZigGeneratedClasses.h | 56 +++++++ src/bun.js/bindings/bindings.cpp | 43 +++++- src/bun.js/bindings/generated_classes.zig | 82 ++++++++++ src/bun.js/bindings/generated_classes_list.zig | 1 + src/bun.js/test/expect.zig | 42 +++++- src/bun.js/test/jest.classes.ts | 12 ++ test/js/bun/test/expect.test.js | 16 ++ 12 files changed, 432 insertions(+), 3 deletions(-) (limited to 'src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h') diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h index a364c0a48..cef5c512a 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h @@ -16,6 +16,7 @@ std::unique_ptr m_clientSubspaceForEndTag; std::unique_ptr m_clientSubspaceForExpect; std::unique_ptr m_clientSubspaceForExpectConstructor;std::unique_ptr m_clientSubspaceForExpectAny; std::unique_ptr m_clientSubspaceForExpectAnything; +std::unique_ptr m_clientSubspaceForExpectArrayContaining; std::unique_ptr m_clientSubspaceForExpectStringContaining; std::unique_ptr m_clientSubspaceForExpectStringMatching; std::unique_ptr m_clientSubspaceForFFI; diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h index e98ea16c3..d06451eda 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h +++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h @@ -16,6 +16,7 @@ std::unique_ptr m_subspaceForEndTag; std::unique_ptr m_subspaceForExpect; std::unique_ptr m_subspaceForExpectConstructor;std::unique_ptr m_subspaceForExpectAny; std::unique_ptr m_subspaceForExpectAnything; +std::unique_ptr m_subspaceForExpectArrayContaining; std::unique_ptr m_subspaceForExpectStringContaining; std::unique_ptr m_subspaceForExpectStringMatching; std::unique_ptr m_subspaceForFFI; diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h index 386e1ea80..381378262 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h +++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h @@ -70,6 +70,10 @@ JSC::Structure* JSExpectAnythingStructure() { return m_JSExpectAnything.getIniti JSC::JSObject* JSExpectAnythingConstructor() { return m_JSExpectAnything.constructorInitializedOnMainThread(this); } JSC::JSValue JSExpectAnythingPrototype() { return m_JSExpectAnything.prototypeInitializedOnMainThread(this); } JSC::LazyClassStructure m_JSExpectAnything; +JSC::Structure* JSExpectArrayContainingStructure() { return m_JSExpectArrayContaining.getInitializedOnMainThread(this); } + JSC::JSObject* JSExpectArrayContainingConstructor() { return m_JSExpectArrayContaining.constructorInitializedOnMainThread(this); } + JSC::JSValue JSExpectArrayContainingPrototype() { return m_JSExpectArrayContaining.prototypeInitializedOnMainThread(this); } + JSC::LazyClassStructure m_JSExpectArrayContaining; JSC::Structure* JSExpectStringContainingStructure() { return m_JSExpectStringContaining.getInitializedOnMainThread(this); } JSC::JSObject* JSExpectStringContainingConstructor() { return m_JSExpectStringContaining.constructorInitializedOnMainThread(this); } JSC::JSValue JSExpectStringContainingPrototype() { return m_JSExpectStringContaining.prototypeInitializedOnMainThread(this); } diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h index b51752180..84d3df7a0 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h +++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h @@ -1,4 +1,4 @@ -void GlobalObject::initGeneratedLazyClasses() { +ALWAYS_INLINE void GlobalObject::initGeneratedLazyClasses() { m_JSAttributeIterator.initLater( [](LazyClassStructure::Initializer& init) { init.setPrototype(WebCore::JSAttributeIterator::createPrototype(init.vm, reinterpret_cast(init.global))); @@ -106,6 +106,12 @@ void GlobalObject::initGeneratedLazyClasses() { init.setPrototype(WebCore::JSExpectAnything::createPrototype(init.vm, reinterpret_cast(init.global))); init.setStructure(WebCore::JSExpectAnything::createStructure(init.vm, init.global, init.prototype)); + }); + m_JSExpectArrayContaining.initLater( + [](LazyClassStructure::Initializer& init) { + init.setPrototype(WebCore::JSExpectArrayContaining::createPrototype(init.vm, reinterpret_cast(init.global))); + init.setStructure(WebCore::JSExpectArrayContaining::createStructure(init.vm, init.global, init.prototype)); + }); m_JSExpectStringContaining.initLater( [](LazyClassStructure::Initializer& init) { @@ -321,6 +327,7 @@ void GlobalObject::visitGeneratedLazyClasses(GlobalObject *thisObject, Visitor& thisObject->m_JSExpect.visit(visitor); thisObject->m_JSExpectAny.visit(visitor); thisObject->m_JSExpectAnything.visit(visitor); + thisObject->m_JSExpectArrayContaining.visit(visitor); thisObject->m_JSExpectStringContaining.visit(visitor); thisObject->m_JSExpectStringMatching.visit(visitor); thisObject->m_JSFFI.visit(visitor); diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp index ec2add296..b84c1cd16 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses.cpp +++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp @@ -9712,6 +9712,174 @@ extern "C" EncodedJSValue ExpectAnything__create(Zig::GlobalObject* globalObject return JSValue::encode(instance); } +class JSExpectArrayContainingPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + + static JSExpectArrayContainingPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSExpectArrayContainingPrototype* ptr = new (NotNull, JSC::allocateCell(vm)) JSExpectArrayContainingPrototype(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: + JSExpectArrayContainingPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject*); +}; + +extern "C" void ExpectArrayContainingClass__finalize(void*); +extern "C" JSC_DECLARE_HOST_FUNCTION(ExpectArrayContainingClass__call); + +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSExpectArrayContainingPrototype, JSExpectArrayContainingPrototype::Base); + +static const HashTableValue JSExpectArrayContainingPrototypeTableValues[] = {}; + +const ClassInfo JSExpectArrayContainingPrototype::s_info = { "ExpectArrayContaining"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectArrayContainingPrototype) }; + +extern "C" void ExpectArrayContainingPrototype__arrayValueSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value) +{ + auto& vm = globalObject->vm(); + auto* thisObject = jsCast(JSValue::decode(thisValue)); + thisObject->m_arrayValue.set(vm, thisObject, JSValue::decode(value)); +} + +extern "C" EncodedJSValue ExpectArrayContainingPrototype__arrayValueGetCachedValue(JSC::EncodedJSValue thisValue) +{ + auto* thisObject = jsCast(JSValue::decode(thisValue)); + return JSValue::encode(thisObject->m_arrayValue.get()); +} + +void JSExpectArrayContainingPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +JSExpectArrayContaining::~JSExpectArrayContaining() +{ + if (m_ctx) { + ExpectArrayContainingClass__finalize(m_ctx); + } +} +void JSExpectArrayContaining::destroy(JSCell* cell) +{ + static_cast(cell)->JSExpectArrayContaining::~JSExpectArrayContaining(); +} + +const ClassInfo JSExpectArrayContaining::s_info = { "ExpectArrayContaining"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectArrayContaining) }; + +void JSExpectArrayContaining::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +JSExpectArrayContaining* JSExpectArrayContaining::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx) +{ + JSExpectArrayContaining* ptr = new (NotNull, JSC::allocateCell(vm)) JSExpectArrayContaining(vm, structure, ctx); + ptr->finishCreation(vm); + return ptr; +} + +extern "C" void* ExpectArrayContaining__fromJS(JSC::EncodedJSValue value) +{ + JSC::JSValue decodedValue = JSC::JSValue::decode(value); + if (decodedValue.isEmpty() || !decodedValue.isCell()) + return nullptr; + + JSC::JSCell* cell = decodedValue.asCell(); + JSExpectArrayContaining* object = JSC::jsDynamicCast(cell); + + if (!object) + return nullptr; + + return object->wrapped(); +} + +extern "C" bool ExpectArrayContaining__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr) +{ + JSExpectArrayContaining* object = JSC::jsDynamicCast(JSValue::decode(value)); + if (!object) + return false; + + object->m_ctx = ptr; + return true; +} + +extern "C" const size_t ExpectArrayContaining__ptrOffset = JSExpectArrayContaining::offsetOfWrapped(); + +void JSExpectArrayContaining::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); +} + +JSObject* JSExpectArrayContaining::createPrototype(VM& vm, JSDOMGlobalObject* globalObject) +{ + return JSExpectArrayContainingPrototype::create(vm, globalObject, JSExpectArrayContainingPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); +} + +extern "C" EncodedJSValue ExpectArrayContaining__create(Zig::GlobalObject* globalObject, void* ptr) +{ + auto& vm = globalObject->vm(); + JSC::Structure* structure = globalObject->JSExpectArrayContainingStructure(); + JSExpectArrayContaining* instance = JSExpectArrayContaining::create(vm, globalObject, structure, ptr); + + return JSValue::encode(instance); +} + +template +void JSExpectArrayContaining::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + JSExpectArrayContaining* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + thisObject->visitAdditionalChildren(visitor); +} + +DEFINE_VISIT_CHILDREN(JSExpectArrayContaining); + +template +void JSExpectArrayContaining::visitAdditionalChildren(Visitor& visitor) +{ + JSExpectArrayContaining* thisObject = this; + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + visitor.append(thisObject->m_arrayValue); +} + +DEFINE_VISIT_ADDITIONAL_CHILDREN(JSExpectArrayContaining); + +template +void JSExpectArrayContaining::visitOutputConstraintsImpl(JSCell* cell, Visitor& visitor) +{ + JSExpectArrayContaining* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + thisObject->visitAdditionalChildren(visitor); +} + +DEFINE_VISIT_OUTPUT_CONSTRAINTS(JSExpectArrayContaining); class JSExpectStringContainingPrototype final : public JSC::JSNonFinalObject { public: using Base = JSC::JSNonFinalObject; diff --git a/src/bun.js/bindings/ZigGeneratedClasses.h b/src/bun.js/bindings/ZigGeneratedClasses.h index 142a96746..33aa58487 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses.h +++ b/src/bun.js/bindings/ZigGeneratedClasses.h @@ -1004,6 +1004,62 @@ public: void finishCreation(JSC::VM&); }; +class JSExpectArrayContaining final : public JSC::JSDestructibleObject { +public: + using Base = JSC::JSDestructibleObject; + static JSExpectArrayContaining* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx); + + DECLARE_EXPORT_INFO; + 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_clientSubspaceForExpectArrayContaining.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForExpectArrayContaining = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForExpectArrayContaining.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForExpectArrayContaining = std::forward(space); }); + } + + static void destroy(JSC::JSCell*); + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast(0b11101110), StructureFlags), info()); + } + + static JSObject* createPrototype(VM& vm, JSDOMGlobalObject* globalObject); + ; + + ~JSExpectArrayContaining(); + + void* wrapped() const { return m_ctx; } + + void detach() + { + m_ctx = nullptr; + } + + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSExpectArrayContaining, m_ctx); } + + void* m_ctx { nullptr }; + + JSExpectArrayContaining(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) + : Base(vm, structure) + { + m_ctx = sinkPtr; + } + + void finishCreation(JSC::VM&); + + DECLARE_VISIT_CHILDREN; + template void visitAdditionalChildren(Visitor&); + DECLARE_VISIT_OUTPUT_CONSTRAINTS; + + mutable JSC::WriteBarrier m_arrayValue; +}; + class JSExpectStringContaining final : public JSC::JSDestructibleObject { public: using Base = JSC::JSDestructibleObject; diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 173a454a1..47ef36aea 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -229,6 +229,47 @@ AsymmetricMatcherResult matchAsymmetricMatcher(JSGlobalObject* globalObject, JSC } } + return AsymmetricMatcherResult::FAIL; + } else if (auto* expectArrayContaining = jsDynamicCast(matcherPropCell)) { + JSValue expectedArrayValue = expectArrayContaining->m_arrayValue.get(); + + if (JSC::isArray(globalObject, otherProp)) { + if (JSC::isArray(globalObject, expectedArrayValue)) { + JSArray* expectedArray = jsDynamicCast(expectedArrayValue); + JSArray* otherArray = jsDynamicCast(otherProp); + + unsigned expectedLength = expectedArray->length(); + unsigned otherLength = otherArray->length(); + + // A empty array is all array's subset + if (expectedLength == 0) { + return AsymmetricMatcherResult::PASS; + } + + // O(m*n) but works for now + for (unsigned m = 0; m < expectedLength; m++) { + JSValue expectedValue = expectedArray->getIndex(globalObject, m); + bool found = false; + + for (unsigned n = 0; n < otherLength; n++) { + JSValue otherValue = otherArray->getIndex(globalObject, n); + ThrowScope scope = DECLARE_THROW_SCOPE(globalObject->vm()); + Vector, 16> stack; + if (Bun__deepEquals(globalObject, expectedValue, otherValue, stack, &scope, true)) { + found = true; + break; + } + } + + if (!found) { + return AsymmetricMatcherResult::FAIL; + } + } + + return AsymmetricMatcherResult::PASS; + } + } + return AsymmetricMatcherResult::FAIL; } @@ -4702,4 +4743,4 @@ CPP_DECL void JSC__VM__setControlFlowProfiler(JSC__VM* vm, bool isEnabled) } else { vm->disableControlFlowProfiler(); } -} \ No newline at end of file +} diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig index 329506718..581d4a5f3 100644 --- a/src/bun.js/bindings/generated_classes.zig +++ b/src/bun.js/bindings/generated_classes.zig @@ -2507,6 +2507,87 @@ pub const JSExpectAnything = struct { } } }; +pub const JSExpectArrayContaining = struct { + const ExpectArrayContaining = Classes.ExpectArrayContaining; + const GetterType = fn (*ExpectArrayContaining, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; + const GetterTypeWithThisValue = fn (*ExpectArrayContaining, JSC.JSValue, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; + const SetterType = fn (*ExpectArrayContaining, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool; + const SetterTypeWithThisValue = fn (*ExpectArrayContaining, JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool; + const CallbackType = fn (*ExpectArrayContaining, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue; + + /// Return the pointer to the wrapped object. + /// If the object does not match the type, return null. + pub fn fromJS(value: JSC.JSValue) ?*ExpectArrayContaining { + JSC.markBinding(@src()); + return ExpectArrayContaining__fromJS(value); + } + + extern fn ExpectArrayContainingPrototype__arrayValueSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void; + + extern fn ExpectArrayContainingPrototype__arrayValueGetCachedValue(JSC.JSValue) JSC.JSValue; + + /// `ExpectArrayContaining.arrayValue` setter + /// This value will be visited by the garbage collector. + pub fn arrayValueSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { + JSC.markBinding(@src()); + ExpectArrayContainingPrototype__arrayValueSetCachedValue(thisValue, globalObject, value); + } + + /// `ExpectArrayContaining.arrayValue` getter + /// This value will be visited by the garbage collector. + pub fn arrayValueGetCached(thisValue: JSC.JSValue) ?JSC.JSValue { + JSC.markBinding(@src()); + const result = ExpectArrayContainingPrototype__arrayValueGetCachedValue(thisValue); + if (result == .zero) + return null; + + return result; + } + + /// Create a new instance of ExpectArrayContaining + pub fn toJS(this: *ExpectArrayContaining, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + JSC.markBinding(@src()); + if (comptime Environment.allow_assert) { + const value__ = ExpectArrayContaining__create(globalObject, this); + std.debug.assert(value__.as(ExpectArrayContaining).? == this); // If this fails, likely a C ABI issue. + return value__; + } else { + return ExpectArrayContaining__create(globalObject, this); + } + } + + /// Modify the internal ptr to point to a new instance of ExpectArrayContaining. + pub fn dangerouslySetPtr(value: JSC.JSValue, ptr: ?*ExpectArrayContaining) bool { + JSC.markBinding(@src()); + return ExpectArrayContaining__dangerouslySetPtr(value, ptr); + } + + /// Detach the ptr from the thisValue + pub fn detachPtr(_: *ExpectArrayContaining, value: JSC.JSValue) void { + JSC.markBinding(@src()); + std.debug.assert(ExpectArrayContaining__dangerouslySetPtr(value, null)); + } + + extern fn ExpectArrayContaining__fromJS(JSC.JSValue) ?*ExpectArrayContaining; + extern fn ExpectArrayContaining__getConstructor(*JSC.JSGlobalObject) JSC.JSValue; + + extern fn ExpectArrayContaining__create(globalObject: *JSC.JSGlobalObject, ptr: ?*ExpectArrayContaining) JSC.JSValue; + + extern fn ExpectArrayContaining__dangerouslySetPtr(JSC.JSValue, ?*ExpectArrayContaining) bool; + + comptime { + if (@TypeOf(ExpectArrayContaining.finalize) != (fn (*ExpectArrayContaining) callconv(.C) void)) { + @compileLog("ExpectArrayContaining.finalize is not a finalizer"); + } + + if (@TypeOf(ExpectArrayContaining.call) != StaticCallbackType) + @compileLog("Expected ExpectArrayContaining.call to be a static callback"); + if (!JSC.is_bindgen) { + @export(ExpectArrayContaining.call, .{ .name = "ExpectArrayContainingClass__call" }); + @export(ExpectArrayContaining.finalize, .{ .name = "ExpectArrayContainingClass__finalize" }); + } + } +}; pub const JSExpectStringContaining = struct { const ExpectStringContaining = Classes.ExpectStringContaining; const GetterType = fn (*ExpectStringContaining, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; @@ -7006,6 +7087,7 @@ comptime { _ = JSExpect; _ = JSExpectAny; _ = JSExpectAnything; + _ = JSExpectArrayContaining; _ = JSExpectStringContaining; _ = JSExpectStringMatching; _ = JSFFI; diff --git a/src/bun.js/bindings/generated_classes_list.zig b/src/bun.js/bindings/generated_classes_list.zig index a86f74dd3..3b45f33b8 100644 --- a/src/bun.js/bindings/generated_classes_list.zig +++ b/src/bun.js/bindings/generated_classes_list.zig @@ -17,6 +17,7 @@ pub const Classes = struct { pub const ExpectAnything = JSC.Expect.ExpectAnything; pub const ExpectStringContaining = JSC.Expect.ExpectStringContaining; pub const ExpectStringMatching = JSC.Expect.ExpectStringMatching; + pub const ExpectArrayContaining = JSC.Expect.ExpectArrayContaining; pub const FileSystemRouter = JSC.API.FileSystemRouter; pub const Bundler = JSC.API.JSBundler; pub const JSBundler = Bundler; diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig index 0d3ff663e..6434e3702 100644 --- a/src/bun.js/test/expect.zig +++ b/src/bun.js/test/expect.zig @@ -3437,8 +3437,11 @@ pub const Expect = struct { return ExpectStringMatching.call(globalObject, callFrame); } + pub fn arrayContaining(globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + return ExpectArrayContaining.call(globalObject, callFrame); + } + pub const extend = notImplementedStaticFn; - pub const arrayContaining = notImplementedStaticFn; pub const assertions = notImplementedStaticFn; pub const hasAssertions = notImplementedStaticFn; pub const objectContaining = notImplementedStaticFn; @@ -3621,6 +3624,43 @@ pub const ExpectAny = struct { } }; +pub const ExpectArrayContaining = struct { + pub usingnamespace JSC.Codegen.JSExpectArrayContaining; + + pub fn finalize( + this: *ExpectArrayContaining, + ) callconv(.C) void { + VirtualMachine.get().allocator.destroy(this); + } + + pub fn call(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + const args = callFrame.arguments(1).slice(); + + if (args.len == 0 or !args[0].jsType().isArray()) { + const fmt = "expect.arrayContaining(array)\n\nExpected a array\n"; + globalObject.throwPretty(fmt, .{}); + return .zero; + } + + const array_value = args[0]; + const array_containing = globalObject.bunVM().allocator.create(ExpectArrayContaining) catch unreachable; + + if (Jest.runner.?.pending_test == null) { + const err = globalObject.createErrorInstance("expect.arrayContaining() must be called in a test", .{}); + err.put(globalObject, ZigString.static("name"), ZigString.init("TestNotRunningError").toValueGC(globalObject)); + globalObject.throwValue(err); + return .zero; + } + + const array_containing_js_value = array_containing.toJS(globalObject); + ExpectArrayContaining.arrayValueSetCached(array_containing_js_value, globalObject, array_value); + + var vm = globalObject.bunVM(); + vm.autoGarbageCollect(); + return array_containing_js_value; + } +}; + /// JSValue.zero is used to indicate it was not a JSMockFunction /// If there were no calls, it returns an empty JSArray* extern fn JSMockFunction__getCalls(JSValue) JSValue; diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts index d40acbf07..186c8f9a8 100644 --- a/src/bun.js/test/jest.classes.ts +++ b/src/bun.js/test/jest.classes.ts @@ -48,6 +48,18 @@ export default [ klass: {}, proto: {}, }), + define({ + name: "ExpectArrayContaining", + construct: false, + noConstructor: true, + call: true, + finalize: true, + JSType: "0b11101110", + values: ["arrayValue"], + configurable: false, + klass: {}, + proto: {}, + }), define({ name: "Expect", construct: true, diff --git a/test/js/bun/test/expect.test.js b/test/js/bun/test/expect.test.js index 804a5339f..4b9e5ec22 100644 --- a/test/js/bun/test/expect.test.js +++ b/test/js/bun/test/expect.test.js @@ -1298,6 +1298,16 @@ describe("expect()", () => { a = { a: 1, b: 2, c: 3 }; b = { a: 1, b: 2 }; expect(a).not.toEqual(b); + + array1 = [1, 2, 3]; + expect(array1).toEqual(expect.arrayContaining([])); + expect(array1).toEqual(expect.arrayContaining([1, 2])); + expect(array1).not.toEqual(expect.arrayContaining([1, 2, 4])); + + array2 = [{ a: 1, b: 2 }, { a: { a: 1 } }]; + expect(array2).toEqual(expect.arrayContaining([{ a: 1, b: 2 }])); + expect(array2).toEqual(expect.arrayContaining([{ a: { a: 1 } }])); + expect(array2).not.toEqual(expect.arrayContaining([{ a: 2, b: 3 }])); }); test("symbol based keys in arrays are processed correctly", () => { @@ -2624,6 +2634,12 @@ describe("expect()", () => { expect({ a: [1, 2, 3] }).toMatchObject({ a: [1, 2, 3] }); expect({ a: [1, 2, 4] }).not.toMatchObject({ a: [1, 2, 3] }); + expect({ a: [1, 2, 3] }).toMatchObject({ a: expect.arrayContaining([1, 2]) }); + expect({ a: [1, 2, 3] }).not.toMatchObject({ a: expect.arrayContaining([4]) }); + expect({ a: ['hello', 'world'] }).toMatchObject({ a: expect.arrayContaining([]) }); + expect({ a: ['hello', 'world'] }).toMatchObject({ a: expect.arrayContaining(['world']) }); + expect({ a: ['hello', 'world'] }).not.toMatchObject({ a: expect.arrayContaining(['hello', 'mars']) }); + expect([]).toMatchObject([]); expect([]).toMatchObject({}); expect({}).not.toMatchObject([]); -- cgit v1.2.3