From fd571a14e05bfe44e9b88d37329bc3db8f200afb Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 9 Aug 2025 14:59:03 +0900 Subject: [PATCH 1/3] Add memory stress tests for JSObject and JSClosure Tests memory exhaustion, heap fragmentation, and boundary conditions without FinalizationRegistry to validate reference counting under extreme allocation pressure. --- Tests/JavaScriptKitTests/StressTests.swift | 259 +++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 Tests/JavaScriptKitTests/StressTests.swift diff --git a/Tests/JavaScriptKitTests/StressTests.swift b/Tests/JavaScriptKitTests/StressTests.swift new file mode 100644 index 00000000..1a428789 --- /dev/null +++ b/Tests/JavaScriptKitTests/StressTests.swift @@ -0,0 +1,259 @@ +import JavaScriptKit +import XCTest + +final class StressTests: XCTestCase { + + func testJSObjectMemoryExhaustion() async throws { + guard let gc = JSObject.global.gc.function else { + throw XCTSkip("Missing --expose-gc flag") + } + + // Push JSObject allocation to stress memory management + // This tests reference counting and cleanup under heavy load + let maxIterations = 25_000 + var objects: [JSObject] = [] + var lastSuccessfulCount = 0 + + do { + for i in 0.. 5) // Should be 5 + capturedData.count (100+) + } + + #if JAVASCRIPTKIT_WITHOUT_WEAKREFS + for closure in closures { + closure.release() + } + #endif + + closures.removeAll() + for _ in 0..<20 { + gc() + try await Task.sleep(for: .milliseconds(10)) + } + } + + func testMixedAllocationMemoryBoundaries() async throws { + guard let gc = JSObject.global.gc.function else { + throw XCTSkip("Missing --expose-gc flag") + } + + // Test system behavior at memory boundaries with mixed object types + let cycles = 200 + var totalObjects = 0 + var totalClosures = 0 + + for cycle in 0.. Date: Sat, 9 Aug 2025 15:32:32 +0900 Subject: [PATCH 2/3] Fix code formatting for StressTests.swift --- Tests/JavaScriptKitTests/StressTests.swift | 104 +++++++++++---------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/Tests/JavaScriptKitTests/StressTests.swift b/Tests/JavaScriptKitTests/StressTests.swift index 1a428789..ef986a11 100644 --- a/Tests/JavaScriptKitTests/StressTests.swift +++ b/Tests/JavaScriptKitTests/StressTests.swift @@ -2,7 +2,7 @@ import JavaScriptKit import XCTest final class StressTests: XCTestCase { - + func testJSObjectMemoryExhaustion() async throws { guard let gc = JSObject.global.gc.function else { throw XCTSkip("Missing --expose-gc flag") @@ -13,22 +13,22 @@ final class StressTests: XCTestCase { let maxIterations = 25_000 var objects: [JSObject] = [] var lastSuccessfulCount = 0 - + do { for i in 0.. 5) // Should be 5 + capturedData.count (100+) + XCTAssertTrue(result.number! > 5) // Should be 5 + capturedData.count (100+) } - + #if JAVASCRIPTKIT_WITHOUT_WEAKREFS for closure in closures { closure.release() } #endif - + closures.removeAll() for _ in 0..<20 { gc() try await Task.sleep(for: .milliseconds(10)) } } - + func testMixedAllocationMemoryBoundaries() async throws { guard let gc = JSObject.global.gc.function else { throw XCTSkip("Missing --expose-gc flag") @@ -122,33 +122,35 @@ final class StressTests: XCTestCase { let cycles = 200 var totalObjects = 0 var totalClosures = 0 - + for cycle in 0.. Date: Sat, 9 Aug 2025 15:48:20 +0900 Subject: [PATCH 3/3] Fix JSClosure calling syntax in stress tests --- Tests/JavaScriptKitTests/StressTests.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/JavaScriptKitTests/StressTests.swift b/Tests/JavaScriptKitTests/StressTests.swift index ef986a11..73b302fd 100644 --- a/Tests/JavaScriptKitTests/StressTests.swift +++ b/Tests/JavaScriptKitTests/StressTests.swift @@ -80,8 +80,8 @@ final class StressTests: XCTestCase { successCount = i + 1 // Test closure immediately to ensure it works under memory pressure - let result = closure([JSValue.number(10)]) - XCTAssertEqual(result.number, 110.0) // 100 (capturedData.count) + 10 + let result = closure() + XCTAssertEqual(result.number, 100.0) // capturedData.count // More frequent GC to stress the system if i % 500 == 0 { @@ -96,8 +96,8 @@ final class StressTests: XCTestCase { // Test random closures still work after extreme memory pressure for _ in 0.. 5) // Should be 5 + capturedData.count (100+) + let result = closures[randomIndex]() + XCTAssertEqual(result.number, 100.0) // capturedData.count } #if JAVASCRIPTKIT_WITHOUT_WEAKREFS @@ -152,7 +152,7 @@ final class StressTests: XCTestCase { } // Allocate closures with increasing complexity - for i in 0..