diff --git a/src/workerd/api/actor-state-test.c++ b/src/workerd/api/actor-state-test.c++ index 5ad0444cb3f..b87c5969220 100644 --- a/src/workerd/api/actor-state-test.c++ +++ b/src/workerd/api/actor-state-test.c++ @@ -39,8 +39,7 @@ KJ_TEST("v8 serialization version tag hasn't changed") { auto v8Context = isolateLock.newContext().getHandle(isolateLock); auto contextScope = isolateLock.enterContextScope(v8Context); - auto trueVal = v8::True(isolateLock.v8Isolate); - auto buf = serializeV8Value(isolateLock, trueVal); + auto buf = serializeV8Value(isolateLock, isolateLock.boolean(true)); // Confirm that a version header is appropriately written and that it contains the expected // current version. When the version increases, we need to write a v8 patch that allows it to @@ -56,7 +55,7 @@ KJ_TEST("v8 serialization version tag hasn't changed") { KJ_EXPECT(deserializer.GetWireFormatVersion() == 15); // Just for kicks, make sure it deserializes properly too. - KJ_EXPECT(deserializeV8Value(isolateLock, "some-key"_kj, buf)->IsTrue()); + KJ_EXPECT(deserializeV8Value(isolateLock, "some-key"_kj, buf).isTrue()); }); } @@ -77,7 +76,7 @@ KJ_TEST("we support deserializing up to v15") { for (const auto& hexStr : testCases) { auto dataIn = kj::decodeHex(hexStr.asArray()); - KJ_EXPECT(deserializeV8Value(isolateLock, "some-key"_kj, dataIn)->IsTrue()); + KJ_EXPECT(deserializeV8Value(isolateLock, "some-key"_kj, dataIn).isTrue()); } }); } @@ -89,13 +88,13 @@ KJ_TEST("we support deserializing up to v15") { // data and round-trip it back to storage to deal with the problem that it likes to read in "sparse" // JS arrays and write them back out as "dense" JS arrays, which breaks the equality check after // round-tripping a value. -v8::Local oldDeserializeV8Value( +jsg::JsValue oldDeserializeV8Value( jsg::Lock& js, kj::ArrayPtr key, kj::ArrayPtr buf) { v8::ValueDeserializer deserializer(js.v8Isolate, buf.begin(), buf.size()); auto maybeValue = deserializer.ReadValue(js.v8Context()); v8::Local value; KJ_ASSERT(maybeValue.ToLocal(&value)); - return kj::mv(value); + return jsg::JsValue(value); } KJ_TEST("wire format version does not change deserialization behavior on real data") { diff --git a/src/workerd/api/actor-state.c++ b/src/workerd/api/actor-state.c++ index 48cbc178546..ef444a7bb96 100644 --- a/src/workerd/api/actor-state.c++ +++ b/src/workerd/api/actor-state.c++ @@ -33,12 +33,12 @@ uint32_t billingUnits(size_t bytes, BillAtLeastOne billAtLeastOne = BillAtLeastO return bytes / BILLING_UNIT + (bytes % BILLING_UNIT != 0); } -v8::Local deserializeMaybeV8Value( +jsg::JsValue deserializeMaybeV8Value( jsg::Lock& js, kj::ArrayPtr key, kj::Maybe> buf) { KJ_IF_MAYBE(b, buf) { return deserializeV8Value(js, key, *b); } else { - return js.v8Undefined(); + return js.undefined(); } } @@ -117,19 +117,18 @@ ActorObserver& currentActorMetrics() { return IoContext::current().getActorOrThrow().getMetrics(); } -jsg::Value listResultsToMap(jsg::Lock& js, - ActorCacheOps::GetResultList value, - bool completelyCached) { +jsg::JsRef listResultsToMap(jsg::Lock& js, + ActorCacheOps::GetResultList value, + bool completelyCached) { return js.withinHandleScope([&] { - auto map = v8::Map::New(js.v8Isolate); + auto map = js.map(); size_t cachedReadBytes = 0; size_t uncachedReadBytes = 0; for (auto entry: value) { auto& bytesRef = entry.status == ActorCacheOps::CacheStatus::CACHED ? cachedReadBytes : uncachedReadBytes; bytesRef += entry.key.size() + entry.value.size(); - jsg::check(map->Set(js.v8Context(), jsg::v8Str(js.v8Isolate, entry.key), - deserializeV8Value(js, entry.key, entry.value))); + map.set(js, entry.key, deserializeV8Value(js, entry.key, entry.value)); } auto& actorMetrics = currentActorMetrics(); if (cachedReadBytes || uncachedReadBytes) { @@ -150,23 +149,23 @@ jsg::Value listResultsToMap(jsg::Lock& js, actorMetrics.addUncachedStorageReadUnits(1); } - return js.v8Ref(map.As()); + return jsg::JsValue(map).addRef(js); }); } -kj::Function getMultipleResultsToMap( +kj::Function(jsg::Lock&, ActorCacheOps::GetResultList)> +getMultipleResultsToMap( size_t numInputKeys) { return [numInputKeys](jsg::Lock& js, ActorCacheOps::GetResultList value) mutable { return js.withinHandleScope([&] { - auto map = v8::Map::New(js.v8Isolate); + auto map = js.map(); uint32_t cachedUnits = 0; uint32_t uncachedUnits = 0; for (auto entry: value) { auto& unitsRef = entry.status == ActorCacheOps::CacheStatus::CACHED ? cachedUnits : uncachedUnits; unitsRef += billingUnits(entry.key.size() + entry.value.size()); - jsg::check(map->Set(js.v8Context(), jsg::v8Str(js.v8Isolate, entry.key), - deserializeV8Value(js, entry.key, entry.value))); + map.set(js, entry.key, deserializeV8Value(js, entry.key, entry.value)); } auto& actorMetrics = currentActorMetrics(); actorMetrics.addCachedStorageReadUnits(cachedUnits); @@ -187,7 +186,7 @@ kj::Function getMultipleRe // only for uncached reads, we'll need to address this. actorMetrics.addUncachedStorageReadUnits(leftoverKeys + uncachedUnits); - return js.v8Ref(map.As()); + return jsg::JsValue(map).addRef(js); }); }; } @@ -213,7 +212,7 @@ kj::Promise updateStorageDeletes(IoContext& context, } // namespace -jsg::Promise DurableObjectStorageOperations::get( +jsg::Promise> DurableObjectStorageOperations::get( jsg::Lock& js, kj::OneOf> keys, jsg::Optional maybeOptions) { @@ -229,7 +228,7 @@ jsg::Promise DurableObjectStorageOperations::get( KJ_UNREACHABLE } -jsg::Promise DurableObjectStorageOperations::getOne( +jsg::Promise> DurableObjectStorageOperations::getOne( jsg::Lock& js, kj::String key, const GetOptions& options) { ActorStorageLimits::checkMaxKeySize(key); @@ -246,7 +245,7 @@ jsg::Promise DurableObjectStorageOperations::getOne( } else { actorMetrics.addUncachedStorageReadUnits(units); } - return js.v8Ref(deserializeMaybeV8Value(js, key, value)); + return deserializeMaybeV8Value(js, key, value).addRef(js); }); } @@ -270,7 +269,7 @@ jsg::Promise> DurableObjectStorageOperations::getAlarm( }); } -jsg::Promise DurableObjectStorageOperations::list( +jsg::Promise> DurableObjectStorageOperations::list( jsg::Lock& js, jsg::Optional maybeOptions) { kj::String start; kj::Maybe end; @@ -278,7 +277,7 @@ jsg::Promise DurableObjectStorageOperations::list( kj::Maybe limit; auto makeEmptyResult = [&]() { - return js.resolvedPromise(js.v8Ref(v8::Map::New(js.v8Isolate).As())); + return js.resolvedPromise(jsg::JsValue(js.map()).addRef(js)); }; KJ_IF_MAYBE(o, maybeOptions) { @@ -380,9 +379,11 @@ jsg::Promise DurableObjectStorageOperations::list( options, &listResultsToMap); } -jsg::Promise DurableObjectStorageOperations::put(jsg::Lock& js, - kj::OneOf>> keyOrEntries, - jsg::Optional> value, jsg::Optional maybeOptions, +jsg::Promise DurableObjectStorageOperations::put( + jsg::Lock& js, + kj::OneOf> keyOrEntries, + jsg::Optional value, + jsg::Optional maybeOptions, const jsg::TypeHandler& optionsTypeHandler) { // TODO(soon): Add tests of data generated at current versions to ensure we'll // know before releasing any backwards-incompatible serializer changes, @@ -396,7 +397,7 @@ jsg::Promise DurableObjectStorageOperations::put(jsg::Lock& js, JSG_FAIL_REQUIRE(TypeError, "put() called with undefined value."); } } - KJ_CASE_ONEOF(o, jsg::Dict>) { + KJ_CASE_ONEOF(o, jsg::Dict) { KJ_IF_MAYBE(v, value) { KJ_IF_MAYBE(opt, optionsTypeHandler.tryUnwrap(js, *v)) { return putMultiple(js, kj::mv(o), configureOptions(kj::mv(*opt))); @@ -454,7 +455,10 @@ jsg::Promise DurableObjectStorageOperations::setAlarm( } jsg::Promise DurableObjectStorageOperations::putOne( - jsg::Lock& js, kj::String key, v8::Local value, const PutOptions& options) { + jsg::Lock& js, + kj::String key, + jsg::JsValue value, + const PutOptions& options) { ActorStorageLimits::checkMaxKeySize(key); kj::Array buffer = serializeV8Value(js, value); @@ -530,8 +534,10 @@ jsg::Promise DurableObjectStorageOperations::deleteOne( }); } -jsg::Promise DurableObjectStorageOperations::getMultiple( - jsg::Lock& js, kj::Array keys, const GetOptions& options) { +jsg::Promise> DurableObjectStorageOperations::getMultiple( + jsg::Lock& js, + kj::Array keys, + const GetOptions& options) { ActorStorageLimits::checkMaxPairsCount(keys.size()); auto numKeys = keys.size(); @@ -541,12 +547,14 @@ jsg::Promise DurableObjectStorageOperations::getMultiple( } jsg::Promise DurableObjectStorageOperations::putMultiple( - jsg::Lock& js, jsg::Dict> entries, const PutOptions& options) { + jsg::Lock& js, + jsg::Dict entries, + const PutOptions& options) { kj::Vector kvs(entries.fields.size()); uint32_t units = 0; for (auto& field : entries.fields) { - if (field.value->IsUndefined()) continue; + if (field.value.isUndefined()) continue; // We silently drop fields with value=undefined in putMultiple. There aren't many good options here, as // deleting an undefined field is confusing, throwing could break otherwise working code, and // a stray undefined here or there is probably closer to what the user desires. @@ -589,13 +597,14 @@ ActorCacheOps& DurableObjectStorage::getCache(OpName op) { return *cache; } -jsg::Promise DurableObjectStorage::transaction(jsg::Lock& js, - jsg::Function(jsg::Ref)> callback, +jsg::Promise> DurableObjectStorage::transaction(jsg::Lock& js, + jsg::Function>( + jsg::Ref)> callback, jsg::Optional options) { auto& context = IoContext::current(); struct TxnResult { - jsg::Value value; + jsg::JsRef value; bool isError; }; @@ -613,7 +622,7 @@ jsg::Promise DurableObjectStorage::transaction(jsg::Lock& js, return js.resolvedPromise(txn.addRef()) .then(js, kj::mv(callback)) - .then(js, [txn = txn.addRef()](jsg::Lock& js, jsg::Value value) mutable { + .then(js, [txn = txn.addRef()](jsg::Lock& js, jsg::JsRef value) mutable { // In correct usage, `context` should not have changed here, particularly because we're in // a critical section so it should have been impossible for any other context to receive // control. However, depending on all that is a bit precarious. jsg::Promise::then() itself @@ -630,18 +639,24 @@ jsg::Promise DurableObjectStorage::transaction(jsg::Lock& js, // we only want to roll back the transaction and propagate the exception. So, we carefully // pack the exception away into a value. txn->maybeRollback(); - return js.resolvedPromise(TxnResult { kj::mv(exception), true }); + return js.resolvedPromise(TxnResult { + // TODO(cleanup): Simplify this once exception is passed using jsg::JsRef instead + // of jsg::V8Ref + jsg::JsValue(exception.getHandle(js)).addRef(js), true + }); }); - }).then(js, [](jsg::Lock& js, TxnResult result) -> jsg::Value { + }).then(js, [](jsg::Lock& js, TxnResult result) -> jsg::JsRef { if (result.isError) { - js.throwException(kj::mv(result.value)); + js.throwException(result.value.getHandle(js)); } else { return kj::mv(result.value); } }); } -jsg::Value DurableObjectStorage::transactionSync(jsg::Lock& js, jsg::Function callback) { +jsg::JsRef DurableObjectStorage::transactionSync( + jsg::Lock& js, + jsg::Function()> callback) { KJ_IF_MAYBE(sqlite, cache->getSqliteDatabase()) { // SAVEPOINT is a readonly statement, but we need to trigger an outer TRANSACTION sqlite->notifyWrite(); @@ -654,7 +669,7 @@ jsg::Value DurableObjectStorage::transactionSync(jsg::Lock& js, jsg::Functionrun(SqliteDatabase::TRUSTED, kj::str("RELEASE _cf_sync_savepoint_", depth)); return kj::mv(result); - }, [&](jsg::Value exception) -> jsg::Value { + }, [&](jsg::Value exception) -> jsg::JsRef { sqlite->run(SqliteDatabase::TRUSTED, kj::str("ROLLBACK TO _cf_sync_savepoint_", depth)); js.throwException(kj::mv(exception)); }); @@ -736,8 +751,11 @@ void DurableObjectTransaction::maybeRollback() { } ActorState::ActorState(Worker::Actor::Id actorId, - kj::Maybe transient, kj::Maybe> persistent) - : id(kj::mv(actorId)), transient(kj::mv(transient)), persistent(kj::mv(persistent)) {} + kj::Maybe> transient, + kj::Maybe> persistent) + : id(kj::mv(actorId)), + transient(kj::mv(transient)), + persistent(kj::mv(persistent)) {} kj::OneOf, kj::StringPtr> ActorState::getId() { KJ_SWITCH_ONEOF(id) { @@ -771,8 +789,8 @@ kj::OneOf, kj::StringPtr> DurableObjectState::getId() KJ_UNREACHABLE; } -jsg::Promise DurableObjectState::blockConcurrencyWhile(jsg::Lock& js, - jsg::Function()> callback) { +jsg::Promise> DurableObjectState::blockConcurrencyWhile(jsg::Lock& js, + jsg::Function>()> callback) { return IoContext::current().blockConcurrencyWhile(js, kj::mv(callback)); } @@ -901,7 +919,7 @@ kj::Maybe DurableObjectState::getWebSocketAutoResponseTimestamp(jsg::R return ws->getAutoResponseTimestamp(); } -kj::Array serializeV8Value(jsg::Lock& js, v8::Local value) { +kj::Array serializeV8Value(jsg::Lock& js, const jsg::JsValue& value) { jsg::Serializer serializer(js, jsg::Serializer::Options { .version = 15, .omitHeader = false, @@ -911,9 +929,9 @@ kj::Array serializeV8Value(jsg::Lock& js, v8::Local value) return kj::mv(released.data); } -v8::Local deserializeV8Value(jsg::Lock& js, - kj::ArrayPtr key, - kj::ArrayPtr buf) { +jsg::JsValue deserializeV8Value(jsg::Lock& js, + kj::ArrayPtr key, + kj::ArrayPtr buf) { KJ_ASSERT(buf.size() > 0, "unexpectedly empty value buffer", key); try { @@ -921,7 +939,7 @@ v8::Local deserializeV8Value(jsg::Lock& js, // additional try/catch in case the js.tryCatch hits an exception that is // terminal for the isolate, causing exception to be rethrown, in which case // we throw a kj::Exception wrapping a jsg.Error. - return js.tryCatch([&]() -> v8::Local { + return js.tryCatch([&]() -> jsg::JsValue { jsg::Deserializer::Options options {}; if (buf[0] != 0xFF) { // When Durable Objects was first released, it did not properly write headers when serializing @@ -934,8 +952,8 @@ v8::Local deserializeV8Value(jsg::Lock& js, jsg::Deserializer deserializer(js, buf, nullptr, nullptr, options); - return deserializer.readValue(); - }, [&](jsg::Value&& exception) mutable -> v8::Local { + return jsg::JsValue(deserializer.readValue()); + }, [&](jsg::Value&& exception) mutable -> jsg::JsValue { // If we do hit a deserialization error, we log information that will be helpful in // understanding the problem but that won't leak too much about the customer's data. We // include the key (to help find the data in the database if it hasn't been deleted), the diff --git a/src/workerd/api/actor-state.h b/src/workerd/api/actor-state.h index 301808c6b9d..56a0612c67a 100644 --- a/src/workerd/api/actor-state.h +++ b/src/workerd/api/actor-state.h @@ -11,7 +11,6 @@ #include #include #include -#include #include namespace workerd::api { @@ -21,9 +20,9 @@ class SqlStorage; class DurableObjectId; class WebSocket; -kj::Array serializeV8Value(jsg::Lock& js, v8::Local value); +kj::Array serializeV8Value(jsg::Lock& js, const jsg::JsValue& value); -v8::Local deserializeV8Value( +jsg::JsValue deserializeV8Value( jsg::Lock& js, kj::ArrayPtr key, kj::ArrayPtr buf); class DurableObjectStorageOperations { @@ -45,9 +44,10 @@ class DurableObjectStorageOperations { JSG_STRUCT_TS_OVERRIDE(DurableObjectGetOptions); // Rename from DurableObjectStorageOperationsGetOptions }; - jsg::Promise get(jsg::Lock& js, - kj::OneOf> keys, - jsg::Optional options); + jsg::Promise> get( + jsg::Lock& js, + kj::OneOf> keys, + jsg::Optional options); struct GetAlarmOptions { jsg::Optional allowConcurrency; @@ -79,7 +79,7 @@ class DurableObjectStorageOperations { JSG_STRUCT_TS_OVERRIDE(DurableObjectListOptions); // Rename from DurableObjectStorageOperationsListOptions }; - jsg::Promise list(jsg::Lock& js, jsg::Optional options); + jsg::Promise> list(jsg::Lock& js, jsg::Optional options); struct PutOptions { jsg::Optional allowConcurrency; @@ -98,9 +98,10 @@ class DurableObjectStorageOperations { }; jsg::Promise put(jsg::Lock& js, - kj::OneOf>> keyOrEntries, - jsg::Optional> value, jsg::Optional options, - const jsg::TypeHandler& optionsTypeHandler); + kj::OneOf> keyOrEntries, + jsg::Optional value, + jsg::Optional options, + const jsg::TypeHandler& optionsTypeHandler); kj::OneOf, jsg::Promise> delete_( jsg::Lock& js, @@ -160,16 +161,18 @@ class DurableObjectStorageOperations { } private: - jsg::Promise getOne(jsg::Lock& js, kj::String key, const GetOptions& options); - jsg::Promise getMultiple(jsg::Lock& js, - kj::Array keys, - const GetOptions& options); + jsg::Promise> getOne(jsg::Lock& js, + kj::String key, + const GetOptions& options); + jsg::Promise> getMultiple(jsg::Lock& js, + kj::Array keys, + const GetOptions& options); jsg::Promise putOne(jsg::Lock& js, kj::String key, - v8::Local value, + jsg::JsValue value, const PutOptions& options); - jsg::Promise putMultiple(jsg::Lock& js, jsg::Dict> entries, + jsg::Promise putMultiple(jsg::Lock& js, jsg::Dict entries, const PutOptions& options); jsg::Promise deleteOne(jsg::Lock& js, kj::String key, const PutOptions& options); @@ -196,11 +199,15 @@ class DurableObjectStorage: public jsg::Object, public DurableObjectStorageOpera // Omit from definitions }; - jsg::Promise transaction(jsg::Lock& js, - jsg::Function(jsg::Ref)> closure, + jsg::Promise> transaction( + jsg::Lock& js, + jsg::Function>( + jsg::Ref)> closure, jsg::Optional options); - jsg::Value transactionSync(jsg::Lock& js, jsg::Function callback); + jsg::JsRef transactionSync( + jsg::Lock& js, + jsg::Function()> callback); jsg::Promise deleteAll(jsg::Lock& js, jsg::Optional options); @@ -343,13 +350,14 @@ class ActorState: public jsg::Object { // TODO(cleanup): Remove getPersistent method that isn't supported for colo-local actors anymore. public: - ActorState(Worker::Actor::Id actorId, kj::Maybe transient, - kj::Maybe> persistent); + ActorState(Worker::Actor::Id actorId, + kj::Maybe> transient, + kj::Maybe> persistent); kj::OneOf, kj::StringPtr> getId(); - jsg::Optional> getTransient(jsg::Lock& js) { - return transient.map([&](jsg::Value& v) { return v.getHandle(js); }); + jsg::Optional getTransient(jsg::Lock& js) { + return transient.map([&](jsg::JsRef& v) { return v.getHandle(js); }); } jsg::Optional> getPersistent() { @@ -366,7 +374,7 @@ class ActorState: public jsg::Object { private: Worker::Actor::Id id; - kj::Maybe transient; + kj::Maybe> transient; kj::Maybe> persistent; }; @@ -406,8 +414,9 @@ class DurableObjectState: public jsg::Object { return storage.map([&](jsg::Ref& p) { return p.addRef(); }); } - jsg::Promise blockConcurrencyWhile(jsg::Lock& js, - jsg::Function()> callback); + jsg::Promise> blockConcurrencyWhile( + jsg::Lock& js, + jsg::Function>()> callback); void abort(jsg::Optional reason); // Reset the object, including breaking the output gate and canceling any writes that haven't diff --git a/src/workerd/api/basics.c++ b/src/workerd/api/basics.c++ index 78093b90e9a..90c166765b7 100644 --- a/src/workerd/api/basics.c++ +++ b/src/workerd/api/basics.c++ @@ -567,7 +567,9 @@ jsg::Optional> ExtendableEvent::getActorState() { auto& lock = context.getCurrentLock(); auto persistent = actor.makeStorageForSwSyntax(lock); return jsg::alloc( - actor.cloneId(), actor.getTransient(lock), kj::mv(persistent)); + actor.cloneId(), + actor.getTransient(lock), + kj::mv(persistent)); }); } diff --git a/src/workerd/io/worker.c++ b/src/workerd/io/worker.c++ index 934c63ccb7e..38c82733d85 100644 --- a/src/workerd/io/worker.c++ +++ b/src/workerd/io/worker.c++ @@ -2794,7 +2794,7 @@ struct Worker::Actor::Impl { kj::Own metrics; - kj::Maybe transient; + kj::Maybe> transient; kj::Maybe> actorCache; struct NoClass {}; @@ -2941,7 +2941,7 @@ struct Worker::Actor::Impl { js.withinHandleScope([&] { auto contextScope = js.enterContextScope(lock.getContext()); if (hasTransient) { - transient.emplace(js.v8Isolate, v8::Object::New(js.v8Isolate)); + transient.emplace(js, js.obj()); } actorCache = makeActorCache(self.worker->getIsolate().impl->actorCacheLru, outputGate, hooks); @@ -3120,9 +3120,11 @@ Worker::Actor::Id Worker::Actor::cloneId() { return cloneId(impl->actorId); } -kj::Maybe Worker::Actor::getTransient(Worker::Lock& lock) { +kj::Maybe> Worker::Actor::getTransient(Worker::Lock& lock) { KJ_REQUIRE(&lock.getWorker() == worker.get()); - return impl->transient.map([&](jsg::Value& val) { return val.addRef(lock.getIsolate()); }); + return impl->transient.map([&](jsg::JsRef& val) { + return val.addRef(lock); + }); } kj::Maybe Worker::Actor::getPersistent() { diff --git a/src/workerd/io/worker.h b/src/workerd/io/worker.h index c876f9392dc..242d2e46855 100644 --- a/src/workerd/io/worker.h +++ b/src/workerd/io/worker.h @@ -732,7 +732,7 @@ class Worker::Actor final: public kj::Refcounted { const Id& getId(); Id cloneId(); static Id cloneId(Id& id); - kj::Maybe getTransient(Worker::Lock& lock); + kj::Maybe> getTransient(Worker::Lock& lock); kj::Maybe getPersistent(); kj::Own getLoopback();