From 2aa6cf16f10b9ed902685aeb2de3dd8d31f3ded8 Mon Sep 17 00:00:00 2001 From: Mike Aizatsky Date: Tue, 1 Sep 2026 10:02:43 -0700 Subject: [PATCH 1/2] Avoid temporary strings in actor SQLite tests --- src/workerd/io/actor-sqlite-test.c++ | 185 +++++++++++++-------------- 1 file changed, 90 insertions(+), 95 deletions(-) diff --git a/src/workerd/io/actor-sqlite-test.c++ b/src/workerd/io/actor-sqlite-test.c++ index fd466211ada..db3f02c9021 100644 --- a/src/workerd/io/actor-sqlite-test.c++ +++ b/src/workerd/io/actor-sqlite-test.c++ @@ -206,7 +206,7 @@ KJ_TEST("check put multiple wraps operations in a transaction") { ActorSqliteTest test; kj::Vector putKVs; - putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray(kj::str("bar").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray("bar"_kj.asBytes())}); // NoTxn test { @@ -216,18 +216,18 @@ KJ_TEST("check put multiple wraps operations in a transaction") { // During write, all NoTxn operations are wrapped in an ImplicitTxn. auto commitFulfiller = kj::mv(test.pollAndExpectCalls({"commit"})[0]); commitFulfiller->fulfill(); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } // ExplicitTxn test { - putKVs.add(ActorCache::KeyValuePair{kj::str("foo2"), kj::heapArray(kj::str("bar2").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo2"), kj::heapArray("bar2"_kj.asBytes())}); KJ_ASSERT(!test.actor.isCommitScheduled()); // Similar to the previous putMultiple, but wrapped in a transactionSync (ExplicitTxn) test.putMultipleExplicitTxn(putKVs.releaseAsArray()); auto commitFulfiller = kj::mv(test.pollAndExpectCalls({"commit"})[0]); commitFulfiller->fulfill(); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo2"))) == kj::str("bar2").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo2"))) == "bar2"_kj.asBytes()); } // ImplicitTxn test @@ -238,12 +238,12 @@ KJ_TEST("check put multiple wraps operations in a transaction") { // By now, we should check there's a commit scheduled in a ImplicitTxn. KJ_ASSERT(test.actor.isCommitScheduled()); - putKVs.add(ActorCache::KeyValuePair{kj::str("foo3"), kj::heapArray(kj::str("bar3").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo3"), kj::heapArray("bar3"_kj.asBytes())}); test.putMultiple(putKVs.releaseAsArray()); auto commitFulfiller = kj::mv(test.pollAndExpectCalls({"commit"})[0]); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == kj::str("bat").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo3"))) == kj::str("bar3").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == "bat"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo3"))) == "bar3"_kj.asBytes()); commitFulfiller->fulfill(); } } @@ -252,7 +252,7 @@ KJ_TEST("check put multiple wraps operations in a transaction") { ActorSqliteTest test; kj::Vector putKVs; - putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray(kj::str("bar").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray("bar"_kj.asBytes())}); // NoTxn test { @@ -262,18 +262,18 @@ KJ_TEST("check put multiple wraps operations in a transaction") { // During write, all NoTxn operations are wrapped in an ImplicitTxn. auto commitFulfiller = kj::mv(test.pollAndExpectCalls({"commit"})[0]); commitFulfiller->fulfill(); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } // ExplicitTxn test { - putKVs.add(ActorCache::KeyValuePair{kj::str("foo2"), kj::heapArray(kj::str("bar2").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo2"), kj::heapArray("bar2"_kj.asBytes())}); KJ_ASSERT(!test.actor.isCommitScheduled()); // Similar to the previous putMultiple, but wrapped in a transactionSync (ExplicitTxn) test.putMultipleExplicitTxn(putKVs.releaseAsArray()); auto commitFulfiller = kj::mv(test.pollAndExpectCalls({"commit"})[0]); commitFulfiller->fulfill(); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo2"))) == kj::str("bar2").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo2"))) == "bar2"_kj.asBytes()); } // ImplicitTxn test @@ -284,12 +284,12 @@ KJ_TEST("check put multiple wraps operations in a transaction") { // By now, we should check there's a commit scheduled in a ImplicitTxn. KJ_ASSERT(test.actor.isCommitScheduled()); - putKVs.add(ActorCache::KeyValuePair{kj::str("foo3"), kj::heapArray(kj::str("bar3").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo3"), kj::heapArray("bar3"_kj.asBytes())}); test.putMultiple(putKVs.releaseAsArray()); auto commitFulfiller = kj::mv(test.pollAndExpectCalls({"commit"})[0]); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == kj::str("bat").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo3"))) == kj::str("bar3").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == "bat"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo3"))) == "bar3"_kj.asBytes()); commitFulfiller->fulfill(); } } @@ -302,16 +302,15 @@ KJ_TEST("check put multiple wraps operations in a transaction and rollback on er kj::Vector putKVs; // Add some regular key-value pairs that we know are supported - putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray(kj::str("bar").asBytes())}); - putKVs.add(ActorCache::KeyValuePair{kj::str("foo2"), kj::heapArray(kj::str("bar2").asBytes())}); - putKVs.add(ActorCache::KeyValuePair{kj::str("foo3"), kj::heapArray(kj::str("bar3").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray("bar"_kj.asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo2"), kj::heapArray("bar2"_kj.asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo3"), kj::heapArray("bar3"_kj.asBytes())}); // Now create a key that's too large. Should fail with string or blob too big: SQLITE_TOOBIG auto tooLongKey = kj::heapString(4 * 1024 * 1024 + 1); tooLongKey.asArray().fill('a'); // Add it to our KV array - putKVs.add( - ActorCache::KeyValuePair{kj::str(tooLongKey), kj::heapArray(kj::str("bar").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str(tooLongKey), kj::heapArray("bar"_kj.asBytes())}); // NoTxn test { @@ -362,7 +361,7 @@ KJ_TEST("check put multiple wraps operations in a transaction and rollback on er auto commitFulfiller = kj::mv(test.pollAndExpectCalls({"commit"})[0]); // The single put succeeded, but the putMultiple did not. - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == kj::str("bat").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == "bat"_kj.asBytes()); KJ_ASSERT(expectSync(test.get(kj::str("foo"))) == nullptr); KJ_ASSERT(expectSync(test.get(kj::str("foo2"))) == nullptr); KJ_ASSERT(expectSync(test.get(kj::str("foo3"))) == nullptr); @@ -379,7 +378,7 @@ KJ_TEST("alarm write happens transactionally with storage ops") { test.pollAndExpectCalls({"commit"})[0]->fulfill(); KJ_ASSERT(expectSync(test.getAlarm()) == oneMs); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } KJ_TEST("storage op without alarm change does not wait on scheduler") { @@ -388,7 +387,7 @@ KJ_TEST("storage op without alarm change does not wait on scheduler") { test.put("foo", "bar"); test.pollAndExpectCalls({"commit"})[0]->fulfill(); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); KJ_ASSERT(expectSync(test.getAlarm()) == kj::none); } @@ -1917,7 +1916,7 @@ KJ_TEST("allowUnconfirmed put does not block output gate") { KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify data was written - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } KJ_TEST("confirmed put blocks output gate") { @@ -1939,7 +1938,7 @@ KJ_TEST("confirmed put blocks output gate") { KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify data was written - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } KJ_TEST("mixed confirmed and unconfirmed writes in same transaction use output gate") { @@ -1962,8 +1961,8 @@ KJ_TEST("mixed confirmed and unconfirmed writes in same transaction use output g KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Both writes should be committed - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == kj::str("quux").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == "quux"_kj.asBytes()); } KJ_TEST("allowUnconfirmed delete does not block output gate") { @@ -2000,9 +1999,9 @@ KJ_TEST("allowUnconfirmed putMultiple does not block output gate") { // Create multiple key-value pairs for the test kj::Vector putKVs; - putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray(kj::str("bar").asBytes())}); - putKVs.add(ActorCache::KeyValuePair{kj::str("baz"), kj::heapArray(kj::str("qux").asBytes())}); - putKVs.add(ActorCache::KeyValuePair{kj::str("key3"), kj::heapArray(kj::str("value3").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray("bar"_kj.asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("baz"), kj::heapArray("qux"_kj.asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("key3"), kj::heapArray("value3"_kj.asBytes())}); // Perform an unconfirmed putMultiple within the implicit transaction test.putMultiple(putKVs.releaseAsArray(), {.allowUnconfirmed = true}); @@ -2017,9 +2016,9 @@ KJ_TEST("allowUnconfirmed putMultiple does not block output gate") { KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify all data was written correctly - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == kj::str("qux").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("key3"))) == kj::str("value3").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == "qux"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("key3"))) == "value3"_kj.asBytes()); } KJ_TEST("allowUnconfirmed deleteMultiple does not block output gate") { @@ -2027,9 +2026,9 @@ KJ_TEST("allowUnconfirmed deleteMultiple does not block output gate") { // First set up some data kj::Vector putKVs; - putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray(kj::str("bar").asBytes())}); - putKVs.add(ActorCache::KeyValuePair{kj::str("baz"), kj::heapArray(kj::str("qux").asBytes())}); - putKVs.add(ActorCache::KeyValuePair{kj::str("key3"), kj::heapArray(kj::str("value3").asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("foo"), kj::heapArray("bar"_kj.asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("baz"), kj::heapArray("qux"_kj.asBytes())}); + putKVs.add(ActorCache::KeyValuePair{kj::str("key3"), kj::heapArray("value3"_kj.asBytes())}); test.putMultiple(putKVs.releaseAsArray()); test.pollAndExpectCalls({"commit"})[0]->fulfill(); @@ -2133,7 +2132,7 @@ KJ_TEST("An unconfirmed put followed by a direct SQL queries requires the output KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Make sure that the write actually succeeded. - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); { auto query = db.run("SELECT * FROM myTable"); KJ_ASSERT(!query.isDone()); @@ -2176,7 +2175,7 @@ KJ_TEST("sync() waits for confirmed writes to complete") { syncPromise.wait(test.ws); // Verify data was written - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } KJ_TEST("sync() waits for unconfirmed writes to complete") { @@ -2202,7 +2201,7 @@ KJ_TEST("sync() waits for unconfirmed writes to complete") { syncPromise.wait(test.ws); // Verify data was written - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } KJ_TEST("sync() waits for multiple unconfirmed writes in a row") { @@ -2229,9 +2228,9 @@ KJ_TEST("sync() waits for multiple unconfirmed writes in a row") { syncPromise.wait(test.ws); // Verify all writes were committed - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == kj::str("qux").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("key3"))) == kj::str("value3").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == "qux"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("key3"))) == "value3"_kj.asBytes()); } KJ_TEST("sync() only waits for writes before it was called") { @@ -2323,8 +2322,8 @@ KJ_TEST("sync() with mixed confirmed and unconfirmed writes") { syncPromise.wait(test.ws); // Both writes should be committed - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == kj::str("qux").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == "qux"_kj.asBytes()); } KJ_TEST("multiple sync() calls for same commit") { @@ -2432,7 +2431,7 @@ KJ_TEST("allowUnconfirmed setAlarm then confirmed put uses output gate") { // Both operations should be committed KJ_ASSERT(expectSync(test.getAlarm()) == oneMs); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } KJ_TEST("allowUnconfirmed setAlarm with storage ops") { @@ -2457,7 +2456,7 @@ KJ_TEST("allowUnconfirmed setAlarm with storage ops") { // Verify both alarm and storage writes committed KJ_ASSERT(expectSync(test.getAlarm()) == oneMs); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } KJ_TEST("allowUnconfirmed setAlarm updating existing alarm") { @@ -2583,7 +2582,7 @@ KJ_TEST("sync() throws after critical error in explicit transaction") { auto txn = test.startTransaction(); // Do a write within the transaction - txn->put(kj::str("foo"), kj::heapArray(kj::str("bar").asBytes()), {}, nullptr); + txn->put(kj::str("foo"), kj::heapArray("bar"_kj.asBytes()), {}, nullptr); // Trigger a critical error using SQLITE_NOMEM by setting a very low heap limit // and then trying to insert a large value. @@ -2627,8 +2626,7 @@ KJ_TEST("allowUnconfirmed put in explicit transaction does not block output gate auto txn = test.startTransaction(); // Do an unconfirmed put within the transaction - txn->put( - kj::str("foo"), kj::heapArray(kj::str("bar").asBytes()), {.allowUnconfirmed = true}, nullptr); + txn->put(kj::str("foo"), kj::heapArray("bar"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); // Gate still isn't blocked during the transaction, because we set `allowUnconfirmed`. KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); @@ -2646,7 +2644,7 @@ KJ_TEST("allowUnconfirmed put in explicit transaction does not block output gate KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify data was written - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } KJ_TEST("confirmed put in explicit transaction blocks output gate on commit") { @@ -2659,8 +2657,7 @@ KJ_TEST("confirmed put in explicit transaction blocks output gate on commit") { auto txn = test.startTransaction(); // Do a confirmed put (default behavior) - txn->put(kj::str("foo"), kj::heapArray(kj::str("bar").asBytes()), {.allowUnconfirmed = false}, - nullptr); + txn->put(kj::str("foo"), kj::heapArray("bar"_kj.asBytes()), {.allowUnconfirmed = false}, nullptr); // Gate should still not be blocked during the transaction - explicit txns only lock on commit KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); @@ -2678,7 +2675,7 @@ KJ_TEST("confirmed put in explicit transaction blocks output gate on commit") { KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify data was written - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); } KJ_TEST("mixed confirmed and unconfirmed puts in explicit transaction use output gate") { @@ -2691,10 +2688,9 @@ KJ_TEST("mixed confirmed and unconfirmed puts in explicit transaction use output auto txn = test.startTransaction(); // Do an unconfirmed put followed by a confirmed put + txn->put(kj::str("foo"), kj::heapArray("bar"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); txn->put( - kj::str("foo"), kj::heapArray(kj::str("bar").asBytes()), {.allowUnconfirmed = true}, nullptr); - txn->put(kj::str("baz"), kj::heapArray(kj::str("quux").asBytes()), {.allowUnconfirmed = false}, - nullptr); + kj::str("baz"), kj::heapArray("quux"_kj.asBytes()), {.allowUnconfirmed = false}, nullptr); // Gate should still not be blocked during the transaction KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); @@ -2712,8 +2708,8 @@ KJ_TEST("mixed confirmed and unconfirmed puts in explicit transaction use output KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Both writes should be committed - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == kj::str("quux").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == "quux"_kj.asBytes()); } KJ_TEST("allowUnconfirmed delete in explicit transaction does not block output gate") { @@ -2762,8 +2758,8 @@ KJ_TEST("allowUnconfirmed putMultiple in explicit transaction does not block out // Do an unconfirmed putMultiple auto pairs = kj::heapArrayBuilder(2); - pairs.add(ActorCacheOps::KeyValuePair{kj::str("foo"), kj::heapArray(kj::str("bar").asBytes())}); - pairs.add(ActorCacheOps::KeyValuePair{kj::str("baz"), kj::heapArray(kj::str("quux").asBytes())}); + pairs.add(ActorCacheOps::KeyValuePair{kj::str("foo"), kj::heapArray("bar"_kj.asBytes())}); + pairs.add(ActorCacheOps::KeyValuePair{kj::str("baz"), kj::heapArray("quux"_kj.asBytes())}); txn->put(pairs.finish(), {.allowUnconfirmed = true}, nullptr); // Gate still isn't blocked during the transaction @@ -2782,8 +2778,8 @@ KJ_TEST("allowUnconfirmed putMultiple in explicit transaction does not block out KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify data was written - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == kj::str("bar").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == kj::str("quux").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("foo"))) == "bar"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("baz"))) == "quux"_kj.asBytes()); } KJ_TEST("allowUnconfirmed deleteMultiple in explicit transaction does not block output gate") { @@ -2868,16 +2864,16 @@ KJ_TEST("nested transaction: unconfirmed child commit does not block output gate auto parentTxn = test.startTransaction(); // Do an unconfirmed put in the parent - parentTxn->put(kj::str("parent"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = true}, nullptr); + parentTxn->put( + kj::str("parent"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); { // Start a nested child transaction auto childTxn = test.startTransaction(); // Do an unconfirmed put in the child - childTxn->put(kj::str("child"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = true}, nullptr); + childTxn->put( + kj::str("child"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); // Gate still isn't blocked KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); @@ -2902,8 +2898,8 @@ KJ_TEST("nested transaction: unconfirmed child commit does not block output gate KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify both writes were committed - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("parent"))) == kj::str("data").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("child"))) == kj::str("data").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("parent"))) == "data"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("child"))) == "data"_kj.asBytes()); } KJ_TEST("nested transaction: confirmed child propagates to parent commit") { @@ -2914,16 +2910,16 @@ KJ_TEST("nested transaction: confirmed child propagates to parent commit") { // Start a parent transaction with unconfirmed write auto parentTxn = test.startTransaction(); - parentTxn->put(kj::str("parent"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = true}, nullptr); + parentTxn->put( + kj::str("parent"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); { // Start a nested child transaction auto childTxn = test.startTransaction(); // Do a confirmed put in the child - childTxn->put(kj::str("child"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = false}, nullptr); + childTxn->put( + kj::str("child"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = false}, nullptr); // Gate still isn't blocked during the transaction KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); @@ -2948,8 +2944,8 @@ KJ_TEST("nested transaction: confirmed child propagates to parent commit") { KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify both writes were committed - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("parent"))) == kj::str("data").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("child"))) == kj::str("data").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("parent"))) == "data"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("child"))) == "data"_kj.asBytes()); } KJ_TEST("nested transaction: confirmed parent with unconfirmed child blocks output gate") { @@ -2960,16 +2956,16 @@ KJ_TEST("nested transaction: confirmed parent with unconfirmed child blocks outp // Start a parent transaction with confirmed write auto parentTxn = test.startTransaction(); - parentTxn->put(kj::str("parent"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = false}, nullptr); + parentTxn->put( + kj::str("parent"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = false}, nullptr); { // Start a nested child transaction auto childTxn = test.startTransaction(); // Do an unconfirmed put in the child - childTxn->put(kj::str("child"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = true}, nullptr); + childTxn->put( + kj::str("child"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); // Gate still isn't blocked during the transaction KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); @@ -2994,8 +2990,8 @@ KJ_TEST("nested transaction: confirmed parent with unconfirmed child blocks outp KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify both writes were committed - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("parent"))) == kj::str("data").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("child"))) == kj::str("data").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("parent"))) == "data"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("child"))) == "data"_kj.asBytes()); } KJ_TEST("nested transaction: deeply nested confirmed write propagates to root") { @@ -3006,20 +3002,20 @@ KJ_TEST("nested transaction: deeply nested confirmed write propagates to root") // Start a parent transaction with unconfirmed write auto txn1 = test.startTransaction(); - txn1->put(kj::str("level1"), kj::heapArray(kj::str("data").asBytes()), {.allowUnconfirmed = true}, - nullptr); + txn1->put( + kj::str("level1"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); { // Start a second level nested transaction with unconfirmed write auto txn2 = test.startTransaction(); - txn2->put(kj::str("level2"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = true}, nullptr); + txn2->put( + kj::str("level2"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); { // Start a third level nested transaction with confirmed write auto txn3 = test.startTransaction(); - txn3->put(kj::str("level3"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = false}, nullptr); + txn3->put(kj::str("level3"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = false}, + nullptr); // Gate still isn't blocked during the transaction KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); @@ -3049,9 +3045,9 @@ KJ_TEST("nested transaction: deeply nested confirmed write propagates to root") KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify all writes were committed - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("level1"))) == kj::str("data").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("level2"))) == kj::str("data").asBytes()); - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("level3"))) == kj::str("data").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("level1"))) == "data"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("level2"))) == "data"_kj.asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("level3"))) == "data"_kj.asBytes()); } KJ_TEST("nested transaction: rollback resets someWriteConfirmed flag") { @@ -3062,16 +3058,16 @@ KJ_TEST("nested transaction: rollback resets someWriteConfirmed flag") { // Start a parent transaction with unconfirmed write auto parentTxn = test.startTransaction(); - parentTxn->put(kj::str("parent"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = true}, nullptr); + parentTxn->put( + kj::str("parent"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); { // Start a nested child transaction auto childTxn = test.startTransaction(); // Do a confirmed put in the child - childTxn->put(kj::str("child"), kj::heapArray(kj::str("data").asBytes()), - {.allowUnconfirmed = false}, nullptr); + childTxn->put( + kj::str("child"), kj::heapArray("data"_kj.asBytes()), {.allowUnconfirmed = false}, nullptr); // Gate still isn't blocked during the transaction KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); @@ -3096,7 +3092,7 @@ KJ_TEST("nested transaction: rollback resets someWriteConfirmed flag") { KJ_ASSERT(test.gate.wait(nullptr).poll(test.ws)); // Verify only parent write was committed - KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("parent"))) == kj::str("data").asBytes()); + KJ_ASSERT(KJ_ASSERT_NONNULL(expectSync(test.get("parent"))) == "data"_kj.asBytes()); KJ_ASSERT(expectSync(test.get("child")) == kj::none); } @@ -3112,8 +3108,7 @@ KJ_TEST("explicit transaction: commit failure breaks output gate even for unconf auto txn = test.startTransaction(); // Do an unconfirmed put - txn->put( - kj::str("foo"), kj::heapArray(kj::str("bar").asBytes()), {.allowUnconfirmed = true}, nullptr); + txn->put(kj::str("foo"), kj::heapArray("bar"_kj.asBytes()), {.allowUnconfirmed = true}, nullptr); // Commit the transaction txn->commit(); From 969e87ddac21ce256b5293ee118ca2488dd2ef26 Mon Sep 17 00:00:00 2001 From: Mike Aizatsky Date: Tue, 1 Sep 2026 10:02:52 -0700 Subject: [PATCH 2/2] Give module test sources stable ownership --- src/workerd/jsg/modules-new-test.c++ | 214 ++++++++++++--------------- 1 file changed, 98 insertions(+), 116 deletions(-) diff --git a/src/workerd/jsg/modules-new-test.c++ b/src/workerd/jsg/modules-new-test.c++ index 7d8ef630f8d..6577e124712 100644 --- a/src/workerd/jsg/modules-new-test.c++ +++ b/src/workerd/jsg/modules-new-test.c++ @@ -847,8 +847,9 @@ KJ_TEST("A worker bundle module can shadow node:process") { registryBuilder.add(internalBuilder.finish()); ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto shadowSource = kj::str("export default 'shadowed-process';"); - bundleBuilder.addEsmModule("node:process", shadowSource); + bundleBuilder.addEsmModule("node:process", + kj::arc( + kj::heapArray("export default 'shadowed-process';"_kj.asArray()))); registryBuilder.add(bundleBuilder.finish()); auto registry = registryBuilder.finish(); @@ -890,13 +891,15 @@ KJ_TEST("A worker bundle module can shadow node:process via dynamic import") { registryBuilder.add(internalBuilder.finish()); ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto shadowSource = kj::str("export default 'shadowed-process';"); - bundleBuilder.addEsmModule("node:process", shadowSource); + bundleBuilder.addEsmModule("node:process", + kj::arc( + kj::heapArray("export default 'shadowed-process';"_kj.asArray()))); // Entrypoint module that reaches node:process exclusively through dynamic // import(), forcing resolution through dynamicResolve() rather than the // static resolveCallback path. - auto mainSource = kj::str("export default (await import('node:process')).default;"); - bundleBuilder.addEsmModule("main", mainSource); + bundleBuilder.addEsmModule("main", + kj::arc(kj::heapArray( + "export default (await import('node:process')).default;"_kj.asArray()))); registryBuilder.add(bundleBuilder.finish()); auto registry = registryBuilder.finish(); @@ -956,12 +959,13 @@ KJ_TEST("Attaching a module registry works") { ModuleRegistry::Builder registryBuilder(BASE); ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto source = kj::str("export default 123; export const m = 'abc';"); - // Done this way to avoid including the nullptr at the end... - bundleBuilder.addEsmModule("main", source); - - auto mainSource = kj::str("import foo from 'main'; export default foo;"); - bundleBuilder.addEsmModule("worker1", mainSource.first(mainSource.size()), Module::Flags::MAIN); + bundleBuilder.addEsmModule("main", + kj::arc( + kj::heapArray("export default 123; export const m = 'abc';"_kj.asArray()))); + bundleBuilder.addEsmModule("worker1", + kj::arc( + kj::heapArray("import foo from 'main'; export default foo;"_kj.asArray())), + Module::Flags::MAIN); registryBuilder.add(bundleBuilder.finish()); @@ -1017,14 +1021,14 @@ KJ_TEST("Basic types of modules work (text, data, json, wasm)") { bundleBuilder.addSyntheticModule("xyz", Module::newDataModuleHandler(xyzData)); bundleBuilder.addSyntheticModule("json", Module::newJsonModuleHandler(json.first(json.size()))); bundleBuilder.addSyntheticModule("wasm", Module::newWasmModuleHandler(wasm)); - - auto mainSource2 = kj::str("export { default as abc } from 'abc';" - "export { default as xyz } from 'xyz';" - "export { default as json } from 'json';" - "export { default as wasm } from 'wasm';" - "export { default as wasm2 } from 'wasm?a';"); - - bundleBuilder.addEsmModule("worker", mainSource2, Module::Flags::MAIN); + bundleBuilder.addEsmModule("worker", + kj::arc( + kj::heapArray("export { default as abc } from 'abc';" + "export { default as xyz } from 'xyz';" + "export { default as json } from 'json';" + "export { default as wasm } from 'wasm';" + "export { default as wasm2 } from 'wasm?a';"_kj.asArray())), + Module::Flags::MAIN); registryBuilder.add(bundleBuilder.finish()); @@ -1182,8 +1186,9 @@ KJ_TEST("compileEvalFunction in synthetic module works") { }); }); - auto source = kj::str("import 'abc'"); - bundleBuilder.addEsmModule("main", source, Module::Flags::MAIN); + bundleBuilder.addEsmModule("main", + kj::arc(kj::heapArray("import 'abc'"_kj.asArray())), + Module::Flags::MAIN); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1203,10 +1208,9 @@ KJ_TEST("import.meta works as expected") { CompilationObserver compilationObserver; ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto foo = kj::str("export default import.meta"); - bundleBuilder.addEsmModule("foo", foo); - auto bar = kj::str("export default import.meta"); - bundleBuilder.addEsmModule("foo/././././bar", bar, Module::Flags::MAIN); + bundleBuilder.addEsmModule("foo", "export default import.meta"_kjc); + bundleBuilder.addEsmModule( + "foo/././././bar", "export default import.meta"_kjc, Module::Flags::MAIN); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); auto attached = registry->attachToIsolate(js, compilationObserver); @@ -1290,8 +1294,7 @@ KJ_TEST("import specifiers with query params and hash fragments work") { CompilationObserver compilationObserver; ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto foo = kj::str("export default import.meta"); - bundleBuilder.addEsmModule("foo", foo); + bundleBuilder.addEsmModule("foo", "export default import.meta"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1344,8 +1347,7 @@ KJ_TEST("Previously resolved modules not found with incompatible resolve context ModuleBundle::BuiltinBuilder builtinBuilder(ModuleBundle::BuiltinBuilder::Type::BUILTIN_ONLY); const auto foo = "foo:bar"_url; - auto source = "export default 123;"_kjc; - builtinBuilder.addEsm(foo, source.first(source.size()).attach(kj::mv(source))); + builtinBuilder.addEsm(foo, "export default 123;"_kjc); auto barData = kj::heapArray({1, 2, 3}); @@ -1399,11 +1401,8 @@ KJ_TEST("Awaiting top-level dynamic import in synchronous require works as expec CompilationObserver compilationObserver; ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto foo = kj::str("export default (await import('bar')).default;"); - bundleBuilder.addEsmModule("foo", foo); - - auto bar = kj::str("export default 123;"); - bundleBuilder.addEsmModule("bar", bar); + bundleBuilder.addEsmModule("foo", "export default (await import('bar')).default;"_kjc); + bundleBuilder.addEsmModule("bar", "export default 123;"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1421,8 +1420,7 @@ KJ_TEST("Awaiting a never resolved promise in synchronous require fails as expec CompilationObserver compilationObserver; ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto foo = kj::str("const p = new Promise(() => {}); await p;"); - bundleBuilder.addEsmModule("foo", foo); + bundleBuilder.addEsmModule("foo", "const p = new Promise(() => {}); await p;"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1450,8 +1448,7 @@ KJ_TEST("Throwing an exception inside a ESM module works as expected") { CompilationObserver compilationObserver; ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto foo = kj::str("throw new Error('foo');"); - bundleBuilder.addEsmModule("foo", foo); + bundleBuilder.addEsmModule("foo", "throw new Error('foo');"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1475,9 +1472,7 @@ KJ_TEST("Syntax error in ESM module is properly reported") { CompilationObserver compilationObserver; ModuleBundle::BundleBuilder bundleBuilder(BASE); - - auto foo = kj::str("export default 123; syntax error"); - bundleBuilder.addEsmModule("foo", foo); + bundleBuilder.addEsmModule("foo", "export default 123; syntax error"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1499,9 +1494,7 @@ KJ_TEST("Syntax error in ESM module is reported consistently on repeated resolut CompilationObserver compilationObserver; ModuleBundle::BundleBuilder bundleBuilder(BASE); - - auto foo = kj::str("export default 123; syntax error"); - bundleBuilder.addEsmModule("foo", foo); + bundleBuilder.addEsmModule("foo", "export default 123; syntax error"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1562,26 +1555,28 @@ KJ_TEST("Module source is decoded as UTF-8 across all encoding tiers") { ModuleBundle::BundleBuilder bundleBuilder(BASE); // Tier 1: pure-ASCII source (zero-copy external one-byte string). - auto ascii = kj::str("export default 'plain';"); - bundleBuilder.addEsmModule("ascii", ascii); + bundleBuilder.addEsmModule("ascii", + kj::arc(kj::heapArray("export default 'plain';"_kj.asArray()))); // Tier 2: non-ASCII source whose code points all fit in Latin-1. The // identifier and the literal both contain é (U+00E9, UTF-8 c3 a9); under a // Latin-1 misread the identifier would be a SyntaxError (a UTF-8 // continuation byte is not a valid identifier char) and the literal would // be mojibake. - auto latin1 = kj::str("const caf\xc3\xa9 = 'caf\xc3\xa9'; export default caf\xc3\xa9;"); - bundleBuilder.addEsmModule("latin1", latin1); + bundleBuilder.addEsmModule("latin1", + kj::arc(kj::heapArray( + "const caf\xc3\xa9 = 'caf\xc3\xa9'; export default caf\xc3\xa9;"_kj.asArray()))); // Tier 3: source requiring UTF-16 (CJK + non-BMP emoji). - auto utf16 = kj::str("export default '\xe9\x83\xa8\xe5\x93\x81 \xf0\x9f\x8e\x89';"); - bundleBuilder.addEsmModule("utf16", utf16); + bundleBuilder.addEsmModule("utf16", + kj::arc(kj::heapArray( + "export default '\xe9\x83\xa8\xe5\x93\x81 \xf0\x9f\x8e\x89';"_kj.asArray()))); // Invalid UTF-8: a lone 0xE9 byte inside a literal. Malformed sequences are // replaced with U+FFFD (UTF-8 ef bf bd), matching v8::String::NewFromUtf8's // tolerance rather than rejecting the module. - auto invalid = kj::str("export default 'caf\xe9';"); - bundleBuilder.addEsmModule("invalid", invalid); + bundleBuilder.addEsmModule("invalid", + kj::arc(kj::heapArray("export default 'caf\xe9';"_kj.asArray()))); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); auto attached = registry->attachToIsolate(js, compilationObserver); @@ -1593,11 +1588,11 @@ KJ_TEST("Module source is decoded as UTF-8 across all encoding tiers") { auto cafe = kj::str(ModuleRegistry::resolve(js, "file:///latin1")); KJ_ASSERT(cafe == "caf\xc3\xa9", cafe); - auto tokyo = kj::str(ModuleRegistry::resolve(js, "file:///utf16")); - KJ_ASSERT(tokyo == "\xe9\x83\xa8\xe5\x93\x81 \xf0\x9f\x8e\x89", tokyo); + auto cjk = kj::str(ModuleRegistry::resolve(js, "file:///utf16")); + KJ_ASSERT(cjk == "\xe9\x83\xa8\xe5\x93\x81 \xf0\x9f\x8e\x89", cjk); - auto korean = kj::str(ModuleRegistry::resolve(js, "file:///invalid")); - KJ_ASSERT(korean == "caf\xef\xbf\xbd", korean); + auto replaced = kj::str(ModuleRegistry::resolve(js, "file:///invalid")); + KJ_ASSERT(replaced == "caf\xef\xbf\xbd", replaced); } JSG_CATCH(exception) { js.throwException(kj::mv(exception)); @@ -1735,12 +1730,11 @@ KJ_TEST("Dynamic import from within a CJS-style eval module works") { bundleBuilder.addSyntheticModule( "cjs-dyn", Module::newCjsStyleModuleHandler(source)); - auto dep = kj::str("export default 123;"); - bundleBuilder.addEsmModule("dep", dep); + bundleBuilder.addEsmModule("dep", "export default 123;"_kjc); // The ESM entry point awaits the promise exported by the CJS module. - auto entry = kj::str("import cjs from 'cjs-dyn'; export default (await cjs.p).default;"); - bundleBuilder.addEsmModule("entry", entry); + bundleBuilder.addEsmModule( + "entry", "import cjs from 'cjs-dyn'; export default (await cjs.p).default;"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); auto attached = registry->attachToIsolate(js, compilationObserver); @@ -1764,8 +1758,7 @@ KJ_TEST("Dynamic import from a script with a non-URL origin fails cleanly") { CompilationObserver compilationObserver; ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto dep = kj::str("export default 123;"); - bundleBuilder.addEsmModule("dep", dep); + bundleBuilder.addEsmModule("dep", "export default 123;"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); auto attached = registry->attachToIsolate(js, compilationObserver); @@ -1802,9 +1795,7 @@ KJ_TEST("Invalid JSON syntax module throws exception as expected") { auto json = kj::str("not valid json"); ModuleBundle::BundleBuilder bundleBuilder(BASE); bundleBuilder.addSyntheticModule("foo", Module::newJsonModuleHandler(json.first(json.size()))); - - auto esm = kj::str("import foo from 'foo'"); - bundleBuilder.addEsmModule("bar", esm, Module::Flags::MAIN); + bundleBuilder.addEsmModule("bar", "import foo from 'foo'"_kjc, Module::Flags::MAIN); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1848,8 +1839,7 @@ KJ_TEST("Recursive import works or fails as expected") { ModuleBundle::BundleBuilder bundleBuilder(BASE); // A recursive import with an ESM works just fine... - auto foo = kj::str("import foo from 'foo'; export default 123;"); - bundleBuilder.addEsmModule("foo", foo); + bundleBuilder.addEsmModule("foo", "import foo from 'foo'; export default 123;"_kjc); auto source = kj::str("require('bar')"); @@ -1898,8 +1888,7 @@ KJ_TEST("Recursively require ESM from CJS required from ESM fails as expected (d bundleBuilder.addSyntheticModule( "foo", Module::newCjsStyleModuleHandler(source2)); - auto bar = kj::str("export default {}; await import('foo');"); - bundleBuilder.addEsmModule("bar", bar); + bundleBuilder.addEsmModule("bar", "export default {}; await import('foo');"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1941,8 +1930,7 @@ KJ_TEST("Recursively require ESM from CJS required from ESM fails as expected (s bundleBuilder.addSyntheticModule( "foo", Module::newCjsStyleModuleHandler(source2)); - auto bar = kj::str("export default {}; import bar from 'foo';"); - bundleBuilder.addEsmModule("bar", bar); + bundleBuilder.addEsmModule("bar", "export default {}; import bar from 'foo';"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -1974,8 +1962,8 @@ KJ_TEST("ESM -> CJS -> require(ESM) -> static import CJS circular dependency fai ModuleBundle::BundleBuilder bundleBuilder(BASE); // a.js (ESM) -> imports b (CJS) - auto a = kj::str("import b from 'b'; export default b;"); - bundleBuilder.addEsmModule("a", a, Module::Flags::MAIN); + bundleBuilder.addEsmModule( + "a", "import b from 'b'; export default b;"_kjc, Module::Flags::MAIN); // b (CJS) -> require('c') which is an ESM that imports b back auto bSource = kj::str("exports = require('c');"); @@ -1983,8 +1971,7 @@ KJ_TEST("ESM -> CJS -> require(ESM) -> static import CJS circular dependency fai "b", Module::newCjsStyleModuleHandler(bSource)); // c.js (ESM) -> imports b (CJS) — creates the circular dependency - auto c = kj::str("import b from 'b'; export default b;"); - bundleBuilder.addEsmModule("c", c); + bundleBuilder.addEsmModule("c", "import b from 'b'; export default b;"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); @@ -2030,15 +2017,16 @@ KJ_TEST("Nested require() that pumps microtasks does not crash a sibling TLA mod ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto entry = kj::str("import { v } from 'leaf';\n" - "import 'pump';\n" - "export default v;\n"); - bundleBuilder.addEsmModule("entry", entry, Module::Flags::MAIN); + bundleBuilder.addEsmModule("entry", + "import { v } from 'leaf';\n" + "import 'pump';\n" + "export default v;\n"_kjc, + Module::Flags::MAIN); // Top-level await: leaf's evaluation promise fulfills on a later microtask. - auto leaf = kj::str("await Promise.resolve();\n" - "export const v = 1;\n"); - bundleBuilder.addEsmModule("leaf", leaf); + bundleBuilder.addEsmModule("leaf", + "await Promise.resolve();\n" + "export const v = 1;\n"_kjc); // pump (CJS): its evaluation performs a nested require(), which pumps the // microtask queue while entry is still kEvaluating. @@ -2083,14 +2071,13 @@ KJ_TEST("A throwing module evaluation does not leak the evaluation depth") { ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto boom = kj::str("throw new Error('boom');\n"); - bundleBuilder.addEsmModule("boom", boom, Module::Flags::MAIN); + bundleBuilder.addEsmModule("boom", "throw new Error('boom');\n"_kjc, Module::Flags::MAIN); // Top-level await that settles within a single microtask drain. Requiring this at // the top level must pump and resolve -- which only works if the depth is back at 0. - auto after = kj::str("await Promise.resolve();\n" - "export const ok = 1;\n"); - bundleBuilder.addEsmModule("after", after); + bundleBuilder.addEsmModule("after", + "await Promise.resolve();\n" + "export const ok = 1;\n"_kjc); auto registry = ModuleRegistry::Builder(BASE).add(bundleBuilder.finish()).finish(); auto attached = registry->attachToIsolate(js, compilationObserver); @@ -2124,17 +2111,18 @@ KJ_TEST("UNWRAP_DEFAULT returns namespace for bundle ESM, default for others") { ResolveObserverImpl observer; CompilationObserver compilationObserver; + // Synthetic module handlers borrow their source buffers. auto json = kj::str("{\"key\": \"value\"}"); auto text = kj::str("hello world"); ModuleBundle::BundleBuilder bundleBuilder(BASE); // Bundle ESM with named exports (no __cjsUnwrapDefault) - auto esm = kj::str("export default 42; export const name = 'esm';"); - bundleBuilder.addEsmModule("esm-mod", esm, Module::Flags::MAIN); + bundleBuilder.addEsmModule( + "esm-mod", "export default 42; export const name = 'esm';"_kjc, Module::Flags::MAIN); // Bundle ESM with __cjsUnwrapDefault convention - auto esmCjs = kj::str("export default 'unwrapped'; export const __cjsUnwrapDefault = true;"); - bundleBuilder.addEsmModule("esm-cjs", esmCjs); + bundleBuilder.addEsmModule( + "esm-cjs", "export default 'unwrapped'; export const __cjsUnwrapDefault = true;"_kjc); // JSON synthetic module bundleBuilder.addSyntheticModule( @@ -2208,27 +2196,26 @@ KJ_TEST("UNWRAP_DEFAULT honors module.exports, marker order, and builtin fallbac // Node's official require(esm) mechanism: a string-named 'module.exports' // export controls the require() result. - auto modExports = kj::str("const impl = { hello: 1 };\n" - "export { impl as 'module.exports' };\n" - "export default 'not-this';\n"); - bundleBuilder.addEsmModule("mod-exports", modExports); + bundleBuilder.addEsmModule("mod-exports", + "const impl = { hello: 1 };\n" + "export { impl as 'module.exports' };\n" + "export default 'not-this';\n"_kjc); // When both markers are present, __cjsUnwrapDefault wins (matching the // legacy registry's check order). - auto bothMarkers = kj::str("export const __cjsUnwrapDefault = true;\n" - "const impl = 'module-exports-value';\n" - "export { impl as 'module.exports' };\n" - "export default 'default-value';\n"); - bundleBuilder.addEsmModule("both-markers", bothMarkers); + bundleBuilder.addEsmModule("both-markers", + "export const __cjsUnwrapDefault = true;\n" + "const impl = 'module-exports-value';\n" + "export { impl as 'module.exports' };\n" + "export default 'default-value';\n"_kjc); // Builtin ESM with and without a default export. ModuleBundle::BuiltinBuilder builtinBuilder(ModuleBundle::BuiltinBuilder::Type::BUILTIN); static const auto kWithDefault = "test:with-default"_url; static const auto kNoDefault = "test:no-default"_url; - auto withDefault = kj::str("export default 'builtin-default'; export const extra = 1;"); - auto noDefault = kj::str("export const onlyNamed = 42;"); - builtinBuilder.addEsm(kWithDefault, withDefault); - builtinBuilder.addEsm(kNoDefault, noDefault); + builtinBuilder.addEsm( + kWithDefault, "export default 'builtin-default'; export const extra = 1;"_kjc); + builtinBuilder.addEsm(kNoDefault, "export const onlyNamed = 42;"_kjc); // Fallback-service ESM serves user code and behaves like bundle ESM. auto fallback = ModuleBundle::newFallbackBundle( @@ -2293,21 +2280,18 @@ KJ_TEST("A URL can hold distinct modules per context type (bundle shadow vs buil // dynamic import so the referrer probe is exercised for a URL that has // entries under multiple context types. ModuleBundle::BundleBuilder bundleBuilder(BASE); - auto shadow = kj::str("export default 'shadow'; export const p = import('file:///dep');"); - bundleBuilder.addEsmModule("test:thing", shadow); - auto dep = kj::str("export default 'dep';"); - bundleBuilder.addEsmModule("dep", dep); + bundleBuilder.addEsmModule( + "test:thing", "export default 'shadow'; export const p = import('file:///dep');"_kjc); + bundleBuilder.addEsmModule("dep", "export default 'dep';"_kjc); // ...and the real builtin registered under the very same URL. ModuleBundle::BuiltinBuilder builtinBuilder(ModuleBundle::BuiltinBuilder::Type::BUILTIN); static const auto kThing = "test:thing"_url; - auto builtin = kj::str("export default 'builtin';"); - builtinBuilder.addEsm(kThing, builtin); + builtinBuilder.addEsm(kThing, "export default 'builtin';"_kjc); // An unshadowed builtin, for the shared-instantiation direction. static const auto kShared = "test:shared"_url; - auto shared = kj::str("export default 'shared';"); - builtinBuilder.addEsm(kShared, shared); + builtinBuilder.addEsm(kShared, "export default 'shared';"_kjc); auto registry = ModuleRegistry::Builder(BASE) .add(bundleBuilder.finish()) @@ -2370,9 +2354,7 @@ KJ_TEST("REQUIRE_ESM rejects non-ESM entry points before evaluation") { auto json = kj::str("{\"key\": \"value\"}"); ModuleBundle::BundleBuilder bundleBuilder(BASE); - - auto esm = kj::str("export default 42;"); - bundleBuilder.addEsmModule("main", esm); + bundleBuilder.addEsmModule("main", "export default 42;"_kjc); bundleBuilder.addSyntheticModule( "data.json", Module::newJsonModuleHandler(json.first(json.size())));