File
Blob: src/workerd/jsg/jsg.h
| 1 | // Copyright (c) 2017-2022 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | #pragma once |
| 6 | // Main public interface to JSG library. |
| 7 | // |
| 8 | // Any files declaring an API to export to JavaScript will need to include this header. |
| 9 | |
| 10 | #include "util.h" |
| 11 | #include "wrappable.h" |
| 12 | |
| 13 | #include <workerd/jsg/exception.h> |
| 14 | #include <workerd/jsg/macro-meta.h> |
| 15 | #include <workerd/jsg/memory.h> |
| 16 | #include <workerd/util/strong-bool.h> |
| 17 | |
| 18 | #include <v8-external-memory-accounter.h> |
| 19 | #include <v8-forward.h> |
| 20 | #include <v8-locker.h> |
| 21 | #include <v8-profiler.h> |
| 22 | #include <v8-regexp.h> |
| 23 | |
| 24 | #include <capnp/schema-loader.h> |
| 25 | #include <kj/debug.h> |
| 26 | #include <kj/exception.h> |
| 27 | #include <kj/function.h> |
| 28 | #include <kj/one-of.h> |
| 29 | #include <kj/string.h> |
| 30 | #include <kj/time.h> |
| 31 | |
| 32 | using kj::byte; |
| 33 | using kj::uint; |
| 34 | |
| 35 | #if _MSC_VER |
| 36 | using ssize_t = long long; |
| 37 | #endif |
| 38 | |
| 39 | namespace workerd::jsg { |
| 40 | kj::String stringifyHandle(v8::Local<v8::Value> value); |
| 41 | } |
| 42 | |
| 43 | namespace v8 { |
| 44 | // Allows v8 handles to be passed to kj::str() as well as KJ_LOG and related macros. |
| 45 | template <workerd::jsg::V8Value T> |
| 46 | kj::String KJ_STRINGIFY(v8::Local<T> value) { |
| 47 | return workerd::jsg::stringifyHandle(value); |
| 48 | } |
| 49 | } // namespace v8 |
| 50 | |
| 51 | namespace workerd::jsg { |
| 52 | |
| 53 | // ======================================================================================= |
| 54 | // Macros for declaring type glue. |
| 55 | |
| 56 | #define JSG_RESOURCE_TYPE(Type, ...) \ |
| 57 | static constexpr ::workerd::jsg::JsgKind JSG_KIND KJ_UNUSED = ::workerd::jsg::JsgKind::RESOURCE; \ |
| 58 | using jsgSuper = jsgThis; \ |
| 59 | using jsgThis = Type; \ |
| 60 | inline kj::StringPtr jsgGetMemoryName() const override { \ |
| 61 | return #Type##_kjc; \ |
| 62 | } \ |
| 63 | inline size_t jsgGetMemorySelfSize() const override { \ |
| 64 | return sizeof(Type); \ |
| 65 | } \ |
| 66 | inline void jsgGetMemoryInfo(jsg::MemoryTracker& tracker) const override { \ |
| 67 | const Type* self = static_cast<const Type*>(this); \ |
| 68 | jsgSuper::jsgGetMemoryInfo(tracker); \ |
| 69 | ::workerd::jsg::visitSubclassForMemoryInfo<Type>(self, tracker); \ |
| 70 | } \ |
| 71 | template <typename> \ |
| 72 | friend constexpr bool ::workerd::jsg::resourceNeedsGcTracing(); \ |
| 73 | template <typename T> \ |
| 74 | friend void ::workerd::jsg::visitSubclassForGc(T* obj, ::workerd::jsg::GcVisitor& visitor); \ |
| 75 | inline void jsgVisitForGc(::workerd::jsg::GcVisitor& visitor) override { \ |
| 76 | jsgSuper::jsgVisitForGc(visitor); \ |
| 77 | ::workerd::jsg::visitSubclassForGc<Type>(this, visitor); \ |
| 78 | } \ |
| 79 | static void jsgConfiguration(__VA_ARGS__); \ |
| 80 | template <typename Registry, typename Self> \ |
| 81 | static void registerMembers(Registry& registry, ##__VA_ARGS__) |
| 82 | // Begins a block nested inside a C++ class to declare how that class should be accessible in |
| 83 | // JavaScript. JSG_RESOURCE_TYPE declares that the class is a "resource type" in KJ parlance. |
| 84 | // |
| 85 | // https://github.com/sandstorm-io/capnproto/blob/master/style-guide.md#value-types-vs-resource-types |
| 86 | // |
| 87 | // In short, this means that the type is normally passed by reference, and that when JavaScript |
| 88 | // code accesses members of the type, it calls back into C++. This differs from value types, which |
| 89 | // are normally deep-copied into JavaScript objects such that C++ is no longer involved. |
| 90 | // |
| 91 | // Example usage: |
| 92 | // |
| 93 | // class MyApiType: public jsg::Object { |
| 94 | // // Some type we want to expose to JavaScript. |
| 95 | // public: |
| 96 | // static jsg::Ref<MyType> constructor(bool b, kj::String s); |
| 97 | // // Called when JavaScript invokes `new MyType()`. The name `constructor` is special. |
| 98 | // // If you do not declare a constructor, then attempts to construct the type from |
| 99 | // // JavaScript will throw an exception, but you'll still be able to construct it in C++ |
| 100 | // // using the regular C++ constructor(s). |
| 101 | // |
| 102 | // void foo(int i, kj::String str); |
| 103 | // double bar(); |
| 104 | // // Methods that can be called from JavaScript. |
| 105 | // |
| 106 | // kj::StringPtr getBaz(); |
| 107 | // void setBaz(kj::String value); |
| 108 | // // Methods implementing a property. |
| 109 | // |
| 110 | // JSG_RESOURCE_TYPE(MyApiType) { |
| 111 | // JSG_METHOD(foo); |
| 112 | // JSG_METHOD(bar); |
| 113 | // JSG_INSTANCE_PROPERTY(baz, getBaz, setBaz); |
| 114 | // } |
| 115 | // |
| 116 | // private: |
| 117 | // void visitForGc(jsg::GcVisitor visitor); |
| 118 | // // If this type contains any Ref or Value objects, it must implement visitForGc(), and when |
| 119 | // // this is called, it must call `visitor.visit()` on all handles that it knows about. If |
| 120 | // // the object doesn't hold any JS handles then it need not implement this. See the |
| 121 | // // definition of GcVisitor, below, for more information. |
| 122 | // |
| 123 | // jsg::Value someValue; |
| 124 | // jsg::Ref<MyOtherApiType> someOtherResourceObject; |
| 125 | // jsg::V8Ref<v8::Map> someState; |
| 126 | // // Objects of resource type may be destroyed outside of the isolate lock. Therefore, if you |
| 127 | // // need to hold a reference to a V8 object in a resource type, you should use one of these |
| 128 | // // classes / class templates. In particular, holding a raw v8::Global<T> may result in |
| 129 | // // undefined behavior upon destruction. |
| 130 | // }; |
| 131 | // |
| 132 | // Notice that method parameters and return types are automatically converted between C++ and |
| 133 | // JavaScript. You specify the full set of types that your JavaScript execution environment will |
| 134 | // support when you declare your Isolate (usually in high-level code). |
| 135 | // |
| 136 | // Additionally, the following types are always supported: |
| 137 | // - C++ double, int <-> JS Number |
| 138 | // - C++ kj::Date <-> JS Date in return position, JS Date or millisecond unix epoch as argument |
| 139 | // - C++ kj::String, kj::StringPtr <-> JS String |
| 140 | // - C++ kj::Maybe<T> <-> JS null or T |
| 141 | // - C++ jsg::Optional<T> <-> JS undefined or T |
| 142 | // - C++ jsg::LenientOptional<T> <-> JS undefined or T (treats type errors as JS undefined) |
| 143 | // - C++ kj::OneOf<T, U, ...> <-> JS T or U or ... |
| 144 | // - C++ kj::Array<T> <-> JS Array of T |
| 145 | // - C++ kj::Array<byte> <-> JS ArrayBuffer |
| 146 | // - C++ jsg::Dict<T> <-> JS Object used as a map of strings to values of type T |
| 147 | // - C++ jsg::Function<T(U, V, ...)> <-> JS Function |
| 148 | // - C++ jsg::Promise<T> <-> JS Promise |
| 149 | // - C++ jsg::Ref<T> <-> JavaScript resource type |
| 150 | // - C++ v8::Local<T> <-> JavaScript value |
| 151 | // |
| 152 | // There is also some magic. If the first parameter to a method has type |
| 153 | // `const v8::FunctionCallbackInfo<v8::Value>&`, then it will receive the FunctionCallbackInfo |
| 154 | // as passed from V8. (For property accessors, this should be PropertyCallbackInfo instead.) This |
| 155 | // gives you an escape hatch by which you can directly access the V8 context when needed. In this |
| 156 | // case the second parameter to your method will correspond to the first parameter passed from |
| 157 | // JavaScript. |
| 158 | // |
| 159 | // As another piece of magic, you can add some special types to the end of your parameter list in |
| 160 | // order to receive functionality from the JavaScript environment itself. These parameters will not |
| 161 | // actually correspond to JavaScript parameters, and should always be placed at the end of the |
| 162 | // argument list. They are: |
| 163 | // |
| 164 | // - const jsg::TypeHandler<T>&: Provides callback which can be used to convert between V8 handles |
| 165 | // and a C++ object of type T, and (for resource types) to allocate objects of type T on the V8 |
| 166 | // heap. The reference is valid only until your method returns. |
| 167 | // - `v8::Isolate*`: Receives the V8 isolate pointer. |
| 168 | // |
| 169 | // In yet more magic, you can add a single configuration parameter to the JSG_RESOURCE_TYPE macro: |
| 170 | // |
| 171 | // class MyApiType: ... { |
| 172 | // public: |
| 173 | // JSG_RESOURCE_TYPE(MyApiType, uint apiVersion) { |
| 174 | // if (apiVersion > 42) { |
| 175 | // using namespace newapi; |
| 176 | // JSG_NESTED_TYPE(Widget); |
| 177 | // } else { |
| 178 | // using namespace oldapi; |
| 179 | // JSG_NESTED_TYPE(Widget); |
| 180 | // } |
| 181 | // } |
| 182 | // }; |
| 183 | // |
| 184 | // Populate the configuration parameter by passing it to the JSG isolate's constructor (the type |
| 185 | // declared by JSG_DECLARE_ISOLATE_TYPE in setup.h). |
| 186 | // |
| 187 | // Different resource types may have different configuration types. However, all the configuration |
| 188 | // types must be constructable from a single "meta" configuration type, which is the type of the |
| 189 | // configuration passed to the JSG isolate's constructor. |
| 190 | |
| 191 | // Use inside a JSG_RESOURCE_TYPE to declare that the resource type itself can be invoked as |
| 192 | // a function. |
| 193 | #define JSG_CALLABLE(name) \ |
| 194 | do { \ |
| 195 | registry.template registerCallable<decltype(&Self::name), &Self::name>(); \ |
| 196 | } while (false) |
| 197 | |
| 198 | // Use inside a JSG_RESOURCE_TYPE block to declare that the given method should be callable from |
| 199 | // JavaScript on instances of the resource type. |
| 200 | #define JSG_METHOD(name) \ |
| 201 | do { \ |
| 202 | static const char NAME[] = #name; \ |
| 203 | registry.template registerMethod<NAME, &Self::name>(); \ |
| 204 | } while (false) |
| 205 | |
| 206 | // Like JSG_METHOD but allows you to specify a different name to use in JavaScript. This is |
| 207 | // particularly useful when a JavaScript API wants to use a name that is a keyword in C++. For |
| 208 | // example: |
| 209 | // |
| 210 | // JSG_METHOD_NAMED(delete, delete_); |
| 211 | #define JSG_METHOD_NAMED(name, method) \ |
| 212 | do { \ |
| 213 | static const char NAME[] = #name; \ |
| 214 | registry.template registerMethod<NAME, &Self::method>(); \ |
| 215 | } while (false) |
| 216 | |
| 217 | // Use inside a JSG_RESOURCE_TYPE block to declare that the given method should be callable from |
| 218 | // JavaScript on the resource type's constructor. |
| 219 | #define JSG_STATIC_METHOD(name) \ |
| 220 | do { \ |
| 221 | static const char NAME[] = #name; \ |
| 222 | registry.template registerStaticMethod<NAME, decltype(Self::name), &Self::name>(); \ |
| 223 | } while (false) |
| 224 | |
| 225 | // Like JSG_METHOD_NAMED, but for static methods. |
| 226 | #define JSG_STATIC_METHOD_NAMED(name, method) \ |
| 227 | do { \ |
| 228 | static const char NAME[] = #name; \ |
| 229 | registry.template registerStaticMethod<NAME, decltype(Self::method), &Self::method>(); \ |
| 230 | } while (false) |
| 231 | |
| 232 | // Use inside a JSG_RESOURCE_TYPE block to make objects of this type iterable. Pass in the name of |
| 233 | // a method returning an object satisfying the requirements of a JavaScript iterator. Note that this |
| 234 | // will NOT automatically register the method for you -- you still need to use JSG_METHOD{,_NAMED} |
| 235 | // if you plan to expose the method to JavaScript. For example: |
| 236 | // |
| 237 | // struct Iterable { |
| 238 | // static Iterable constructor(); |
| 239 | // Iterator entries(); |
| 240 | // JSG_RESOURCE_TYPE { |
| 241 | // JSG_ITERABLE(entries); |
| 242 | // } |
| 243 | // }; |
| 244 | // |
| 245 | // will allow a resource type to be iterated over, but not its entries() function to be called. |
| 246 | // |
| 247 | // for (let x of new Iterable()) { /* ... */ } // GOOD |
| 248 | // for (let x of new Iterable().entries()) { /* ... */ } // BAD! |
| 249 | // |
| 250 | // To enable the latter case, you would need to use JSG_METHOD(entries), and make Iterator itself |
| 251 | // iterable. |
| 252 | #define JSG_ITERABLE(method) \ |
| 253 | do { \ |
| 254 | static const char NAME[] = #method; \ |
| 255 | registry.template registerIterable<NAME, decltype(&Self::method), &Self::method>(); \ |
| 256 | } while (false) |
| 257 | |
| 258 | // Use inside a JSG_RESOURCE_TYPE block to make objects of this type async iterable. Pass in the |
| 259 | // name of a method returning a kj::Promise for an object satisfying the requirements of a |
| 260 | // JavaScript iterator. |
| 261 | #define JSG_ASYNC_ITERABLE(method) \ |
| 262 | do { \ |
| 263 | static const char NAME[] = #method; \ |
| 264 | registry.template registerAsyncIterable<NAME, decltype(&Self::method), &Self::method>(); \ |
| 265 | } while (false) |
| 266 | |
| 267 | // JSG_DISPOSE and JSG_ASYNC_DISPOSE are used to make an object compatible with the |
| 268 | // JavaScript using and await using keywords (respectively). These allow variables to |
| 269 | // be defined in such a way that they will have their disposer functions automatically |
| 270 | // called when the variable goes out of scope, for instance: |
| 271 | // |
| 272 | // class Foo { [Symbol.dispose]() { console.log('...'); }} |
| 273 | // { using foo = new Foo(); } |
| 274 | // |
| 275 | // When the containing block exits, the [Symbol.dispose] function will be called on |
| 276 | // the object, allowing cleanup actions to be performed. |
| 277 | // |
| 278 | // There are a number of guidelines that should be followed when implementing |
| 279 | // disposer methods: |
| 280 | // |
| 281 | // 1. Always prefer Symbol.dispose over Symbol.asyncDispose, avoid defining both. |
| 282 | // 2. Always assume that if the resource needs to be disposed, it's being disposed |
| 283 | // in an exception case. Clean disposal should always be explicit. |
| 284 | // 3. At least for the time being, the exception will not be available to the |
| 285 | // disposer, so it will not be able to propagate the error |
| 286 | // 4. Always implement disposal as an idempotent operation and remember that |
| 287 | // users can call the disposer methods directly as many times as they want. |
| 288 | // 5. Remember that errors thrown from within the disposer will mask the original |
| 289 | // error using a SupressedError. |
| 290 | #define JSG_DISPOSE(method) \ |
| 291 | do { \ |
| 292 | static const char NAME[] = #method; \ |
| 293 | registry.template registerDispose<NAME, decltype(&Self::method), &Self::method>(); \ |
| 294 | } while (false) |
| 295 | #define JSG_ASYNC_DISPOSE(method) \ |
| 296 | do { \ |
| 297 | static const char NAME[] = #method; \ |
| 298 | registry.template registerAsyncDispose<NAME, decltype(&Self::method), &Self::method>(); \ |
| 299 | } while (false) |
| 300 | |
| 301 | // Use inside a JSG_RESOURCE_TYPE block to declare a property on this object that should be |
| 302 | // accessible to JavaScript. `name` is the JavaScript member name, while `getter` and `setter` are |
| 303 | // the names of C++ methods that get and set this property. |
| 304 | // |
| 305 | // WARNING: This is usually not what you want. Usually you want JSG_PROTOTYPE_PROPERTY instead. |
| 306 | // Note that V8 implements instance properties by modifying the instance immediately after |
| 307 | // construction, which is inefficient and can break some optimizations. For example, any object |
| 308 | // with an instance property will not be possible to collect during minor GCs, only major GCs. |
| 309 | // Prototype properties are on the prototype, so have no runtime overhead until they are used. |
| 310 | #define JSG_INSTANCE_PROPERTY(name, getter, setter) \ |
| 311 | do { \ |
| 312 | static const char NAME[] = #name; \ |
| 313 | registry.template registerInstanceProperty<NAME, decltype(&Self::getter), &Self::getter, \ |
| 314 | decltype(&Self::setter), &Self::setter>(); \ |
| 315 | } while (false) |
| 316 | |
| 317 | // Use inside a JSG_RESOURCE_TYPE block to declare a property on this object's prototype that |
| 318 | // should be accessible to JavaScript. `name` is the JavaScript member name, while `getter` and |
| 319 | // `setter` are the names of C++ methods that get and set this property. |
| 320 | // |
| 321 | // The key difference between JSG_INSTANCE_PROPERTY and JSG_PROTOTYPE_PROPERTY is in exactly how |
| 322 | // the getters and setters are attached to created JavaScript object. Specifically, |
| 323 | // JSG_INSTANCE_PROPERTY is similar to: |
| 324 | // |
| 325 | // class Foo1 { |
| 326 | // constructor() { |
| 327 | // Object.defineProperty(this, 'bar', { |
| 328 | // get() { /* ... */ }, |
| 329 | // set(v) { /* ... */ }, |
| 330 | // }); |
| 331 | // } |
| 332 | // } |
| 333 | // |
| 334 | // Whereas JSG_PROTOTYPE_PROPERTY is equivalent to: |
| 335 | // |
| 336 | // class Foo2 { |
| 337 | // get bar() { /* ... */ } |
| 338 | // set bar(v) { /* ... */ } |
| 339 | // } |
| 340 | // |
| 341 | // The difference here is important because, in the former case, the properties are |
| 342 | // defined directly on *instances* of Foo1 as own properties, while in latter case, |
| 343 | // the properties are defined on the prototype of all Foo2 instances. In the former |
| 344 | // case, using JSG_INSTANCE_PROPERTY, the properties are directly enumerable on all instances |
| 345 | // of Foo1 such that calling Object.keys(new Foo1()) returns ['bar']. However, calling |
| 346 | // Object.keys(new Foo2()) will return an empty array [] as prototype properties are |
| 347 | // not directly enumerable. |
| 348 | // |
| 349 | // However, that's not the only critical difference. Because instance properties take |
| 350 | // precedence over prototype properties, Foo1 is not properly subclassable. If I did: |
| 351 | // |
| 352 | // class MyFoo1 extends Foo1 { |
| 353 | // get bar() { /** .. **/ } |
| 354 | // } |
| 355 | // |
| 356 | // const myFoo1 = new MyFoo1(); |
| 357 | // console.log(myFoo1.bar); |
| 358 | // |
| 359 | // The getter specified in the constructor of Foo1 would be called rather than the |
| 360 | // getter defined in the MyFoo1 class, which is not what a user would expect! |
| 361 | // This means that any resource type that uses JSG_INSTANCE_PROPERTY to attach properties |
| 362 | // will not be properly subclassable. To allow subclasses to work correctly, use |
| 363 | // JSG_PROTOTYPE_PROPERTY instead. |
| 364 | #define JSG_PROTOTYPE_PROPERTY(name, getter, setter) \ |
| 365 | do { \ |
| 366 | static const char NAME[] = #name; \ |
| 367 | registry.template registerPrototypeProperty<NAME, decltype(&Self::getter), &Self::getter, \ |
| 368 | decltype(&Self::setter), &Self::setter>(); \ |
| 369 | } while (false) |
| 370 | |
| 371 | // Like JSG_INSTANCE_PROPERTY but creates a property that will throw an exception if |
| 372 | // JavaScript tries to assign to it. |
| 373 | #define JSG_READONLY_INSTANCE_PROPERTY(name, getter) \ |
| 374 | do { \ |
| 375 | static const char NAME[] = #name; \ |
| 376 | registry.template registerReadonlyInstanceProperty<NAME, decltype(&Self::getter), \ |
| 377 | &Self::getter>(); \ |
| 378 | } while (false) |
| 379 | |
| 380 | // Like JSG_PROTOTYPE_PROPERTY but creates a property that will throw an exception if JavaScript |
| 381 | // tries to assign to it. |
| 382 | #define JSG_READONLY_PROTOTYPE_PROPERTY(name, getter) \ |
| 383 | do { \ |
| 384 | static const char NAME[] = #name; \ |
| 385 | registry.template registerReadonlyPrototypeProperty<NAME, decltype(&Self::getter), \ |
| 386 | &Self::getter>(); \ |
| 387 | } while (false) |
| 388 | |
| 389 | // A lazy property will call the getter the first time the property is access but will then |
| 390 | // replace the property definition with a normal instance property using the returned value. |
| 391 | // Keep in mind that, as an instance property, these lazily set properties cannot be overridden |
| 392 | // by subclasses. They are set directly on the instance object itself when it is created. |
| 393 | #define JSG_LAZY_INSTANCE_PROPERTY(name, getter) \ |
| 394 | do { \ |
| 395 | static const char NAME[] = #name; \ |
| 396 | registry.template registerLazyInstanceProperty<NAME, decltype(&Self::getter), &Self::getter, \ |
| 397 | false>(); \ |
| 398 | } while (false) |
| 399 | |
| 400 | #define JSG_LAZY_READONLY_INSTANCE_PROPERTY(name, getter) \ |
| 401 | do { \ |
| 402 | static const char NAME[] = #name; \ |
| 403 | registry.template registerLazyInstanceProperty<NAME, decltype(&Self::getter), &Self::getter, \ |
| 404 | true>(); \ |
| 405 | } while (false) |
| 406 | |
| 407 | // Use inside a JSG_RESOURCE_TYPE block to declare a property that should be shown when calling |
| 408 | // `node:util`'s `inspect()` function on values of this type. These properties will be shown when |
| 409 | // `console.log()`ing too, and should be used to expose internal state useful for debugging. |
| 410 | // `name` is the name of the property (displayed in square brackets), while `getter` is the name of |
| 411 | // the C++ method that gets this property's value. |
| 412 | #define JSG_INSPECT_PROPERTY(name, getter) \ |
| 413 | do { \ |
| 414 | static const char NAME[] = #name; \ |
| 415 | registry.template registerInspectProperty<NAME, decltype(&Self::getter), &Self::getter>(); \ |
| 416 | } while (false) |
| 417 | |
| 418 | // Use inside a JSG_RESOURCE_TYPE to expose a static property on the JavaScript constructor. |
| 419 | // The property will be read-only and will call the specified static function when accessed. |
| 420 | // The function should take no parameters or take jsg::Lock& as the first parameter. |
| 421 | // Example: |
| 422 | // static int getVersion() { return 42; } |
| 423 | // JSG_RESOURCE_TYPE(MyClass) { |
| 424 | // JSG_STATIC_READONLY_PROPERTY(getVersion); // Exposes as MyClass.getVersion |
| 425 | // } |
| 426 | #define JSG_STATIC_READONLY_PROPERTY(name) \ |
| 427 | do { \ |
| 428 | static const char NAME[] = #name; \ |
| 429 | registry.template registerStaticProperty<NAME, decltype(Self::name), &Self::name>(); \ |
| 430 | } while (false) |
| 431 | |
| 432 | // Use inside a JSG_RESOURCE_TYPE to expose a static property with a different name than the |
| 433 | // underlying C++ function. The property will be read-only and will call the specified static |
| 434 | // getter function when accessed. The getter can optionally take jsg::Lock& as the first parameter. |
| 435 | // Example: |
| 436 | // static kj::Array<kj::String> getSupportedTypes() { ... } |
| 437 | // JSG_RESOURCE_TYPE(MyClass) { |
| 438 | // JSG_STATIC_READONLY_PROPERTY_NAMED(supportedTypes, getSupportedTypes); // MyClass.supportedTypes |
| 439 | // } |
| 440 | #define JSG_STATIC_READONLY_PROPERTY_NAMED(name, getter) \ |
| 441 | do { \ |
| 442 | static const char NAME[] = #name; \ |
| 443 | registry.template registerStaticProperty<NAME, decltype(Self::getter), &Self::getter>(); \ |
| 444 | } while (false) |
| 445 | |
| 446 | // Use inside a JSG_RESOURCE_TYPE to create a static constant member on the constructor and |
| 447 | // prototype of this object. Only primitive data types (booleans, strings, numbers) are allowed. |
| 448 | // Unlike the JSG_INSTANCE_PROPERTY and JSG_READONLY_PROPERTY macros, this does not use a getter |
| 449 | // -- it expects a static constexpr member of a primitive type available in the class by the same |
| 450 | // name. For example: |
| 451 | // |
| 452 | // struct Interface { |
| 453 | // static Interface constructor() |
| 454 | // static constexpr int FOO_BAR = 123; |
| 455 | // JSG_RESOURCE_TYPE { |
| 456 | // JSG_STATIC_CONSTANT(FOO_BAR); |
| 457 | // } |
| 458 | // }; |
| 459 | // |
| 460 | // will allow all of the following JS expressions to hold true: |
| 461 | // |
| 462 | // Interface.FOO_BAR === 123 |
| 463 | // Interface.prototype.FOO_BAR === 123 |
| 464 | // new Interface().FOO_BAR === 123 |
| 465 | // Object.getPrototypeOf(new Interface()).FOO_BAR === 123 |
| 466 | // |
| 467 | // This is useful to implement constant interface members as specified in Web IDL: |
| 468 | // https://heycam.github.io/webidl/#idl-constants |
| 469 | // |
| 470 | // TODO(someday): This should probably also support the null JS value. |
| 471 | #define JSG_STATIC_CONSTANT(name) \ |
| 472 | do { \ |
| 473 | static const char NAME[] = #name; \ |
| 474 | registry.template registerStaticConstant<NAME, decltype(Self::name)>(Self::name); \ |
| 475 | } while (false) |
| 476 | |
| 477 | // This works the same as JSG_STATIC_CONSTANT but allows us to provide an alias to an arbitrary c++ |
| 478 | // constant instead. For example: |
| 479 | // struct Interface { |
| 480 | // static Interface constructor() |
| 481 | // JSG_RESOURCE_TYPE { |
| 482 | // JSG_STATIC_CONSTANT_NAMED(FOO_BAR, SOME_SYSTEM_CONSTANT); |
| 483 | // } |
| 484 | // }; |
| 485 | #define JSG_STATIC_CONSTANT_NAMED(name, constant) \ |
| 486 | do { \ |
| 487 | static const char NAME[] = #name; \ |
| 488 | registry.template registerStaticConstant<NAME, decltype(constant)>(constant); \ |
| 489 | } while (false) |
| 490 | |
| 491 | // Use inside a JSG_RESOURCE_TYPE block to declare that this type inherits from another type, |
| 492 | // which must also have a JSG_RESOURCE_TYPE block. This type must singly, non-virtually inherit |
| 493 | // from the specified type. (Multiple inheritance and virtual inheritance will not work since we |
| 494 | // rely on the pointer to the superclass and the subclass having the same numeric value.) |
| 495 | #define JSG_INHERIT(Type) \ |
| 496 | static_assert(kj::canConvert<Self&, Type&>(), #Type " is not a superclass of this"); \ |
| 497 | registry.template registerInherit<Type>() |
| 498 | |
| 499 | // Use inside a JSG_RESOURCE_TYPE block to declare that this type inherits from an intrinsic |
| 500 | // prototype. This is primarily useful to inherit from v8::kErrorPrototype, like DOMException, and |
| 501 | // v8::kIteratorPrototype. |
| 502 | #define JSG_INHERIT_INTRINSIC(intrinsic) \ |
| 503 | do { \ |
| 504 | static const char NAME[] = #intrinsic; \ |
| 505 | registry.template registerInheritIntrinsic<NAME>(intrinsic); \ |
| 506 | } while (false) |
| 507 | |
| 508 | // An isDetected() operation which detects if the expression t.getTemplate(isolate, &u) is available |
| 509 | // for instances `t`, `u` of types T and U. |
| 510 | template <typename T, typename U> |
| 511 | using HasGetTemplateOverload = decltype(kj::instance<T&>().getTemplate( |
| 512 | static_cast<v8::Isolate*>(nullptr), static_cast<U*>(nullptr))); |
| 513 | |
| 514 | // Use inside a JSG_RESOURCE_TYPE block to declare that the given type should be visible as a |
| 515 | // static member of this type. Typically, your "global" type would use several of these |
| 516 | // declarations to make other types appear in the global scope. It is not necessary for the types |
| 517 | // to be nested in C++. |
| 518 | #define JSG_NESTED_TYPE(Type) \ |
| 519 | do { \ |
| 520 | /* Note that `Type` may be incomplete here, we should be OK with that. */ \ |
| 521 | static const char NAME[] = #Type; \ |
| 522 | registry.template registerNestedType<Type, NAME>(); \ |
| 523 | } while (false) |
| 524 | |
| 525 | #define JSG_NESTED_TYPE_NAMED(Type, Name) \ |
| 526 | do { \ |
| 527 | /* Note that `Type` may be incomplete here, we should be OK with that. */ \ |
| 528 | static const char NAME[] = #Name; \ |
| 529 | registry.template registerNestedType<Type, NAME>(); \ |
| 530 | } while (false) |
| 531 | |
| 532 | // Adds reflection to a resource type. See PropertyReflection<T> for usage. |
| 533 | #define JSG_REFLECTION(...) \ |
| 534 | static constexpr bool jsgHasReflection = true; \ |
| 535 | template <typename TypeWrapper> \ |
| 536 | void jsgInitReflection(TypeWrapper& wrapper) { \ |
| 537 | jsgSuper::jsgInitReflection(wrapper); \ |
| 538 | wrapper.initReflection(this, __VA_ARGS__); \ |
| 539 | } |
| 540 | |
| 541 | // Declares the type serializable. See jsg::Serializer for usage. |
| 542 | #define JSG_SERIALIZABLE(TAG, ...) \ |
| 543 | static_assert(static_cast<uint>(jsgSuper::jsgSerializeTag) != static_cast<uint>(TAG)); \ |
| 544 | static constexpr auto jsgSerializeTag = TAG; \ |
| 545 | static constexpr decltype(jsgSerializeTag) jsgSerializeOldTags[] = {__VA_ARGS__}; \ |
| 546 | static constexpr auto jsgSerializeOneway = false |
| 547 | |
| 548 | // Like JSG_SERIALIZABLE(), but the type has only a serialize() method and no deserialize(). It |
| 549 | // is expected that the specified tag actually belongs to some other type, so a serialization |
| 550 | // round trip will have the effect of replacing this type with that other type. |
| 551 | // |
| 552 | // Used e.g. for JsRpcTarget, which becomes JsRpcStub after serialization. |
| 553 | #define JSG_ONEWAY_SERIALIZABLE(TAG) \ |
| 554 | static_assert(static_cast<uint>(jsgSuper::jsgSerializeTag) != static_cast<uint>(TAG)); \ |
| 555 | static constexpr auto jsgSerializeTag = TAG; \ |
| 556 | static constexpr decltype(jsgSerializeTag) jsgSerializeOldTags[] = {}; \ |
| 557 | static constexpr auto jsgSerializeOneway = true |
| 558 | |
| 559 | // Declares a wildcard property getter. If a property is requested that isn't already present on |
| 560 | // the object or its prototypes, the wildcard property getter will be given a chance to return the |
| 561 | // property. |
| 562 | // |
| 563 | // WARNING: Be very careful about the property named "then". If it exists and is a function, V8 |
| 564 | // will treat your type as a custom thenable, i.e. as a kind of Promise, which means among other |
| 565 | // things that any time a Promise would resolve to it, it will try to chain with it. You should |
| 566 | // probably return kj::none when "then" is requested. |
| 567 | // |
| 568 | // Example: |
| 569 | // |
| 570 | // struct MyType { |
| 571 | // // Get the value of the named dynamic property. Returns none if the property doesn't exist. |
| 572 | // // `SomeType` can be any type that JSG is able to convert to JavaScript. |
| 573 | // kj::Maybe<SomeType> getWildcard(jsg::Lock& js, kj::StringPtr name); |
| 574 | // |
| 575 | // JSG_RESOURCE_TYPE(MyType) { |
| 576 | // JSG_WILDCARD_PROPERTY(getWildcard); |
| 577 | // } |
| 578 | // }; |
| 579 | #define JSG_WILDCARD_PROPERTY(method) \ |
| 580 | do { \ |
| 581 | registry.template registerWildcardProperty<Self, decltype(&Self::method), &Self::method>(); \ |
| 582 | } while (false) |
| 583 | |
| 584 | // Use inside a JSG_RESOURCE_TYPE block to declare that this type should be considered a "root" for |
| 585 | // the purposes of automatically generating TypeScript definitions. All "root" types and their |
| 586 | // recursively referenced types (e.g. method parameter/return types, property types, inherits, etc) |
| 587 | // will be included in the generated TypeScript. See the `## TypeScript` section of the JSG README.md |
| 588 | // for more details. |
| 589 | #define JSG_TS_ROOT() registry.registerTypeScriptRoot() |
| 590 | |
| 591 | // Use inside a JSG_RESOURCE_TYPE block to customise the generated TypeScript definition for this type. |
| 592 | // This macro accepts a single override parameter containing a partial TypeScript statement definition. |
| 593 | // Varargs are accepted so that overrides can contain `,` outside of balanced brackets. See the |
| 594 | // `## TypeScript` section of the JSG README.md for many more details and examples. |
| 595 | // |
| 596 | // The varargs are stringified directly with `#__VA_ARGS__` to capture the user's source verbatim; |
| 597 | // see the comment on `JSG_STRING_LITERAL` in macro-meta.h for why a forwarding helper would be |
| 598 | // wrong here. The other `JSG_*_TS_OVERRIDE` and `JSG_*_TS_DEFINE` macros below follow the same |
| 599 | // pattern for the same reason. |
| 600 | #define JSG_TS_OVERRIDE(...) \ |
| 601 | do { \ |
| 602 | static const char OVERRIDE[] = #__VA_ARGS__; \ |
| 603 | registry.template registerTypeScriptOverride<OVERRIDE>(); \ |
| 604 | } while (false) |
| 605 | |
| 606 | // Use inside a JSG_RESOURCE_TYPE block to insert additional TypeScript definitions next to the generated |
| 607 | // TypeScript definition for this type. This macro accepts a single define parameter containing one or |
| 608 | // more TypeScript definitions (e.g. interfaces, classes, type aliases, consts, ...). Varargs are accepted |
| 609 | // so that defines can contain `,` outside of balanced brackets. See the `## TypeScript`section of the JSG |
| 610 | // README.md for more details. |
| 611 | #define JSG_TS_DEFINE(...) \ |
| 612 | do { \ |
| 613 | static const char DEFINE[] = #__VA_ARGS__; \ |
| 614 | registry.template registerTypeScriptDefine<DEFINE>(); \ |
| 615 | } while (false) |
| 616 | |
| 617 | // Like JSG_TS_DEFINE, but accepts a string literal (e.g. a raw string R"(...)") instead of bare tokens. |
| 618 | // This avoids the preprocessor parsing the TypeScript content as C++ tokens, which is necessary when the |
| 619 | // TypeScript definition contains C++20 keywords like `module` that Clang rejects inside macro arguments. |
| 620 | #define JSG_TS_DEFINE_LITERAL(jsg_string_literal) \ |
| 621 | do { \ |
| 622 | static const char DEFINE[] = jsg_string_literal; \ |
| 623 | registry.template registerTypeScriptDefine<DEFINE>(); \ |
| 624 | } while (false) |
| 625 | |
| 626 | // Like JSG_TS_ROOT but for use with JSG_STRUCT. Should be placed adjacent to the JSG_STRUCT declaration, |
| 627 | // inside the same `struct` definition. See the `## TypeScript` section of the JSG README.md for more |
| 628 | // details. |
| 629 | #define JSG_STRUCT_TS_ROOT() static constexpr bool _JSG_STRUCT_TS_ROOT_DO_NOT_USE_DIRECTLY = true |
| 630 | |
| 631 | // Like JSG_TS_OVERRIDE but for use with JSG_STRUCT. Should be placed adjacent to the JSG_STRUCT |
| 632 | // declaration, inside the same `struct` definition. See the `## TypeScript` section of the JSG README.md |
| 633 | // for many more details and examples. |
| 634 | #define JSG_STRUCT_TS_OVERRIDE(...) \ |
| 635 | static constexpr char _JSG_STRUCT_TS_OVERRIDE_DO_NOT_USE_DIRECTLY[] = #__VA_ARGS__ |
| 636 | |
| 637 | // Like JSG_STRUCT_TS_OVERRIDE, however it enables dynamic selection of TS_OVERRIDE. |
| 638 | // Should be placed adjacent to the JSG_STRUCT declaration, inside the same struct definition. |
| 639 | #define JSG_STRUCT_TS_OVERRIDE_DYNAMIC(...) \ |
| 640 | static void jsgConfiguration(__VA_ARGS__); \ |
| 641 | template <typename Registry> \ |
| 642 | static void registerTypeScriptDynamicOverride(Registry& registry, ##__VA_ARGS__) |
| 643 | |
| 644 | // Like JSG_TS_DEFINE but for use with JSG_STRUCT. Should be placed adjacent to the JSG_STRUCT |
| 645 | // declaration, inside the same `struct` definition. See the `## TypeScript`section of the JSG README.md |
| 646 | // for more details. |
| 647 | #define JSG_STRUCT_TS_DEFINE(...) \ |
| 648 | static constexpr char _JSG_STRUCT_TS_DEFINE_DO_NOT_USE_DIRECTLY[] = #__VA_ARGS__ |
| 649 | |
| 650 | // Adds a group of javascript modules to the module registry when context is instantiated. |
| 651 | // bundle is of a Bundle type from workerd/jsg/modules.capnp. |
| 652 | // Modules will be resolved according to their type and module registry normal resolve rules. |
| 653 | #define JSG_CONTEXT_JS_BUNDLE(bundle) \ |
| 654 | do { \ |
| 655 | registry.registerJsBundle(bundle); \ |
| 656 | } while (false) |
| 657 | |
| 658 | // true when T has _JSG_STRUCT_TS_ROOT_DO_NOT_USE_DIRECTLY field generated by JSG_STRUCT_TS_ROOT |
| 659 | template <typename T> |
| 660 | concept HasStructTypeScriptRoot = requires { T::_JSG_STRUCT_TS_ROOT_DO_NOT_USE_DIRECTLY; }; |
| 661 | |
| 662 | // true when T has _JSG_STRUCT_TS_OVERRIDE_DO_NOT_USE_DIRECTLY field generated by JSG_STRUCT_TS_OVERRIDE |
| 663 | template <typename T> |
| 664 | concept HasStructTypeScriptOverride = requires { T::_JSG_STRUCT_TS_OVERRIDE_DO_NOT_USE_DIRECTLY; }; |
| 665 | |
| 666 | // true when T has _JSG_STRUCT_TS_DEFINE_DO_NOT_USE_DIRECTLY field generated by JSG_STRUCT_TS_DEFINE |
| 667 | template <typename T> |
| 668 | concept HasStructTypeScriptDefine = requires { T::_JSG_STRUCT_TS_DEFINE_DO_NOT_USE_DIRECTLY; }; |
| 669 | |
| 670 | // Nest this inside a simple struct declaration in order to support translating it to/from a |
| 671 | // JavaScript object / Web IDL dictionary. |
| 672 | // |
| 673 | // struct MyStruct { |
| 674 | // double foo; |
| 675 | // kj::String bar; |
| 676 | // kj::String $public; |
| 677 | // |
| 678 | // JSG_STRUCT(foo, bar, $public); |
| 679 | // }; |
| 680 | // |
| 681 | // All of the types which are supported as method parameter / return types are also supported as |
| 682 | // struct field types. |
| 683 | // |
| 684 | // Note that if you use `jsg::Optional<T>` as a field type, then the field will not be present at |
| 685 | // all in JavaScript when the optional is null in C++ (as opposed to the field being present but |
| 686 | // assigned the value `undefined`). |
| 687 | // |
| 688 | // Note that if a `validate` function is provided, then it will be called after the struct is |
| 689 | // unwrapped from v8. This would be an appropriate time to throw an error. |
| 690 | // Signature: void validate(jsg::Lock& js); |
| 691 | // Example: |
| 692 | // struct ValidatingFoo { |
| 693 | // kj::String abc; |
| 694 | // void validate(jsg::Lock& js) { |
| 695 | // JSG_REQUIRE(abc.size() != 0, TypeError, "Field 'abc' had no length in 'ValidatingFoo'."); |
| 696 | // } |
| 697 | // JSG_STRUCT(abc); |
| 698 | // }; |
| 699 | // |
| 700 | // In this example the validate method would throw a `TypeError` if the size of the `abc` field was zero. |
| 701 | // |
| 702 | // Fields with a starting '$' will have that dollar sign prefix stripped in the JS binding. A |
| 703 | // motivating example to enact that change was WebCrypto which has a field in a dictionary called |
| 704 | // "public". '$' was chosen as a token we can use because it's a character for a C++ identifier. If |
| 705 | // the Javascript field needs to contain '$' for some reason (which they probably shouldn't since |
| 706 | // identifiers starting with $ are rare in JS land, especially for things the runtime would be |
| 707 | // exporting), then you should be able to use '$$' as the identifier prefix in C++ since only the |
| 708 | // first '$' gets stripped. |
| 709 | #define JSG_STRUCT(...) \ |
| 710 | static constexpr ::workerd::jsg::JsgKind JSG_KIND KJ_UNUSED = ::workerd::jsg::JsgKind::STRUCT; \ |
| 711 | static constexpr char JSG_FOR_EACH(JSG_STRUCT_FIELD_NAME, , __VA_ARGS__); \ |
| 712 | template <typename TypeWrapper, typename Self> \ |
| 713 | using JsgFieldWrappers = \ |
| 714 | ::workerd::jsg::TypeTuple<JSG_FOR_EACH(JSG_STRUCT_FIELD, , __VA_ARGS__)>; \ |
| 715 | template <typename Self> \ |
| 716 | static v8::Local<v8::DictionaryTemplate> jsgGetTemplate(v8::Isolate* isolate) { \ |
| 717 | kj::Vector<std::string_view> names; \ |
| 718 | JSG_FOR_EACH(JSG_STRUCT_FIELD_COL, , __VA_ARGS__); \ |
| 719 | auto namesPtr = names.asPtr().asConst(); \ |
| 720 | return v8::DictionaryTemplate::New( \ |
| 721 | isolate, v8::MemorySpan<const std::string_view>(namesPtr.begin(), namesPtr.size())); \ |
| 722 | } \ |
| 723 | template <typename Registry, typename Self, typename Config> \ |
| 724 | static void registerMembersInternal(Registry& registry, Config arg) { \ |
| 725 | JSG_FOR_EACH(JSG_STRUCT_REGISTER_MEMBER, , __VA_ARGS__); \ |
| 726 | if constexpr (::workerd::jsg::HasStructTypeScriptRoot<Self>) { \ |
| 727 | registry.registerTypeScriptRoot(); \ |
| 728 | } \ |
| 729 | if constexpr (requires(jsg::GetConfiguration<Self> arg) { \ |
| 730 | registerTypeScriptDynamicOverride<Registry>(registry, arg); \ |
| 731 | }) { \ |
| 732 | registerTypeScriptDynamicOverride<Registry>(registry, arg); \ |
| 733 | } else if constexpr (::workerd::jsg::HasStructTypeScriptOverride<Self>) { \ |
| 734 | registry.template registerTypeScriptOverride< \ |
| 735 | Self::_JSG_STRUCT_TS_OVERRIDE_DO_NOT_USE_DIRECTLY>(); \ |
| 736 | } \ |
| 737 | if constexpr (::workerd::jsg::HasStructTypeScriptDefine<Self>) { \ |
| 738 | registry \ |
| 739 | .template registerTypeScriptDefine<Self::_JSG_STRUCT_TS_DEFINE_DO_NOT_USE_DIRECTLY>(); \ |
| 740 | } \ |
| 741 | } \ |
| 742 | template <typename Registry, typename Self> \ |
| 743 | static void registerMembers(Registry& registry) \ |
| 744 | requires(!jsg::HasConfiguration<Self>) \ |
| 745 | { \ |
| 746 | registerMembersInternal<Registry, Self, void*>(registry, nullptr); \ |
| 747 | } \ |
| 748 | template <typename Registry, typename Self> \ |
| 749 | static void registerMembers(Registry& registry, jsg::GetConfiguration<Self> arg) \ |
| 750 | requires jsg::HasConfiguration<Self> \ |
| 751 | { \ |
| 752 | registerMembersInternal<Registry, Self, jsg::GetConfiguration<Self>>(registry, arg); \ |
| 753 | } |
| 754 | |
| 755 | template <size_t N> |
| 756 | inline consteval size_t prefixLengthToStrip(const char (&s)[N]) { |
| 757 | return s[0] == '$' ? 1 : 0; |
| 758 | } |
| 759 | |
| 760 | // This string may not be what's actually exported to v8. For example, if it starts with a `$`, then |
| 761 | // this value will still contain the `$` even though the `FieldWrapper` template argument will have |
| 762 | // it stripped. |
| 763 | #define JSG_STRUCT_FIELD_NAME(_, name) name##_JSG_NAME_DO_NOT_USE_DIRECTLY[] = #name |
| 764 | |
| 765 | #define JSG_STRUCT_FIELD_COL(_, name) \ |
| 766 | ::workerd::jsg::jsgAddToStructNames<decltype(::kj::instance<Self>().name), \ |
| 767 | name##_JSG_NAME_DO_NOT_USE_DIRECTLY, ::workerd::jsg::prefixLengthToStrip(#name)>(names) |
| 768 | |
| 769 | // (Internal implementation details for JSG_STRUCT.) |
| 770 | #define JSG_STRUCT_FIELD(_, name) \ |
| 771 | ::workerd::jsg::FieldWrapper<TypeWrapper, Self, decltype(::kj::instance<Self>().name), \ |
| 772 | &Self::name, name##_JSG_NAME_DO_NOT_USE_DIRECTLY, \ |
| 773 | ::workerd::jsg::prefixLengthToStrip(#name)> |
| 774 | // (Internal implementation details for JSG_STRUCT.) |
| 775 | #define JSG_STRUCT_REGISTER_MEMBER(_, name) \ |
| 776 | registry.template registerStructProperty<decltype(::kj::instance<Self>().name), &Self::name>( \ |
| 777 | name##_JSG_NAME_DO_NOT_USE_DIRECTLY) |
| 778 | |
| 779 | // Indexes for adding API data to V8's Isolate object. |
| 780 | enum SetDataIndex { |
| 781 | // The jsg::IsolateBase for a particular V8 isolate. |
| 782 | SET_DATA_ISOLATE_BASE, |
| 783 | // The TypeWrapper object for a particular V8 isolate. |
| 784 | SET_DATA_TYPE_WRAPPER, |
| 785 | // The lock associated with the V8 isolate. |
| 786 | SET_DATA_LOCK, |
| 787 | // The Worker::Isolate associated with the V8 isolate. |
| 788 | SET_DATA_ISOLATE, |
| 789 | // The address of the base of the 4Gbyte compressed pointer area. |
| 790 | // If we are using the sandbox it's also the base of the sandbox. |
| 791 | SET_DATA_CAGE_BASE, |
| 792 | // Used by JSG<->Rust integration. |
| 793 | SET_DATA_RUST_REALM, |
| 794 | // The number of slots workerd uses in the API data for Isolate objects. |
| 795 | SET_DATA_SLOTS_IN_USE, |
| 796 | }; |
| 797 | |
| 798 | // ======================================================================================= |
| 799 | // Special types |
| 800 | // |
| 801 | // These types can be used in C++ to represent various JavaScript idioms / Web IDL types. |
| 802 | |
| 803 | class Lock; |
| 804 | WD_STRONG_BOOL(RequireEsm); |
| 805 | |
| 806 | // Arbitrary V8 data, wrapped for storage from C++. You can't do much with it, so instead you |
| 807 | // should probably use V8Ref<T>, a version of this that's strongly typed. |
| 808 | // |
| 809 | // When storing a Value inside a C++ object that is itself exported back to JavaScript, make sure |
| 810 | // to implement GC visitation -- see GcVisitor, below. |
| 811 | // |
| 812 | // It is safe to destroy a strong jsg::Data object outside of the isolate lock. In this case, |
| 813 | // the underlying V8 handles will be added to a queue, to be destroyed the next time a thread |
| 814 | // locks the isolate. This means their destruction is non-deterministic, but that is true of V8 |
| 815 | // objects anyway, due to the GC. Weak jsg::Data (i.e., those which are reachable by V8's GC, |
| 816 | // see GcVisitor below) must still be destroyed under the isolate lock to guard against concurrent |
| 817 | // modification with the GC. |
| 818 | // |
| 819 | // Move construction and move assignment of strong jsg::Data is well-defined even without |
| 820 | // holding the isolate lock. That is, it is safe to move Values unless you have implemented GC |
| 821 | // visitation for them. Moving jsg::Data which are reachable via GC visitation is undefined |
| 822 | // behavior outside of an isolate lock. |
| 823 | class Data { |
| 824 | public: |
| 825 | Data(decltype(nullptr)) {} |
| 826 | ~Data() noexcept(false) { |
| 827 | destroy(); |
| 828 | } |
| 829 | Data(Data&& other) noexcept: isolate(other.isolate), handle(kj::mv(other.handle)) { |
| 830 | KJ_IF_SOME(t, other.tracedHandle) { |
| 831 | moveFromTraced(other, t); |
| 832 | } |
| 833 | other.isolate = nullptr; |
| 834 | } |
| 835 | Data& operator=(Data&& other) { |
| 836 | if (this != &other) { |
| 837 | destroy(); |
| 838 | isolate = other.isolate; |
| 839 | handle = kj::mv(other.handle); |
| 840 | other.isolate = nullptr; |
| 841 | KJ_IF_SOME(t, other.tracedHandle) { |
| 842 | moveFromTraced(other, t); |
| 843 | } |
| 844 | } |
| 845 | assertInvariant(); |
| 846 | other.assertInvariant(); |
| 847 | return *this; |
| 848 | } |
| 849 | KJ_DISALLOW_COPY(Data); |
| 850 | |
| 851 | Data(v8::Isolate* isolate, v8::Local<v8::Data> handle) |
| 852 | : isolate(isolate), |
| 853 | handle(isolate, handle) {} |
| 854 | |
| 855 | // Get the raw underlying v8 handle. |
| 856 | v8::Local<v8::Data> getHandle(v8::Isolate* isolate) const { |
| 857 | return handle.Get(isolate); |
| 858 | } |
| 859 | |
| 860 | // Get the raw underlying v8 handle. |
| 861 | v8::Local<v8::Data> getHandle(Lock& js) const; |
| 862 | |
| 863 | Data addRef(v8::Isolate* isolate) { |
| 864 | return Data(isolate, getHandle(isolate)); |
| 865 | } |
| 866 | Data addRef(Lock& js); |
| 867 | |
| 868 | inline bool operator==(const Data& other) const { |
| 869 | return handle == other.handle; |
| 870 | } |
| 871 | inline bool operator==(const v8::Local<v8::Data>& other) const { |
| 872 | return handle == other; |
| 873 | } |
| 874 | |
| 875 | private: |
| 876 | // The isolate with which the handles below are associated. |
| 877 | v8::Isolate* isolate = nullptr; |
| 878 | |
| 879 | // Handle to the value which will be marked strong if any untraced C++ references exist, weak |
| 880 | // otherwise. |
| 881 | v8::Global<v8::Data> handle; |
| 882 | |
| 883 | // When `handle` is weak, `tracedHandle` is a copy of it used to integrate with V8 GC tracing. |
| 884 | // When `handle` is strong, we null out `tracedHandle`, because we don't need it, and it is |
| 885 | // illegal to hold onto a traced handle without actually marking it during each trace. |
| 886 | kj::Maybe<v8::TracedReference<v8::Data>> tracedHandle; |
| 887 | |
| 888 | friend class GcVisitor; |
| 889 | |
| 890 | void destroy(); |
| 891 | |
| 892 | // Debugging helpers. |
| 893 | |
| 894 | // Assert that only empty values are associated with null isolates. |
| 895 | // |
| 896 | // Note that we use IASSERT (which is only enabled in debug) here because this function is |
| 897 | // intended to be invoked from the move ctor and assignment operator. We expect them to be |
| 898 | // invoked a lot and want them to be as optimizable as possible. |
| 899 | void assertInvariant() { |
| 900 | KJ_IASSERT(isolate != nullptr || handle.IsEmpty()); |
| 901 | } |
| 902 | |
| 903 | // Implement move constructor when the source of the move has previously been visited for |
| 904 | // garbage collection. |
| 905 | void moveFromTraced(Data& other, v8::TracedReference<v8::Data>& otherTracedRef) noexcept; |
| 906 | |
| 907 | friend class MemoryTracker; |
| 908 | }; |
| 909 | |
| 910 | // A drop-in replacement for v8::Global<T>. Its big feature is that, like jsg::Data, a |
| 911 | // jsg::V8Ref<T> is safe to destroy outside of the isolate lock. |
| 912 | // |
| 913 | // Generally you should prefer using jsg::Value (for v8::Value) or jsg::Ref<T>. Use a |
| 914 | // jsg::V8Ref<T> when you need the type-safety of holding a handle to a specific V8 type. |
| 915 | template <typename T> |
| 916 | class V8Ref: private Data { |
| 917 | public: |
| 918 | V8Ref(decltype(nullptr)): Data(nullptr) {} |
| 919 | V8Ref(v8::Isolate* isolate, v8::Local<T> handle): Data(isolate, handle) {} |
| 920 | V8Ref(V8Ref&& other) noexcept: Data(kj::mv(other)) {} |
| 921 | V8Ref& operator=(V8Ref&& other) { |
| 922 | Data::operator=(kj::mv(other)); |
| 923 | return *this; |
| 924 | } |
| 925 | KJ_DISALLOW_COPY(V8Ref); |
| 926 | |
| 927 | v8::Local<T> getHandle(v8::Isolate* isolate) const { |
| 928 | if constexpr (std::is_base_of<v8::Value, T>()) { |
| 929 | // V8 doesn't let us cast directly from v8::Data to subtypes of v8::Value, so we're forced to |
| 930 | // use this double cast... Ech. |
| 931 | return Data::getHandle(isolate).template As<v8::Value>().template As<T>(); |
| 932 | } else { |
| 933 | return Data::getHandle(isolate).template As<T>(); |
| 934 | } |
| 935 | } |
| 936 | v8::Local<T> getHandle(jsg::Lock& js) const; |
| 937 | |
| 938 | V8Ref addRef(v8::Isolate* isolate) { |
| 939 | return V8Ref(isolate, getHandle(isolate)); |
| 940 | } |
| 941 | V8Ref addRef(jsg::Lock& js); |
| 942 | |
| 943 | V8Ref deepClone(jsg::Lock& js); |
| 944 | |
| 945 | inline bool operator==(const V8Ref& other) const { |
| 946 | return Data::operator==(other); |
| 947 | } |
| 948 | inline bool operator==(const v8::Local<T>& other) const { |
| 949 | return Data::operator==(other); |
| 950 | } |
| 951 | |
| 952 | template <typename U> |
| 953 | V8Ref<U> cast(jsg::Lock& js); |
| 954 | |
| 955 | private: |
| 956 | friend class GcVisitor; |
| 957 | friend class MemoryTracker; |
| 958 | }; |
| 959 | |
| 960 | using Value = V8Ref<v8::Value>; |
| 961 | |
| 962 | // Like V8Ref but also implements `hashCode()`. Useful as a key into a kj::HashTable. |
| 963 | // |
| 964 | // T must v8::Object or a subclass (or anything that implements GetIdentityHash()). |
| 965 | template <typename T> |
| 966 | class HashableV8Ref: public V8Ref<T> { |
| 967 | public: |
| 968 | HashableV8Ref(decltype(nullptr)): V8Ref<T>(nullptr), identityHash(0) {} |
| 969 | HashableV8Ref(v8::Isolate* isolate, v8::Local<T> handle) |
| 970 | // TODO(perf): It's not clear if V8's `GetIdentityHash()` is intended to return uniform |
| 971 | // results as required for KJ hashing, so we pass it to `kj::hashCode()` to further hash |
| 972 | // it. This may be unnecessary. Note that there are several other call sites of |
| 973 | // `GetIdentityHash()` which do the same -- if we decide we don't need this we should fix |
| 974 | // all of them. |
| 975 | : V8Ref<T>(isolate, handle), |
| 976 | identityHash(kj::hashCode(handle->GetIdentityHash())) {} |
| 977 | HashableV8Ref(HashableV8Ref&& other) = default; |
| 978 | HashableV8Ref& operator=(HashableV8Ref&& other) = default; |
| 979 | KJ_DISALLOW_COPY(HashableV8Ref); |
| 980 | |
| 981 | HashableV8Ref addRef(v8::Isolate* isolate) { |
| 982 | return HashableV8Ref(isolate, this->getHandle(isolate), identityHash); |
| 983 | } |
| 984 | HashableV8Ref addRef(jsg::Lock& js); |
| 985 | |
| 986 | int hashCode() const { |
| 987 | return identityHash; |
| 988 | } |
| 989 | |
| 990 | private: |
| 991 | int identityHash; |
| 992 | |
| 993 | HashableV8Ref(v8::Isolate* isolate, v8::Local<T> handle, int identityHash) |
| 994 | : V8Ref<T>(isolate, handle), |
| 995 | identityHash(identityHash) {} |
| 996 | }; |
| 997 | |
| 998 | template <V8Value T> |
| 999 | void MemoryTracker::trackField( |
| 1000 | kj::StringPtr edgeName, const V8Ref<T>& value, kj::Maybe<kj::StringPtr> nodeName) { |
| 1001 | // Even though we're passing in a template T, casting to a v8::Value is sufficient here. |
| 1002 | trackField(edgeName, value.handle.Get(isolate_).template As<v8::Value>(), nodeName); |
| 1003 | } |
| 1004 | |
| 1005 | // A value of type T, or `undefined`. |
| 1006 | // |
| 1007 | // In C++, this has the same usage as kj::Maybe<T>. However, a null kj::Maybe<T> corresponds to |
| 1008 | // `null` in JavaScript, whereas a null Optional<T> corresponds to `undefined` in JavaScript. |
| 1009 | // |
| 1010 | // Note: Due to Web IDL's undefined-to-nullable coercion rule, a null Maybe<T> can also unwrap |
| 1011 | // from an `undefined` value explicitly passed to a non-optional nullable. |
| 1012 | // |
| 1013 | // There are two main use cases for Optional<T>: optional function/method parameters and optional |
| 1014 | // JSG_STRUCT members. In both cases, a null value in C++ corresponds to the parameter/field not |
| 1015 | // being present at all in JavaScript, or explicitly set to `undefined`. |
| 1016 | // |
| 1017 | // In Web IDL, function parameters are considered required unless marked `optional`, while |
| 1018 | // dictionary (JSG_STRUCT) members are considered optional unless marked `required`. So, if you |
| 1019 | // were implementing an API specified in Web IDL like so: |
| 1020 | // |
| 1021 | // dictionary Data { |
| 1022 | // double number; |
| 1023 | // required DOMString string; |
| 1024 | // }; |
| 1025 | // void foo(optional Data data); |
| 1026 | // |
| 1027 | // An appropriate representation in C++ would be: |
| 1028 | // |
| 1029 | // struct Data { |
| 1030 | // Optional<double> number; |
| 1031 | // kj::String string; |
| 1032 | // JSG_STRUCT(number, string); |
| 1033 | // }; |
| 1034 | // void foo(Optional<Data> data); |
| 1035 | template <typename T> |
| 1036 | class Optional: public kj::Maybe<T> { |
| 1037 | public: |
| 1038 | // Inheriting constructors does not inherit copy/move constructors, so we declare a forwarding |
| 1039 | // constructor instead. |
| 1040 | template <typename... Params> |
| 1041 | Optional(Params&&... params): kj::Maybe<T>(kj::fwd<Params>(params)...) {} |
| 1042 | }; |
| 1043 | |
| 1044 | // Identical to Optional, but rather than treating failures to unwrap a JS value to type T as an |
| 1045 | // error, it just results in an unset LenientOptional. |
| 1046 | template <typename T> |
| 1047 | class LenientOptional: public kj::Maybe<T> { |
| 1048 | public: |
| 1049 | // Inheriting constructors does not inherit copy/move constructors, so we declare a forwarding |
| 1050 | // constructor instead. |
| 1051 | template <typename... Params> |
| 1052 | LenientOptional(Params&&... params): kj::Maybe<T>(kj::fwd<Params>(params)...) {} |
| 1053 | }; |
| 1054 | |
| 1055 | // Use this type in a JSG_STRUCT to define a special field that will be filled in with a |
| 1056 | // reference to the original struct's JavaScript representation. This is useful e.g. if you |
| 1057 | // may need to pull additional fields out of the struct. |
| 1058 | // |
| 1059 | // Another option is to use jsg::Identified<MyStruct>, but sometimes storing the reference |
| 1060 | // into a field of the unwrapped struct is more convenient. |
| 1061 | class SelfRef: public V8Ref<v8::Object> { |
| 1062 | public: |
| 1063 | using V8Ref::V8Ref; |
| 1064 | |
| 1065 | // Convert the V8Ref<v8::Object> to a V8Ref<v8::Value> |
| 1066 | inline Value asValue(Lock& js) const; |
| 1067 | }; |
| 1068 | |
| 1069 | template <typename U> |
| 1070 | static constexpr bool isUsableStructField = !kj::isSameType<U, SelfRef>() && |
| 1071 | !kj::isSameType<U, Unimplemented>() && !kj::isSameType<U, WontImplement>(); |
| 1072 | |
| 1073 | template <typename T, const char* name, size_t prefix> |
| 1074 | void jsgAddToStructNames(auto& names) { |
| 1075 | constexpr const char* exportedName = name + prefix; |
| 1076 | if constexpr (isUsableStructField<T>) names.add(exportedName); |
| 1077 | } |
| 1078 | |
| 1079 | // A USVString has the exact same representation as a kj::String, but we guarantee that it meets |
| 1080 | // the WHATWG definition of a "scalar value string". Particularly, a USVString will never contain |
| 1081 | // invalid surrogate characters. A USVString should be used when implementing a Web API that |
| 1082 | // requires this behaviour. |
| 1083 | // See <https://infra.spec.whatwg.org/#scalar-value-string> |
| 1084 | class USVString: public kj::String { |
| 1085 | public: |
| 1086 | // Inheriting constructors does not inherit copy/move constructors, so we declare a forwarding |
| 1087 | // constructor instead. |
| 1088 | template <typename... Params> |
| 1089 | explicit USVString(Params&&... params): kj::String(kj::fwd<Params>(params)...) { |
| 1090 | KJ_DASSERT(isValidUtf8()); |
| 1091 | } |
| 1092 | |
| 1093 | private: |
| 1094 | // This is a seperate method to avoid including simdutf in the header file. |
| 1095 | bool isValidUtf8() const; |
| 1096 | }; |
| 1097 | |
| 1098 | // A DOMString has the exact same representation as a kj::String, but may contain WTF-8 encoded |
| 1099 | // data like unpaired surrogate characters, that are not strictly valid in UTF-8. A DOMString |
| 1100 | // should be used when implementing a Web API that requires this behaviour, or when an explicit |
| 1101 | // decision is made to accept potentially invalid strings. |
| 1102 | class DOMString: public kj::String { |
| 1103 | public: |
| 1104 | // Inheriting constructors does not inherit copy/move constructors, so we declare a forwarding |
| 1105 | // constructor instead. |
| 1106 | template <typename... Params> |
| 1107 | explicit DOMString(Params&&... params): kj::String(kj::fwd<Params>(params)...) {} |
| 1108 | }; |
| 1109 | |
| 1110 | // A Dict<V, K> in C++ corresponds to a JavaScript object that is being used as a string -> value |
| 1111 | // map, where all the values are of type T. |
| 1112 | // |
| 1113 | // Note: A Dict<V, K> corresponds to a record<K, V> in the Web IDL language. |
| 1114 | template <typename Value, typename Key = kj::String> |
| 1115 | struct Dict { |
| 1116 | // TODO(someday): Maybe make this a map and not an array? Current use case doesn't care, though. |
| 1117 | |
| 1118 | // Field of an object. |
| 1119 | struct Field { |
| 1120 | Key name; |
| 1121 | Value value; |
| 1122 | |
| 1123 | JSG_MEMORY_INFO(Field) { |
| 1124 | tracker.trackField("name", name); |
| 1125 | tracker.trackField("value", value); |
| 1126 | } |
| 1127 | }; |
| 1128 | |
| 1129 | kj::Array<Field> fields; |
| 1130 | |
| 1131 | JSG_MEMORY_INFO(Dict) { |
| 1132 | for (const auto& field: fields) { |
| 1133 | tracker.trackField(nullptr, field); |
| 1134 | } |
| 1135 | } |
| 1136 | }; |
| 1137 | |
| 1138 | template <typename T> |
| 1139 | class TypeHandler; |
| 1140 | |
| 1141 | // When used as a function argument type, captures all remaining arguments passed to the method, |
| 1142 | // unwrapping them all as type T. |
| 1143 | template <typename T> |
| 1144 | class Arguments: public kj::Array<T> { |
| 1145 | public: |
| 1146 | Arguments(kj::Array<T>&& value): kj::Array<T>(kj::mv(value)) {} |
| 1147 | |
| 1148 | using ElementType = T; |
| 1149 | }; |
| 1150 | |
| 1151 | // Is `T` some specialization of `Arguments<U>`? |
| 1152 | template <typename T> |
| 1153 | struct IsArguments_ { |
| 1154 | static constexpr bool value = false; |
| 1155 | }; |
| 1156 | template <typename T> |
| 1157 | struct IsArguments_<Arguments<T>> { |
| 1158 | static constexpr bool value = true; |
| 1159 | }; |
| 1160 | template <typename T> |
| 1161 | constexpr bool isArguments() { |
| 1162 | return IsArguments_<T>::value; |
| 1163 | } |
| 1164 | |
| 1165 | template <typename T> |
| 1166 | constexpr bool resourceNeedsGcTracing(); |
| 1167 | template <typename T> |
| 1168 | void visitSubclassForGc(T* obj, GcVisitor& visitor); |
| 1169 | |
| 1170 | // All resource types must inherit from this. |
| 1171 | class Object: private Wrappable { |
| 1172 | public: |
| 1173 | using jsgThis = Object; |
| 1174 | |
| 1175 | // Objects that extend from jsg::Object should never be copied or moved |
| 1176 | // independently of their owning jsg::Ref so we explicitly delete the |
| 1177 | // copy and move constructors and assignment operators to be safe. |
| 1178 | KJ_DISALLOW_COPY_AND_MOVE(Object); |
| 1179 | |
| 1180 | // Since we explicitly delete the copy and move constructors, we have |
| 1181 | // to explicitly declare the default constructor. |
| 1182 | Object() = default; |
| 1183 | |
| 1184 | inline void jsgVisitForGc(GcVisitor& visitor) override {} |
| 1185 | |
| 1186 | // Subclasses should override these to provide appropriate information for |
| 1187 | // the heap snapshot process. |
| 1188 | inline kj::StringPtr jsgGetMemoryName() const override { |
| 1189 | return "Object"; |
| 1190 | } |
| 1191 | inline size_t jsgGetMemorySelfSize() const override { |
| 1192 | return sizeof(Object); |
| 1193 | } |
| 1194 | inline void jsgGetMemoryInfo(MemoryTracker& tracker) const override { |
| 1195 | Wrappable::jsgGetMemoryInfo(tracker); |
| 1196 | } |
| 1197 | inline v8::Local<v8::Object> jsgGetMemoryInfoWrapperObject(v8::Isolate* isolate) override { |
| 1198 | return Wrappable::jsgGetMemoryInfoWrapperObject(isolate); |
| 1199 | } |
| 1200 | inline bool jsgGetMemoryInfoIsRootNode() const override { |
| 1201 | return Wrappable::jsgGetMemoryInfoIsRootNode(); |
| 1202 | } |
| 1203 | |
| 1204 | static constexpr bool jsgHasReflection = false; |
| 1205 | template <typename TypeWrapper> |
| 1206 | inline void jsgInitReflection(TypeWrapper& wrapper) {} |
| 1207 | |
| 1208 | // Dummy invalid serialization tag. This is only used to detect when a subclass has defined their |
| 1209 | // own tag. |
| 1210 | static constexpr uint jsgSerializeTag = kj::maxValue; |
| 1211 | |
| 1212 | private: |
| 1213 | inline void visitForMemoryInfo(MemoryTracker& tracker) const {} |
| 1214 | inline void visitForGc(GcVisitor& visitor) {} |
| 1215 | template <typename> |
| 1216 | friend constexpr bool ::workerd::jsg::resourceNeedsGcTracing(); |
| 1217 | template <typename T> |
| 1218 | friend void visitSubclassForGc(T* obj, GcVisitor& visitor); |
| 1219 | template <typename T> |
| 1220 | friend void visitSubclassForMemoryInfo(const T* obj, MemoryTracker& visitor); |
| 1221 | template <typename T> |
| 1222 | friend class Ref; |
| 1223 | friend class kj::Refcounted; |
| 1224 | template <typename T> |
| 1225 | friend kj::Own<T> kj::addRef(T& object); |
| 1226 | template <typename T, typename... Params> |
| 1227 | friend kj::Own<T> kj::refcounted(Params&&... params); |
| 1228 | friend class GcVisitor; |
| 1229 | template <typename, typename...> |
| 1230 | friend class TypeWrapper; |
| 1231 | template <typename, typename> |
| 1232 | friend class ResourceWrapper; |
| 1233 | template <typename> |
| 1234 | friend class ObjectWrapper; |
| 1235 | template <typename> |
| 1236 | friend class SelfPropertyReader; |
| 1237 | friend class MemoryTracker; |
| 1238 | }; |
| 1239 | |
| 1240 | // Ref<T> is a reference to a resource type (a type with a JSG_RESOURCE_TYPE block) living on |
| 1241 | // the V8 heap. |
| 1242 | // |
| 1243 | // Use Ref<T> when you want a long-lived reference to such a type. If you only need a reference |
| 1244 | // that lasts until your method returns, you can specify the parameter type `T&` instead, which |
| 1245 | // is more efficient. Use Ref<T> when you need to keep the reference longer than that. |
| 1246 | // |
| 1247 | // WARNING: When storing Ref<T> in a C++ object that itself is referenced from the JS heap, |
| 1248 | // you must implement GC visitation; see GcVisitor, below. |
| 1249 | // |
| 1250 | // It is safe to destroy a jsg::Ref<T> object outside of the isolate lock. In this case, |
| 1251 | // the underlying V8 handles will be added to a queue, to be destroyed the next time a thread |
| 1252 | // locks the isolate. This means their destruction is non-deterministic, but that is true of V8 |
| 1253 | // objects anyway, due to the GC. |
| 1254 | // |
| 1255 | // Move construction and move assignment of strong jsg::Ref<T>s is well-defined even without |
| 1256 | // holding the isolate lock. That is, it is safe to move Refs unless you have implemented GC |
| 1257 | // visitation for them. Moving jsg::Ref<T>s which are reachable via GC visitation is undefined |
| 1258 | // behavior outside of an isolate lock. |
| 1259 | template <typename T> |
| 1260 | class Ref { |
| 1261 | public: |
| 1262 | Ref(decltype(nullptr)): strong(false) {} |
| 1263 | Ref(Ref&& other) noexcept: inner(kj::mv(other.inner)), strong(true) { |
| 1264 | if (other.strong) { |
| 1265 | other.strong = false; |
| 1266 | } else { |
| 1267 | inner->addStrongRef(); |
| 1268 | } |
| 1269 | } |
| 1270 | |
| 1271 | // Upgrade a KJ allocation to a Ref. This is useful if you want to allocate the object outside |
| 1272 | // the isolate lock and then bring it in later. The object must be allocated with |
| 1273 | // kj::refcounted. Once the Ref is constructed, the refcount is protected by the isolate lock |
| 1274 | // going forward; you can no longer add or remove refs outside the lock. |
| 1275 | explicit Ref(kj::Own<T> innerParam): inner(kj::mv(innerParam)), strong(true) { |
| 1276 | inner->addStrongRef(); |
| 1277 | } |
| 1278 | template <typename U, typename = kj::EnableIf<kj::canConvert<U&, T&>()>> |
| 1279 | Ref(Ref<U>&& other) noexcept: inner(kj::mv(other.inner)), |
| 1280 | strong(true) { |
| 1281 | if (other.strong) { |
| 1282 | other.strong = false; |
| 1283 | } else { |
| 1284 | inner->addStrongRef(); |
| 1285 | } |
| 1286 | } |
| 1287 | template <typename U> |
| 1288 | Ref& operator=(Ref<U>&& other) { |
| 1289 | destroy(); |
| 1290 | inner = kj::mv(other.inner); |
| 1291 | strong = true; |
| 1292 | if (other.strong) { |
| 1293 | other.strong = false; |
| 1294 | } else { |
| 1295 | inner->addStrongRef(); |
| 1296 | } |
| 1297 | return *this; |
| 1298 | } |
| 1299 | ~Ref() noexcept(false) { |
| 1300 | destroy(); |
| 1301 | } |
| 1302 | KJ_DISALLOW_COPY(Ref); |
| 1303 | |
| 1304 | T& operator*() { |
| 1305 | return *inner; |
| 1306 | } |
| 1307 | T* operator->() { |
| 1308 | return inner.get(); |
| 1309 | } |
| 1310 | T* get() { |
| 1311 | return inner.get(); |
| 1312 | } |
| 1313 | |
| 1314 | const T& operator*() const { |
| 1315 | return *inner; |
| 1316 | } |
| 1317 | const T* operator->() const { |
| 1318 | return inner.get(); |
| 1319 | } |
| 1320 | const T* get() const { |
| 1321 | return inner.get(); |
| 1322 | } |
| 1323 | |
| 1324 | Ref addRef() & { |
| 1325 | return Ref(kj::addRef(*inner)); |
| 1326 | } |
| 1327 | Ref addRef() && = delete; // would be redundant |
| 1328 | |
| 1329 | // If the object has a JS wrapper, return it. Note that the JS wrapper is initialized lazily |
| 1330 | // when the object is first passed to JS, so you can't be sure that it exists. To reliably |
| 1331 | // get a handle (creating it on-demand if necessary), use a TypeHandler<Ref<T>>. |
| 1332 | kj::Maybe<v8::Local<v8::Object>> tryGetHandle(v8::Isolate* isolate) { |
| 1333 | return inner->tryGetHandle(isolate); |
| 1334 | } |
| 1335 | |
| 1336 | kj::Maybe<v8::Local<v8::Object>> tryGetHandle(Lock& js); |
| 1337 | |
| 1338 | // Attach a JavaScript object which implements the JS interface for this C++ object. Normally, |
| 1339 | // this happens automatically the first time the Ref is passed across the FFI barrier into JS. |
| 1340 | // This method may be useful in order to use a different wrapper type than the one that would |
| 1341 | // be used automatically. This method is also useful when implementing TypeWrapperExtensions. |
| 1342 | // |
| 1343 | // It is an error to attach a wrapper when another wrapper is already attached. Hence, |
| 1344 | // typically this should only be called on a newly-allocated object. |
| 1345 | void attachWrapper(v8::Isolate* isolate, v8::Local<v8::Object> object) { |
| 1346 | inner->Wrappable::attachWrapper(isolate, object, resourceNeedsGcTracing<T>()); |
| 1347 | } |
| 1348 | |
| 1349 | private: |
| 1350 | kj::Own<T> inner; |
| 1351 | |
| 1352 | // If this has ever been traced, the parent object from which the trace originated. This is kept |
| 1353 | // for debugging purposes only -- there should only ever be one parent for a particular ref. |
| 1354 | // |
| 1355 | // This field does NOT move when the Ref moves, because it's a property of the specific Ref |
| 1356 | // location. |
| 1357 | kj::Maybe<Wrappable&> parent; |
| 1358 | |
| 1359 | // True if the ref is currently counted in the target's strong refcount. |
| 1360 | bool strong; |
| 1361 | |
| 1362 | void destroy() { |
| 1363 | if (auto ptr = inner.get(); ptr != nullptr) { |
| 1364 | inner->maybeDeferDestruction(strong, kj::mv(inner), static_cast<Wrappable*>(ptr)); |
| 1365 | } |
| 1366 | } |
| 1367 | |
| 1368 | template <typename> |
| 1369 | friend class Ref; |
| 1370 | template <typename U, typename... Params> |
| 1371 | friend Ref<U> alloc(Params&&... params); |
| 1372 | friend class Lock; |
| 1373 | template <typename U> |
| 1374 | friend Ref<U> _jsgThis(U* obj); |
| 1375 | template <typename, typename> |
| 1376 | friend class ResourceWrapper; |
| 1377 | template <typename> |
| 1378 | friend class ObjectWrapper; |
| 1379 | friend class GcVisitor; |
| 1380 | }; |
| 1381 | |
| 1382 | template <MemoryRetainer T> |
| 1383 | void MemoryTracker::trackField( |
| 1384 | kj::StringPtr edgeName, const Ref<T>& value, kj::Maybe<kj::StringPtr> nodeName) { |
| 1385 | trackField(edgeName, value.get(), nodeName); |
| 1386 | } |
| 1387 | |
| 1388 | template <typename T, typename... Params> |
| 1389 | // TODO(js.alloc): When most of the jsg::alloc users are updated we can uncomment |
| 1390 | // the deprecation here. When all uses are updated to use js.alloc, we can remove |
| 1391 | // this method entirely. |
| 1392 | //[[deprecated("Use js.alloc<T>(...) instead")]] |
| 1393 | Ref<T> alloc(Params&&... params) { |
| 1394 | return Ref<T>(kj::refcounted<T>(kj::fwd<Params>(params)...)); |
| 1395 | } |
| 1396 | |
| 1397 | template <typename T> |
| 1398 | Ref<T> _jsgThis(T* obj) { |
| 1399 | return Ref<T>(kj::addRef(*obj)); |
| 1400 | } |
| 1401 | |
| 1402 | #define JSG_THIS (::workerd::jsg::_jsgThis(this)) |
| 1403 | |
| 1404 | // Holds a value of type `T` and allows it to be passed to JavaScript multiple times, resulting |
| 1405 | // in exactly the same JavaScript object each time (will compare equal using `===`). You may |
| 1406 | // pass `MemoizedIdentity<T>` by reference, e.g. you could define a method of a JSG_RESOURCE_TYPE |
| 1407 | // which returns `MemoizedIdentity<T>&`, returning a reference to a member of the object. |
| 1408 | // |
| 1409 | // Note that you don't need to wrap `jsg::Ref<T>` this way, as it already has the property that |
| 1410 | // only one wrapper will be created. `MemoizedIdentity` can wrap any type that is convertible to |
| 1411 | // JavaScript, including types that are otherwise pass-by-value. |
| 1412 | template <typename T> |
| 1413 | class MemoizedIdentity { |
| 1414 | public: |
| 1415 | inline MemoizedIdentity(T value): value(kj::mv(value)) {} |
| 1416 | |
| 1417 | inline MemoizedIdentity& operator=(T value) { |
| 1418 | this->value = kj::mv(value); |
| 1419 | return *this; |
| 1420 | } |
| 1421 | |
| 1422 | void visitForGc(GcVisitor& visitor); |
| 1423 | |
| 1424 | JSG_MEMORY_INFO(MemoizedIdentity) { |
| 1425 | KJ_SWITCH_ONEOF(value) { |
| 1426 | KJ_CASE_ONEOF(val, T) { |
| 1427 | if constexpr (MemoryRetainer<T>) { |
| 1428 | tracker.trackField("value", val); |
| 1429 | } else { |
| 1430 | tracker.trackFieldWithSize("value", sizeof(T)); |
| 1431 | } |
| 1432 | } |
| 1433 | KJ_CASE_ONEOF(val, Value) { |
| 1434 | tracker.trackField("value", val); |
| 1435 | } |
| 1436 | } |
| 1437 | } |
| 1438 | |
| 1439 | private: |
| 1440 | kj::OneOf<T, Value> value; |
| 1441 | |
| 1442 | template <typename TypeWrapper> |
| 1443 | friend class MemoizedIdentityWrapper; |
| 1444 | friend class MemoryTracker; |
| 1445 | }; |
| 1446 | |
| 1447 | // Accept this type from JavaScript when you want to receive an object's identity in addition to |
| 1448 | // unwrapping it. This is useful, for example, if you need to be able to recognize when the |
| 1449 | // application passes in the same object again later. |
| 1450 | // |
| 1451 | // `T` must be a type whose JavaScript representation is an Object (including Functions), since |
| 1452 | // other types do not have a notion of identity-equality. |
| 1453 | template <typename T> |
| 1454 | struct Identified { |
| 1455 | // Handle to the original object. |
| 1456 | HashableV8Ref<v8::Object> identity; |
| 1457 | |
| 1458 | // The object's unwrapped value. |
| 1459 | T unwrapped; |
| 1460 | |
| 1461 | JSG_MEMORY_INFO(Identified) { |
| 1462 | tracker.trackField("identity", identity); |
| 1463 | if constexpr (MemoryRetainer<T>) { |
| 1464 | tracker.trackField("unwrapped", unwrapped); |
| 1465 | } else { |
| 1466 | tracker.trackFieldWithSize("unwrapped", sizeof(T)); |
| 1467 | } |
| 1468 | } |
| 1469 | }; |
| 1470 | |
| 1471 | // jsg::Name represents a value that is either a string or a v8::Symbol. It is most useful for |
| 1472 | // use in APIs that can accept both interchangeably. |
| 1473 | // |
| 1474 | // Name implements hashCode() so it is suitable for use as a key in kj::HashMap, etc. |
| 1475 | class Name final { |
| 1476 | public: |
| 1477 | explicit Name(kj::String string); |
| 1478 | explicit Name(kj::StringPtr string); |
| 1479 | explicit Name(Lock& js, v8::Local<v8::Symbol> symbol); |
| 1480 | KJ_DISALLOW_COPY(Name); |
| 1481 | Name(Name&&) = default; |
| 1482 | Name& operator=(Name&&) = default; |
| 1483 | |
| 1484 | inline int hashCode() const { |
| 1485 | return hash; |
| 1486 | } |
| 1487 | |
| 1488 | Name clone(jsg::Lock& js); |
| 1489 | |
| 1490 | kj::String toString(jsg::Lock& js); |
| 1491 | |
| 1492 | JSG_MEMORY_INFO(Name) { |
| 1493 | KJ_SWITCH_ONEOF(inner) { |
| 1494 | KJ_CASE_ONEOF(str, kj::String) { |
| 1495 | tracker.trackField("inner", str); |
| 1496 | } |
| 1497 | KJ_CASE_ONEOF(sym, V8Ref<v8::Symbol>) { |
| 1498 | tracker.trackField("inner", sym); |
| 1499 | } |
| 1500 | } |
| 1501 | } |
| 1502 | |
| 1503 | private: |
| 1504 | int hash; |
| 1505 | kj::OneOf<kj::String, V8Ref<v8::Symbol>> inner; |
| 1506 | |
| 1507 | kj::OneOf<kj::StringPtr, v8::Local<v8::Symbol>> getUnwrapped(v8::Isolate* isolate); |
| 1508 | |
| 1509 | friend class NameWrapper; |
| 1510 | |
| 1511 | void visitForGc(GcVisitor& visitor); |
| 1512 | |
| 1513 | friend class MemoryTracker; |
| 1514 | }; |
| 1515 | |
| 1516 | // jsg::Function<T> behaves much like kj::Function<T>, but can be passed to/from JS. It works in |
| 1517 | // both directions: you can receive a jsg::Function from JavaScript and call it from C++, and you |
| 1518 | // can also initialize a jsg::Function from a C++ lambda and pass it back to JavaScript. |
| 1519 | // |
| 1520 | // Since the function could be backed by JavaScript, when calling it, you must always pass |
| 1521 | // `jsg::Lock&` as the first parameter. When implementing a `jsg::Function` using a C++ lambda, |
| 1522 | // the lambda should similarly take `jsg::Lock&` as the first parameter. Note that this first |
| 1523 | // parameter is not declared in the function's signature. For example, `jsg::Function<int(int)>` |
| 1524 | // declares a function that accepts a parameter of type int and returns an int. However, when |
| 1525 | // actually calling it, you must still pass `jsg::Lock&`, with the `int` as the second parameter. |
| 1526 | // (Of course, from the JavaScript side, the lock parameter is hidden, and the `int` is in fact |
| 1527 | // the first parameter.) |
| 1528 | // |
| 1529 | // jsg::Function can be visited using a GcVisitor. If a jsg::Function is initialized from a |
| 1530 | // C++ functor object that happens to have a public method `visitForGc(jsg::GcVisitor&)`, then |
| 1531 | // it will arrange for that method to be called during GC tracing. |
| 1532 | // |
| 1533 | // Note that, obviously, a normal C++ lambda cannot have a `visitForGc()` method. So when writing |
| 1534 | // a visitable function in C++, you have to write out a struct or class with an `operator()` |
| 1535 | // method and a `visitForGc()` method. That's a bit of a pain, so the macro JSG_VISITABLE_LAMBDA() |
| 1536 | // is provided to assist. This lets you write something like a lambda expression where some of the |
| 1537 | // captured variables can be GC visited. Example: |
| 1538 | // |
| 1539 | // jsg::Function<void(int)> myFunc = |
| 1540 | // JSG_VISITABLE_LAMBDA((foo = getFoo(), bar, &baz), |
| 1541 | // (foo, baz.handle), |
| 1542 | // (jsg::Lock& js, int param) { |
| 1543 | // // ... body of function ... |
| 1544 | // }); |
| 1545 | // |
| 1546 | // The first parameter to JSG_VISITABLE_LAMBDA is your capture list, in exactly the syntax that a |
| 1547 | // regular lambda would use, except in parentheses instead of square brackets. The second |
| 1548 | // parameter is a parenthesized list of visitation expressions. This will literally be used as a |
| 1549 | // parameter list to `gcVisitor.visit()`, e.g. in the above example |
| 1550 | // `gcVisitor.visit(foo, baz.handle)` will be called when visited. Finally, the third parameter |
| 1551 | // is the rest of the lambda expression -- parameter list followed by body block. |
| 1552 | template <typename Signature> |
| 1553 | class Function; |
| 1554 | |
| 1555 | // Use this to unwrap a JavaScript function that should be called as a constructor (with `new`). |
| 1556 | // The return type in this case is the constructed type. `Constructor` is a subclass of `Function`; |
| 1557 | // it can be used in all the same ways. |
| 1558 | template <typename T> |
| 1559 | class Constructor; |
| 1560 | |
| 1561 | // jsg::Promise<T> wraps a JavaScript promise. Use it when you want to pass Promises to or from |
| 1562 | // JavaScript. |
| 1563 | // |
| 1564 | // jsg::Promise<T> offers a `.then()` method which looks a lot like kj::Promise<T>'s similar |
| 1565 | // function, except that you must pass `Lock&` to it, and it passes `Lock&` back to the callback: |
| 1566 | // |
| 1567 | // Promise<int> promise = ...; |
| 1568 | // Promise<kj::String> promise2 = promise.then(js, |
| 1569 | // [](Lock& js, int val) { return kj::str(val); }) |
| 1570 | // |
| 1571 | // Unlike kj::Promise, jsg::Promises run on the V8 microtask loop, NOT on the KJ event loop. That |
| 1572 | // implies that the isolate is already locked and active during callbacks, and control does not |
| 1573 | // return to the KJ event loop at all if a promise continuation is immediately runnable. |
| 1574 | // |
| 1575 | // `.catch_()` and two-argument `.then()` are supported. Thrown exceptions are represented using |
| 1576 | // `jsg::Value`, since technically JavaScript allows throwing any type. |
| 1577 | // |
| 1578 | // The type T does not have to be convertible to/from JavaScript unless a Promise<T> is actually |
| 1579 | // passed to/from JavaScript. That is, you can have an intermediate Promise<U> where U is a type |
| 1580 | // that has no JavaScript representation. What actually happens is, when a Promise<T> is passed |
| 1581 | // from JS into C++, JSG adds a .then() which unwraps the value T, and when a Promise<T> is |
| 1582 | // passed back to JS, JSG adds a .then() to wrap the value again. |
| 1583 | // |
| 1584 | // If the type T is GC visitable (i.e. it is a type that you could pass to GcVisitor::visit()), |
| 1585 | // then the system will arrange to correctly visit it when the T is wrapped in a Promise. |
| 1586 | // Additionally, if a continuation function passed to `.then()` is GC-visitable, it will similarly |
| 1587 | // be visited. JSG_VISITABLE_LAMBDA is a useful in conjunction with `.then()` (see jsg::Function, |
| 1588 | // above). |
| 1589 | // |
| 1590 | // Unlike KJ promises, dropping a jsg::Promise does not cancel it. However, like a KJ promise, |
| 1591 | // a jsg::Promise can only have `.then()` called on it once; the continuation consumes the value. |
| 1592 | // This is so that pass-by-move C++ types can safely be passed through jsg::Promises. Of course, |
| 1593 | // once returned to JavaScript, JS code is free to call `.then()` as many times as it wants; this |
| 1594 | // restriction only applies to calling `.then()` in C++. |
| 1595 | // |
| 1596 | // When a JSG method returns a Promise, the system ensures that the object on which the method |
| 1597 | // was called will not be GC'ed until the Promise resolves (or is itself GC'ed, indicating it will |
| 1598 | // never resolve). This is a convenience so that method implementations that return promises do |
| 1599 | // not need to carefully capture a reference to `JSG_THIS`. |
| 1600 | // |
| 1601 | // You can construct an immediate Promise value using js.resolvedPromise() and |
| 1602 | // js.rejectedPromise() (see below). |
| 1603 | // |
| 1604 | // You can also create a promise/resolver pair: |
| 1605 | // |
| 1606 | // auto [promise, resolver] = js.newPromiseAndResolver<kj::String>(); |
| 1607 | // resolver.resolve(js, kj::str(foo)); |
| 1608 | // |
| 1609 | // The Promise exposes a markAsHandled() API that will mark JavaScript Promise such that rejections |
| 1610 | // are not reported to the isolate's unhandled rejection tracking mechanisms. Importantly, any then |
| 1611 | // then() or catch_() continuation on either type will return an unhandled Promise. But, any |
| 1612 | // whenResolved() continuation, and any type handler continuations added internally will be |
| 1613 | // automatically marked handled. Use of markAsHandled() should be rare. It is largely used by Web |
| 1614 | // Platform APIs in certain cases where consumption of a promise is optional, or where a promise |
| 1615 | // rejection is likely to be surfaced via multiple promises (and therefore only needs to be handled |
| 1616 | // once). |
| 1617 | template <typename T> |
| 1618 | class Promise; |
| 1619 | |
| 1620 | template <typename T> |
| 1621 | struct PromiseResolverPair; |
| 1622 | |
| 1623 | // Convenience template to detect a `jsg::Promise` type. |
| 1624 | template <typename T> |
| 1625 | struct IsPromise_ { |
| 1626 | static constexpr bool value = false; |
| 1627 | }; |
| 1628 | template <typename T> |
| 1629 | struct IsPromise_<Promise<T>> { |
| 1630 | static constexpr bool value = true; |
| 1631 | }; |
| 1632 | template <typename T> |
| 1633 | constexpr bool isPromise() { |
| 1634 | return IsPromise_<T>::value; |
| 1635 | } |
| 1636 | |
| 1637 | // Convenience template to strip off `jsg::Promise`. |
| 1638 | template <typename T> |
| 1639 | struct RemovePromise_ { |
| 1640 | using Type = T; |
| 1641 | }; |
| 1642 | template <typename T> |
| 1643 | struct RemovePromise_<Promise<T>> { |
| 1644 | using Type = T; |
| 1645 | }; |
| 1646 | template <typename T> |
| 1647 | using RemovePromise = RemovePromise_<T>::Type; |
| 1648 | |
| 1649 | // Convenience template to add `jsg::Promise` if it is not present. |
| 1650 | template <typename T> |
| 1651 | struct MaintainPromise_ { |
| 1652 | using Type = Promise<T>; |
| 1653 | }; |
| 1654 | template <typename T> |
| 1655 | struct MaintainPromise_<Promise<T>> { |
| 1656 | using Type = Promise<T>; |
| 1657 | }; |
| 1658 | template <typename T> |
| 1659 | using MaintainPromise = MaintainPromise_<T>::Type; |
| 1660 | |
| 1661 | // Convenience template to calculate the return type of a function when passed parameter type T. |
| 1662 | // `T = void` is understood to mean no parameters. |
| 1663 | template <typename Func, typename T, bool passLock> |
| 1664 | struct ReturnType_; |
| 1665 | template <typename Func, typename T> |
| 1666 | struct ReturnType_<Func, T, false> { |
| 1667 | using Type = decltype(kj::instance<Func>()(kj::instance<T>())); |
| 1668 | }; |
| 1669 | template <typename Func, typename T> |
| 1670 | struct ReturnType_<Func, T, true> { |
| 1671 | using Type = decltype(kj::instance<Func>()(kj::instance<Lock&>(), kj::instance<T>())); |
| 1672 | }; |
| 1673 | template <typename Func> |
| 1674 | struct ReturnType_<Func, void, false> { |
| 1675 | using Type = decltype(kj::instance<Func>()()); |
| 1676 | }; |
| 1677 | template <typename Func> |
| 1678 | struct ReturnType_<Func, void, true> { |
| 1679 | using Type = decltype(kj::instance<Func>()(kj::instance<Lock&>())); |
| 1680 | }; |
| 1681 | template <typename Func, typename T, bool passLock = false> |
| 1682 | using ReturnType = ReturnType_<Func, T, passLock>::Type; |
| 1683 | |
| 1684 | // Convenience template to produce a promise for the result of calling a function with the given |
| 1685 | // parameter type. This wraps the function's result type in `jsg::Promise` UNLESS the function |
| 1686 | // already returns a `jsg::Promise`, in which case the type is unchanged. |
| 1687 | // TODO(cleanup): The passLock = false variation is currently only used for js.evalNow(). |
| 1688 | // It would be nice to refactor that a bit so we can clean up this template and simplify. |
| 1689 | template <typename Func, typename Param, bool passLock> |
| 1690 | using PromiseForResult = MaintainPromise<ReturnType<Func, Param, passLock>>; |
| 1691 | |
| 1692 | // All types declared with JSG_RESOURCE_TYPE which are intended to be used as the global object |
| 1693 | // must inherit jsg::ContextGlobal, in addition to inheriting jsg::Object |
| 1694 | // (or a subclass of jsg::Object). |
| 1695 | // jsg::Object should always be the first inherited class, and jsg::ContextGlobal second. |
| 1696 | // The lifetime of the global object matches the lifetime of the JavaScript context. |
| 1697 | class ContextGlobal { |
| 1698 | public: |
| 1699 | ContextGlobal() {} |
| 1700 | |
| 1701 | KJ_DISALLOW_COPY_AND_MOVE(ContextGlobal); |
| 1702 | |
| 1703 | const capnp::SchemaLoader& getSchemaLoader(); |
| 1704 | |
| 1705 | private: |
| 1706 | // This opaque owner is used to keep the ModuleRegistry alive as long as the ContextGlobal |
| 1707 | // object is alive. This may be the legacy or new module registry, depending which one is |
| 1708 | // in use. We don't care about the actual type here, just that it is kept alive. |
| 1709 | kj::Own<void> moduleRegistryBackingOwner; |
| 1710 | kj::Maybe<const capnp::SchemaLoader&> schemaLoader; |
| 1711 | |
| 1712 | void setModuleRegistryBackingOwner(kj::Own<void> registry) { |
| 1713 | moduleRegistryBackingOwner = kj::mv(registry); |
| 1714 | } |
| 1715 | void setSchemaLoader(const capnp::SchemaLoader& schemaLoader); |
| 1716 | |
| 1717 | template <typename, typename> |
| 1718 | friend class ResourceWrapper; |
| 1719 | }; |
| 1720 | |
| 1721 | // Reference to a JavaScript context whose global object wraps a C++ object of type T. This is |
| 1722 | // similar to Ref but not the same, since JsContext provides access to the Context itself, |
| 1723 | // which is more than just the global object. |
| 1724 | template <typename T> |
| 1725 | class JsContext { |
| 1726 | public: |
| 1727 | static_assert( |
| 1728 | std::is_base_of_v<ContextGlobal, T>, "context global type must extend jsg::ContextGlobal"); |
| 1729 | |
| 1730 | JsContext(v8::Local<v8::Context> handle, Ref<T> object) |
| 1731 | : handle(v8::Isolate::GetCurrent(), handle), |
| 1732 | object(kj::mv(object)) {} |
| 1733 | |
| 1734 | JsContext(JsContext&&) = default; |
| 1735 | KJ_DISALLOW_COPY(JsContext); |
| 1736 | |
| 1737 | T& operator*() { |
| 1738 | return *object; |
| 1739 | } |
| 1740 | T* operator->() { |
| 1741 | return object.get(); |
| 1742 | } |
| 1743 | |
| 1744 | v8::Local<v8::Context> getHandle(v8::Isolate* isolate) const { |
| 1745 | return handle.Get(isolate); |
| 1746 | } |
| 1747 | v8::Local<v8::Context> getHandle(Lock& js) const; |
| 1748 | |
| 1749 | private: |
| 1750 | v8::Global<v8::Context> handle; |
| 1751 | Ref<T> object; |
| 1752 | }; |
| 1753 | |
| 1754 | class BufferSource; |
| 1755 | |
| 1756 | constexpr bool hasPublicVisitForGc_(...) { |
| 1757 | return false; |
| 1758 | } |
| 1759 | template <typename T, typename = decltype(&T::visitForGc)> |
| 1760 | constexpr bool hasPublicVisitForGc_(T*) { |
| 1761 | return true; |
| 1762 | } |
| 1763 | |
| 1764 | template <typename T> |
| 1765 | constexpr bool hasPublicVisitForGc() { |
| 1766 | return hasPublicVisitForGc_(static_cast<T*>(nullptr)); |
| 1767 | } |
| 1768 | |
| 1769 | // Visitor used during garbage collection. Any resource class that holds `Ref`s should |
| 1770 | // implement GC visitation by declaring a private method like: |
| 1771 | // |
| 1772 | // private: |
| 1773 | // void visitForGc(GcVisitor& visitor); |
| 1774 | // |
| 1775 | // In this method, call visitor.visit() on each `Ref` owned by the object. |
| 1776 | // |
| 1777 | // A `visitForGc()` method does NOT need to handle visiting superclasses. The JSG framework will |
| 1778 | // automatically discover the presence of `visitForGc()` in each class in the hierarchy and will |
| 1779 | // arrange for them all to be called. (Thus, when adding a new `visitForGc()` method to a class |
| 1780 | // that has many subclasses, there is no need to update the subclasses.) |
| 1781 | // |
| 1782 | // Functors (freestanding functions/callbacks/lambdas, not declared as resources) can also |
| 1783 | // implement GC visitation. To do so, implement the function as a struct with `operator()`, and |
| 1784 | // also give the function a `visitForGc()` method. In this case, `visitForGc()` must be public. |
| 1785 | // |
| 1786 | // GC visitation is optional. If your type owns no `Ref`s, it can skip implementing |
| 1787 | // `visitForGc()`. You can also omit `visitForGc()` if you don't care about the possibility of |
| 1788 | // reference cycles. Any `Ref` which is not explicitly visited will not be eligible for |
| 1789 | // garbage collection at all. Hence, failure to implement proper visitation may lead to memory |
| 1790 | // leaks, but NOT to use-after-free. |
| 1791 | // |
| 1792 | // Note that GC visitation technically only collects JavaScript objects, including wrapper |
| 1793 | // objects. C++ objects will not be collected if they contain reference cycles entirely in C++ |
| 1794 | // land. That is, if you have two C++ objects that contain `Ref`s to each other, and you |
| 1795 | // implement GC visitation, the JavaScript wrapper objects wrapping these C++ objects will be |
| 1796 | // collected, but the C++ objects will not -- a `Ref` can never becomes "dangling", and |
| 1797 | // therefore the C++ objects cannot be destroyed because there's no correct order in which to |
| 1798 | // destroy them. To avoid this situation, make sure your C++ objects have clear ownership, so |
| 1799 | // that the reference graph is a DAG, just like you always would in C++. |
| 1800 | class GcVisitor { |
| 1801 | public: |
| 1802 | template <typename T> |
| 1803 | void visit(Ref<T>& ref) { |
| 1804 | ref.inner->visitRef(*this, ref.parent, ref.strong); |
| 1805 | } |
| 1806 | |
| 1807 | template <typename T> |
| 1808 | void visit(kj::Maybe<Ref<T>>& maybeRef) { |
| 1809 | KJ_IF_SOME(ref, maybeRef) { |
| 1810 | visit(ref); |
| 1811 | } |
| 1812 | } |
| 1813 | |
| 1814 | void visit(Data& data); |
| 1815 | |
| 1816 | /// Visit a raw `v8::Global<Value>` + `v8::TracedReference<Data>` pair, |
| 1817 | /// implementing the same strongโtraced dual-mode switching as `visit(Data&)`. |
| 1818 | /// |
| 1819 | /// Used by the Rust JSG FFI to support `v8::Global<T>` fields on Rust |
| 1820 | /// resources without a full `jsg::Data` wrapper. |
| 1821 | void visit(v8::Global<v8::Value>& strong, v8::TracedReference<v8::Data>& traced); |
| 1822 | |
| 1823 | void visit(kj::Maybe<Data>& maybeData) { |
| 1824 | KJ_IF_SOME(data, maybeData) { |
| 1825 | visit(data); |
| 1826 | } |
| 1827 | } |
| 1828 | |
| 1829 | template <typename T> |
| 1830 | void visit(V8Ref<T>& value) { |
| 1831 | visit(static_cast<Data&>(value)); |
| 1832 | } |
| 1833 | |
| 1834 | template <typename T> |
| 1835 | void visit(kj::Maybe<V8Ref<T>>& maybeValue) { |
| 1836 | KJ_IF_SOME(value, maybeValue) { |
| 1837 | visit(value); |
| 1838 | } |
| 1839 | } |
| 1840 | |
| 1841 | void visit(BufferSource& bufferSource); |
| 1842 | |
| 1843 | template <typename T, typename = kj::EnableIf<hasPublicVisitForGc<T>()>()> |
| 1844 | void visit(T& supportsVisit) { |
| 1845 | supportsVisit.visitForGc(*this); |
| 1846 | } |
| 1847 | |
| 1848 | template <typename T, typename = kj::EnableIf<hasPublicVisitForGc<T>()>()> |
| 1849 | void visit(kj::Maybe<T>& maybeSupportsVisit) { |
| 1850 | KJ_IF_SOME(supportsVisit, maybeSupportsVisit) { |
| 1851 | supportsVisit.visitForGc(*this); |
| 1852 | } |
| 1853 | } |
| 1854 | |
| 1855 | void visit() {} |
| 1856 | |
| 1857 | template <typename T, typename U, typename... Args> |
| 1858 | void visit(T& t, U& u, Args&... remaining) { |
| 1859 | visit(t); |
| 1860 | visit(u, kj::fwd<Args&>(remaining)...); |
| 1861 | } |
| 1862 | |
| 1863 | void visitAll(auto& collection) { |
| 1864 | for (auto& item: collection) { |
| 1865 | visit(item); |
| 1866 | } |
| 1867 | } |
| 1868 | |
| 1869 | private: |
| 1870 | Wrappable& parent; |
| 1871 | kj::Maybe<cppgc::Visitor&> cppgcVisitor; |
| 1872 | |
| 1873 | explicit GcVisitor(Wrappable& parent, kj::Maybe<cppgc::Visitor&> cppgcVisitor) |
| 1874 | : parent(parent), |
| 1875 | cppgcVisitor(cppgcVisitor) {} |
| 1876 | KJ_DISALLOW_COPY_AND_MOVE(GcVisitor); |
| 1877 | |
| 1878 | friend class Wrappable; |
| 1879 | friend class Object; |
| 1880 | friend class HeapTracer; |
| 1881 | }; |
| 1882 | |
| 1883 | constexpr bool isGcVisitable_(...) { |
| 1884 | return false; |
| 1885 | } |
| 1886 | template <typename T, typename = decltype(kj::instance<GcVisitor>().visit(kj::instance<T&>()))> |
| 1887 | constexpr bool isGcVisitable_(T*) { |
| 1888 | return true; |
| 1889 | } |
| 1890 | |
| 1891 | template <typename T> |
| 1892 | constexpr bool isGcVisitable() { |
| 1893 | return isGcVisitable_(static_cast<T*>(nullptr)); |
| 1894 | } |
| 1895 | |
| 1896 | // TypeHandler translates between V8 values and local values for a particular type T. |
| 1897 | // |
| 1898 | // When you define a function or method that is to be wrapped by V8, you can append TypeHandler |
| 1899 | // references to your argument list, and they will automatically be filled in by the caller. |
| 1900 | // This allows you to manually manage objects of this type in your code. For example, you could |
| 1901 | // use this to manually test two different possible input types: |
| 1902 | // |
| 1903 | // void myMethod(v8::Local<v8::Value> handle, |
| 1904 | // const TypeHandler<MyType1>& wrapper, |
| 1905 | // const TypeHandler<MyType2>& wrapper) { |
| 1906 | // KJ_IF_SOME(value1, wrapper.tryUnwrap(handle)) { |
| 1907 | // value1.someMyType1Method(); |
| 1908 | // } KJ_IF_SOME(value2, wrapper.tryUnwrap(handle)) { |
| 1909 | // value2.someMyType2Method(); |
| 1910 | // } |
| 1911 | // } |
| 1912 | // |
| 1913 | // To use a JSG_RESOURCE_TYPE in the TypeHandler, it must be listed in your isolate type's |
| 1914 | // JSG_DECLARE_ISOLATE_TYPE declaration. See JSG_DECLARE_ISOLATE_TYPE in setup.h for info. |
| 1915 | // For resource types, also need to wrap in Ref, i.e. `TypeHandler<jsg::Ref<T>>`. |
| 1916 | template <typename T> |
| 1917 | class TypeHandler { |
| 1918 | public: |
| 1919 | // --------------------------------------------------------------------------- |
| 1920 | // Interface for value types (i.e. types not declared using JSG_RESOURCE_TYPE). |
| 1921 | // |
| 1922 | // This includes builtin types, e.g. `double` or `kj::String`. |
| 1923 | // |
| 1924 | // These methods will fail for resource types. |
| 1925 | |
| 1926 | // Wrap by value. |
| 1927 | virtual v8::Local<v8::Value> wrap(Lock& js, T value) const = 0; |
| 1928 | |
| 1929 | // Unwrap by value. Returns null if not the right type. |
| 1930 | virtual kj::Maybe<T> tryUnwrap(Lock& js, v8::Local<v8::Value> handle) const = 0; |
| 1931 | }; |
| 1932 | |
| 1933 | // Utility that allows C++ code in a resource type to examine properties that have been added to |
| 1934 | // its JavaScript wrapper. |
| 1935 | // |
| 1936 | // To use this, add a member of type `PropertyReflection<T>` to your resource type, then after |
| 1937 | // your JSG_RESOURCE_TYPE block (NOT inside it; at the class scope), write |
| 1938 | // `JSG_REFLECTION(name)`. You will then be able to use the reflection to read properties |
| 1939 | // set on the JavaScript side, interpreting them as the type `T`. |
| 1940 | // |
| 1941 | // class Foo: public jsg::Object { |
| 1942 | // public: |
| 1943 | // ... |
| 1944 | // JSG_RESOURCE_TYPE(EventTarget) { |
| 1945 | // ... |
| 1946 | // } |
| 1947 | // JSG_REFLECTION(intReader, stringReader); |
| 1948 | // private: |
| 1949 | // PropertyReflection<int> intReader; |
| 1950 | // PropertyReflection<kj::String> stringReader; |
| 1951 | // } |
| 1952 | // |
| 1953 | // PropertyReflection's trick is that it isn't initialized until the JavaScript wrapper is |
| 1954 | // created. Until that point, get() just always returns nullptr. |
| 1955 | // |
| 1956 | // PropertyReflection's main use case is reading event handler `onfoo` properties. That is, |
| 1957 | // traditionally, instead of using `obj.addEventListener("foo", func)` to register an event |
| 1958 | // handler, you can also do `obj.onfoo = func`. |
| 1959 | template <typename T> |
| 1960 | class PropertyReflection { |
| 1961 | public: |
| 1962 | // Read the property of this object called `name`, unwrapping it as type `T`. |
| 1963 | kj::Maybe<T> get(Lock& js, kj::StringPtr name); |
| 1964 | |
| 1965 | // Read the property of this object called `name`, unwrapping it as type `T`. |
| 1966 | kj::Maybe<T> get(v8::Isolate* isolate, kj::StringPtr name) { |
| 1967 | v8::HandleScope scope(isolate); |
| 1968 | KJ_IF_SOME(s, self) { |
| 1969 | KJ_IF_SOME(h, s.tryGetHandle(isolate)) { |
| 1970 | return unwrapper(isolate, h, name); |
| 1971 | } |
| 1972 | } |
| 1973 | return kj::none; |
| 1974 | } |
| 1975 | |
| 1976 | // TODO(someday): Support for reading Symbols and Privates? |
| 1977 | |
| 1978 | private: |
| 1979 | kj::Maybe<Wrappable&> self; |
| 1980 | |
| 1981 | using Unwrapper = kj::Maybe<T>(v8::Isolate*, v8::Local<v8::Object> object, kj::StringPtr name); |
| 1982 | Unwrapper* unwrapper = nullptr; |
| 1983 | |
| 1984 | template <typename, typename...> |
| 1985 | friend class TypeWrapper; |
| 1986 | }; |
| 1987 | |
| 1988 | template <typename T> |
| 1989 | concept CoercibleType = kj::isSameType<kj::String, T>() || kj::isSameType<USVString, T>() || |
| 1990 | kj::isSameType<DOMString, T>() || kj::isSameType<bool, T>() || kj::isSameType<double, T>(); |
| 1991 | // When updating this list, be sure to keep the corresponding checks in the NonCoercibleWrapper |
| 1992 | // class in value.h updated as well. |
| 1993 | |
| 1994 | // By default types in JavaScript can be implicitly converted to other types as needed. This |
| 1995 | // can lead to surprising results. For instance, passing null into an API method that accepts |
| 1996 | // string will have the null coerced into the string value "null". The NonCoercible type can |
| 1997 | // be used to disable automatic type coercion in APIs. For instance, NonCoercible<kj::String> |
| 1998 | // will ensure that any value other than a string will be rejected with a TypeError. |
| 1999 | // |
| 2000 | // Here, T can be only one of several types that support coercion: |
| 2001 | // |
| 2002 | // * kj::String, jsg::USVString, jsg::DOMString (value must be a string) |
| 2003 | // * bool (value must be a boolean) |
| 2004 | // * double (value must be a number) |
| 2005 | // |
| 2006 | // It should be pointed out that using NonCoercible<T> runs counter to Web IDL and general |
| 2007 | // Web Platform API best practices, which use type coercion fairly often. However, in certain |
| 2008 | // Cloudflare-specific APIs, automatic coercion can cause surprising developer experience |
| 2009 | // issues. Only use NonCoercible if you have a good reason to disable coercion. When in |
| 2010 | // doubt, don't use it. |
| 2011 | template <CoercibleType T> |
| 2012 | struct NonCoercible { |
| 2013 | T value; |
| 2014 | }; |
| 2015 | |
| 2016 | // ----------------------------------------------------------------------------- |
| 2017 | |
| 2018 | // A Sequence<T> in C++ corresponds to a Sequence IDL type. A sequence is a list of values |
| 2019 | // that may or may not be an array. The key difference between the kj::Array mapping in |
| 2020 | // JSG and a jsg::Sequence, is that the jsg::Sequence can be initialized from any object |
| 2021 | // that exposes an @@iterable symbol. However, when a Sequence is surfaced back up to |
| 2022 | // JavaScript, it will always be an array. |
| 2023 | // |
| 2024 | // At the C++ level, the Sequence itself is just a kj::Array<Value>. |
| 2025 | // |
| 2026 | // Both jsg::Sequence and jsg::Generator provide the ability to work with synchronous |
| 2027 | // iterable/generator objects. The key difference is that jsg::Sequence will always |
| 2028 | // produce a kj::Array of the elements, does not allow for early termination of the |
| 2029 | // iteration, and does not provide access to the return value. jsg::Generator, on the |
| 2030 | // other hand, allows performing an action on each individual item, terminating the |
| 2031 | // iteration early, and retrieving the generators final return value, if any. |
| 2032 | template <typename T> |
| 2033 | struct Sequence; |
| 2034 | |
| 2035 | // jsg::Generator wraps a JavaScript synchronous generator. |
| 2036 | // |
| 2037 | // jsg::Generator offers a `.forEach()` method that will invoke a callback function for |
| 2038 | // each individual item produced by the generator: |
| 2039 | // |
| 2040 | // Generator<int> generator = ...; |
| 2041 | // generator.forEach(js, [](Lock& js, int val, GeneratorContext<T> context) { |
| 2042 | // // Do something with val. |
| 2043 | // // To exit early from the iteration, either call `context.return_()`, |
| 2044 | // // which will call the `.return()` method on the underlying generator, |
| 2045 | // // or throw a JavaScript exception, which will call the `.throw()` |
| 2046 | // // method on the underlying generator. |
| 2047 | // }); |
| 2048 | // |
| 2049 | // The Generator<T> is intended only to be used when receiving a Generator object as |
| 2050 | // a parameter. Instances of Generator<T> cannot be passed back out to JavaScript. Refer |
| 2051 | // to the documentation for JSG_ITERATOR to see how to create and pass Generator/Iterable |
| 2052 | // objects back out to JavaScript. |
| 2053 | // |
| 2054 | // The `.forEach()` method is fully synchronous and will fully consume the generator |
| 2055 | // before it returns. Calling `.forEach()` a second time on the generator will return |
| 2056 | // immediately as a non-op. |
| 2057 | template <typename T> |
| 2058 | class Generator; |
| 2059 | |
| 2060 | // The jsg::AsyncGenerator wraps a JavaScript asynchronous generator. |
| 2061 | // |
| 2062 | // The jsg::AsyncGenerator is similar to jsg::Generator except that it supports |
| 2063 | // async iteration over the individual elements produced by the generator. The |
| 2064 | // `.forEach()` method returns a `Promise<kj::Maybe<T>>>` that is resolved once the |
| 2065 | // generator as been fully consumed. The callback passed in to `.forEach()` must |
| 2066 | // also return a `Promise<void>` that is resolved whenever the item has been consumed |
| 2067 | // and the iterator should advance to the next item. |
| 2068 | // |
| 2069 | // AsyncGenerator<int> generator = ...; |
| 2070 | // generator.forEach(js, [](Lock& js, int val, GeneratorContext<T> context) { |
| 2071 | // // Do something with val. |
| 2072 | // // To exit early from the iteration, either call `context.return_()`, |
| 2073 | // // which will call the `.return()` method on the underlying generator, |
| 2074 | // // or throw a JavaScript exception, which will call the `.throw()` |
| 2075 | // // method on the underlying generator. |
| 2076 | // return js.resolvedPromise(); |
| 2077 | // }).then(js, [](Lock&, kj::Maybe<T>) { KJ_DBG("DONE!"); }); |
| 2078 | // |
| 2079 | // The `.forEach()` method will fully consume the generator, returning a Promise |
| 2080 | // that is resolved once the generator completes. Calling `.forEach()` a second |
| 2081 | // time on the generator will return an immediately resolved promise. |
| 2082 | template <typename T> |
| 2083 | class AsyncGenerator; |
| 2084 | |
| 2085 | // The jsg::GeneratorContext is used with both jsg::Generator and jsg::AsyncGenerator |
| 2086 | // to allow for early termination of the generator iteration. |
| 2087 | template <typename T> |
| 2088 | class GeneratorContext; |
| 2089 | |
| 2090 | // ----------------------------------------------------------------------------- |
| 2091 | |
| 2092 | struct JsgConfig { |
| 2093 | bool noSubstituteNull = false; |
| 2094 | bool unwrapCustomThenables = false; |
| 2095 | bool fetchIterableTypeSupport = false; |
| 2096 | bool fetchIterableTypeSupportOverrideAdjustment = false; |
| 2097 | bool fastApiEnabled = false; |
| 2098 | }; |
| 2099 | |
| 2100 | static constexpr JsgConfig DEFAULT_JSG_CONFIG = {}; |
| 2101 | |
| 2102 | template <typename Config> |
| 2103 | static const JsgConfig& getConfig(const Config& config) { |
| 2104 | if constexpr (kj::isSameType<Config, JsgConfig>() || kj::canConvert<Config, JsgConfig>()) { |
| 2105 | // Returning a reference to a parameter is harmless here since call sites pass in a reference to |
| 2106 | // config, which they can continue to use if returned here. |
| 2107 | // NOLINTNEXTLINE(bugprone-return-const-ref-from-parameter) |
| 2108 | return config; |
| 2109 | } else { |
| 2110 | return DEFAULT_JSG_CONFIG; |
| 2111 | } |
| 2112 | } |
| 2113 | |
| 2114 | // ----------------------------------------------------------------------------- |
| 2115 | |
| 2116 | class IsolateBase; |
| 2117 | template <typename TypeWrapper> |
| 2118 | class Isolate; |
| 2119 | // Defined in setup.h -- most code doesn't need to use these directly. |
| 2120 | |
| 2121 | template <typename T> |
| 2122 | constexpr bool isV8Ref(T*) { |
| 2123 | return false; |
| 2124 | } |
| 2125 | template <typename T> |
| 2126 | constexpr bool isV8Ref(V8Ref<T>*) { |
| 2127 | return true; |
| 2128 | } |
| 2129 | |
| 2130 | template <typename T> |
| 2131 | constexpr bool isV8Ref() { |
| 2132 | return isV8Ref(static_cast<T*>(nullptr)); |
| 2133 | } |
| 2134 | |
| 2135 | template <typename T> |
| 2136 | constexpr bool isV8Local(T*) { |
| 2137 | return false; |
| 2138 | } |
| 2139 | template <typename T> |
| 2140 | constexpr bool isV8Local(v8::Local<T>*) { |
| 2141 | return true; |
| 2142 | } |
| 2143 | |
| 2144 | template <typename T> |
| 2145 | constexpr bool isV8Local() { |
| 2146 | return isV8Local(static_cast<T*>(nullptr)); |
| 2147 | } |
| 2148 | |
| 2149 | template <typename T> |
| 2150 | constexpr bool isV8MaybeLocal(T*) { |
| 2151 | return false; |
| 2152 | } |
| 2153 | template <typename T> |
| 2154 | constexpr bool isV8MaybeLocal(v8::MaybeLocal<T>*) { |
| 2155 | return true; |
| 2156 | } |
| 2157 | |
| 2158 | template <typename T> |
| 2159 | constexpr bool isV8MaybeLocal() { |
| 2160 | return isV8MaybeLocal(static_cast<T*>(nullptr)); |
| 2161 | } |
| 2162 | |
| 2163 | class AsyncContextFrame; |
| 2164 | template <typename T> |
| 2165 | class JsRef; |
| 2166 | |
| 2167 | #define JS_V8_SYMBOLS(V) \ |
| 2168 | V(AsyncIterator) \ |
| 2169 | V(HasInstance) \ |
| 2170 | V(IsConcatSpreadable) \ |
| 2171 | V(Iterator) \ |
| 2172 | V(Match) \ |
| 2173 | V(Replace) \ |
| 2174 | V(Search) \ |
| 2175 | V(Split) \ |
| 2176 | V(ToPrimitive) \ |
| 2177 | V(ToStringTag) \ |
| 2178 | V(Unscopables) \ |
| 2179 | V(Dispose) \ |
| 2180 | V(AsyncDispose) |
| 2181 | |
| 2182 | class JsValue; |
| 2183 | class JsMessage; |
| 2184 | #define JS_TYPE_CLASSES(V) \ |
| 2185 | V(Object) \ |
| 2186 | V(Boolean) \ |
| 2187 | V(Array) \ |
| 2188 | V(String) \ |
| 2189 | V(Symbol) \ |
| 2190 | V(BigInt) \ |
| 2191 | V(Number) \ |
| 2192 | V(Int32) \ |
| 2193 | V(Uint32) \ |
| 2194 | V(Date) \ |
| 2195 | V(RegExp) \ |
| 2196 | V(Map) \ |
| 2197 | V(Set) \ |
| 2198 | V(Promise) \ |
| 2199 | V(Proxy) \ |
| 2200 | V(Function) \ |
| 2201 | V(Uint8Array) \ |
| 2202 | V(ArrayBuffer) \ |
| 2203 | V(ArrayBufferView) |
| 2204 | |
| 2205 | #define V(Name) class Js##Name; |
| 2206 | JS_TYPE_CLASSES(V) |
| 2207 | #undef V |
| 2208 | |
| 2209 | // JsBufferSource is not in JS_TYPE_CLASSES because there is no v8::BufferSource |
| 2210 | // type (and hence no v8::Value::IsBufferSource() check). It is instead handled |
| 2211 | // with special-case logic in JsValue::tryCast and JsValueWrapper. |
| 2212 | class JsBufferSource; |
| 2213 | |
| 2214 | #define V(Name) || kj::isSameType<T, Js##Name>() |
| 2215 | template <typename T> |
| 2216 | concept IsJsValue = kj::isSameType<T, JsValue>() || |
| 2217 | kj::isSameType<T, JsMessage>() JS_TYPE_CLASSES(V) || kj::isSameType<T, JsBufferSource>(); |
| 2218 | #undef V |
| 2219 | |
| 2220 | class DOMException; |
| 2221 | class ExternalMemoryAdjustment; |
| 2222 | |
| 2223 | // Used to save a reference to an isolate that is responsible for external memory usage. |
| 2224 | // getAdjustment() can be invoked at any time to create a new RAII adjustment object |
| 2225 | // pointing to this isolate. |
| 2226 | // |
| 2227 | // Each isolate has a singleton `ExternalMemoryTarget`, which all `ExternalMemoryAdjustment`s |
| 2228 | // point to. The only purpose of this object is to hold a weak reference back to the isolate; the |
| 2229 | // reference is nulled out when the isolate is destroyed. |
| 2230 | class ExternalMemoryTarget: public kj::AtomicRefcounted { |
| 2231 | public: |
| 2232 | ExternalMemoryTarget(v8::Isolate* isolate): isolate(isolate) {} |
| 2233 | |
| 2234 | ExternalMemoryAdjustment getAdjustment(size_t amount) const; |
| 2235 | |
| 2236 | // Apply any deferred external memory updates. Must be called with isolate locked. |
| 2237 | void applyDeferredMemoryUpdate() const; |
| 2238 | |
| 2239 | // Disconnects the ExternalMemoryTarget from the isolate (called just before destroying the |
| 2240 | // isolate). |
| 2241 | void detach() const; |
| 2242 | |
| 2243 | // These two methods are for tests only. |
| 2244 | bool isIsolateAliveForTest() const; |
| 2245 | int64_t getPendingMemoryUpdateForTest() const; |
| 2246 | |
| 2247 | private: |
| 2248 | void maybeDeferAdjustment(ssize_t amount) const; |
| 2249 | void adjustNow(Lock& js, ssize_t amount) const; |
| 2250 | |
| 2251 | // Mutable so that it can be set null when the isolate is destroyed. |
| 2252 | mutable std::atomic<v8::Isolate*> isolate; |
| 2253 | static_assert(std::atomic<v8::Isolate*>::is_always_lock_free); |
| 2254 | |
| 2255 | // Tracks changes to external memory that were applied from a thread that did not hold the |
| 2256 | // isolate lock. These will be applied the next time the lock is taken. |
| 2257 | mutable std::atomic<int64_t> pendingExternalMemoryUpdate = {0}; |
| 2258 | static_assert(std::atomic<int64_t>::is_always_lock_free); |
| 2259 | |
| 2260 | friend class ExternalMemoryAdjustment; |
| 2261 | }; |
| 2262 | |
| 2263 | // RAII class to adjust the amount of external memory attributed to an isolate. |
| 2264 | // The adjustment will be automatically decremented when the object is destroyed. |
| 2265 | // The allocation amount can be adjusted up or down during the lifetime of an object. |
| 2266 | class ExternalMemoryAdjustment final { |
| 2267 | public: |
| 2268 | ExternalMemoryAdjustment(kj::Arc<const ExternalMemoryTarget> externalMemory, size_t amount); |
| 2269 | ExternalMemoryAdjustment(ExternalMemoryAdjustment&& other) noexcept; |
| 2270 | ExternalMemoryAdjustment& operator=(ExternalMemoryAdjustment&& other); |
| 2271 | KJ_DISALLOW_COPY(ExternalMemoryAdjustment); |
| 2272 | ~ExternalMemoryAdjustment() noexcept(false); |
| 2273 | |
| 2274 | // Adjust the amount of external memory report up or down. |
| 2275 | void adjust(ssize_t amount); |
| 2276 | |
| 2277 | // Like adjust, except that the adjustment is applied immediately with no deferral. |
| 2278 | void adjustNow(Lock& js, ssize_t amount); |
| 2279 | |
| 2280 | // Set a specific amount of external memory to be attributed, overriding |
| 2281 | // the previous amount. |
| 2282 | void set(size_t amount); |
| 2283 | |
| 2284 | // Like set(), except that the adjustment is applied immediately with no deferral. |
| 2285 | void setNow(Lock& js, size_t amount); |
| 2286 | |
| 2287 | inline size_t getAmount() const { |
| 2288 | return amount; |
| 2289 | } |
| 2290 | |
| 2291 | private: |
| 2292 | kj::Arc<const ExternalMemoryTarget> externalMemory; |
| 2293 | size_t amount = 0; |
| 2294 | |
| 2295 | // If the isolate is locked, adjust the external memory immediately. |
| 2296 | // Otherwise, if we don't have the isolate locked, defer the adjustment to the next |
| 2297 | // time that we do. |
| 2298 | void maybeDeferAdjustment(ssize_t amount); |
| 2299 | }; |
| 2300 | |
| 2301 | // If memory protection keys are enabled, provides the ability to run a function |
| 2302 | // within the scope of a particular protection key associated with the isolate lock. |
| 2303 | // This class is designed to be movable. |
| 2304 | class MemoryProtectionKeyScope final { |
| 2305 | public: |
| 2306 | KJ_DISALLOW_COPY(MemoryProtectionKeyScope); |
| 2307 | MemoryProtectionKeyScope(MemoryProtectionKeyScope&&) = default; |
| 2308 | MemoryProtectionKeyScope& operator=(MemoryProtectionKeyScope&&) = default; |
| 2309 | |
| 2310 | auto runWithKey(auto func) { |
| 2311 | #ifdef V8_ENABLE_SANDBOX |
| 2312 | PkeyScope scope(pkey); |
| 2313 | #endif |
| 2314 | return func(); |
| 2315 | } |
| 2316 | |
| 2317 | private: |
| 2318 | #ifdef V8_ENABLE_SANDBOX |
| 2319 | int pkey; |
| 2320 | MemoryProtectionKeyScope(Lock&); |
| 2321 | |
| 2322 | struct PkeyScope { |
| 2323 | int key; |
| 2324 | int saved; |
| 2325 | PkeyScope(int pkey); |
| 2326 | ~PkeyScope(); |
| 2327 | }; |
| 2328 | #else |
| 2329 | MemoryProtectionKeyScope(Lock&) { |
| 2330 | // No-op if sandboxing is not enabled. |
| 2331 | } |
| 2332 | #endif |
| 2333 | |
| 2334 | friend class Lock; |
| 2335 | }; |
| 2336 | |
| 2337 | // Represents an isolate lock, which allows the current thread to execute JavaScript code within |
| 2338 | // an isolate. A thread must lock an isolate -- obtaining an instance of `Lock` -- before it can |
| 2339 | // manipulate JavaScript objects or execute JavaScript code inside the isolate. |
| 2340 | // |
| 2341 | // The `Lock` interface also provides access to basic JavaScript functionality, such as the |
| 2342 | // ability to construct basic JS values, throw and catch errors, etc. |
| 2343 | // |
| 2344 | // By convention, all functions which manipulate JavaScript take `Lock& js` as their first |
| 2345 | // parameter. A `Lock&` reference must never be stored as an object member nor captured in a |
| 2346 | // lambda, as `Lock`s are always constructed on the stack and so their lifetime is never |
| 2347 | // guaranteed beyond the end of the function call. |
| 2348 | // |
| 2349 | // Methods declared with JSG_METHOD and similar macros may optionally take a `Lock&` as the |
| 2350 | // first parameter. Template magic will automatically discover if the parameter is present and |
| 2351 | // will populate it. Such methods are always invoked under lock whether or not they have a |
| 2352 | // `Lock&` parameter, but it is recommended that you declare the parameter if the function |
| 2353 | // touches the JS heap in any way. This way, if someone wants to call the method directly from |
| 2354 | // C++, they know whether a lock is required. |
| 2355 | // |
| 2356 | // To create a lock in the first place, you have to create a specific instance of |
| 2357 | // Isolate<TypeWrapper>::Lock. Usually this is only done in top-level code, and the Lock is |
| 2358 | // passed down to everyone else from there. See setup.h for details. |
| 2359 | |
| 2360 | class Lock { |
| 2361 | public: |
| 2362 | // The underlying V8 isolate, useful for directly calling V8 APIs. Hopefully, this is rarely |
| 2363 | // needed outside JSG itself. |
| 2364 | v8::Isolate* const v8Isolate; |
| 2365 | |
| 2366 | template <typename T, typename... Params> |
| 2367 | Ref<T> alloc(Params&&... params) { |
| 2368 | // TODO(soon): While it is possible to create jsg::Object instances outside of the |
| 2369 | // isolate lock, we intend to change that in order to improve memory accounting and |
| 2370 | // tracking of objects created while under lock. As such, all instances of jsg::alloc<T>(...) |
| 2371 | // are to be replaced by js.alloc<T>(...). For now, these are functionally equivalent. |
| 2372 | return Ref<T>(kj::refcounted<T>(kj::fwd<Params>(params)...)); |
| 2373 | } |
| 2374 | |
| 2375 | // Like alloc() but attaches an external memory adjustment of size indicated by `accountedSize`. |
| 2376 | template <typename T, typename... Params> |
| 2377 | Ref<T> allocAccounted(size_t accountedSize, Params&&... params) { |
| 2378 | return Ref<T>(kj::refcounted<T>(kj::fwd<Params>(params)...) |
| 2379 | .attach(getExternalMemoryAdjustment(accountedSize))); |
| 2380 | } |
| 2381 | |
| 2382 | // When you want to temporarily use a memory allocation that is protected |
| 2383 | // by the isolate's memory protection key, use this to get a utility that |
| 2384 | // will capture the key and allow you to run a function with the key enabled. |
| 2385 | // The key use case is to allow tempporary access outside of the isolate lock |
| 2386 | // for things like ArrayBuffer backing stores. |
| 2387 | MemoryProtectionKeyScope getMemoryProtectionKeyScope() { |
| 2388 | return MemoryProtectionKeyScope(*this); |
| 2389 | } |
| 2390 | |
| 2391 | v8::Local<v8::Context> v8Context() { |
| 2392 | auto context = v8Isolate->GetCurrentContext(); |
| 2393 | KJ_ASSERT(!context.IsEmpty(), "Isolate has no currently active v8::Context::Scope"); |
| 2394 | return context; |
| 2395 | } |
| 2396 | |
| 2397 | // Get the current Lock for the given V8 isolate. Segfaults if the isolate is not locked. |
| 2398 | // |
| 2399 | // This method is intended to be used in callbacks from V8 that pass an isolate pointer but |
| 2400 | // don't provide any further context. Most code should rely on the caller passing in a `Lock&`. |
| 2401 | static Lock& from(v8::Isolate* v8Isolate) { |
| 2402 | return *reinterpret_cast<Lock*>(v8Isolate->GetData(SET_DATA_LOCK)); |
| 2403 | } |
| 2404 | |
| 2405 | // TODO(someday): A clang-tidy rule to enforce use of Lock::current over |
| 2406 | // v8::Isolate::GetCurrent would be helpful. |
| 2407 | static Lock& current() { |
| 2408 | return from(v8::Isolate::GetCurrent()); |
| 2409 | } |
| 2410 | |
| 2411 | // RAII construct that reports amount of external memory to be manually attributed to |
| 2412 | // the isolate. When the returned ExtrernalMemoryAdjuster is dropped, the amount will |
| 2413 | // be subtracted from the isolate's external memory accounting. If the adjuster is |
| 2414 | // dropped while the isolate lock is not being held, the adjustment will be deferred |
| 2415 | // until the next time the lock is held. The ExternalMemoryAdjustment itself can be |
| 2416 | // moved and can be used to increment or decrement the amount of external memory |
| 2417 | // held. |
| 2418 | ExternalMemoryAdjustment getExternalMemoryAdjustment(int64_t amount = 0); |
| 2419 | |
| 2420 | // Used to save a reference to an isolate that is responsible for external memory usage. |
| 2421 | // getAdjustment() can be invoked at any time to create a new RAII adjustment object |
| 2422 | // pointing to this isolate |
| 2423 | kj::Arc<const ExternalMemoryTarget> getExternalMemoryTarget(); |
| 2424 | |
| 2425 | Value parseJson(kj::ArrayPtr<const char> data); |
| 2426 | Value parseJson(v8::Local<v8::String> text); |
| 2427 | template <typename T> |
| 2428 | kj::String serializeJson(V8Ref<T>& value) { |
| 2429 | return serializeJson(value.getHandle(*this)); |
| 2430 | } |
| 2431 | template <typename T> |
| 2432 | kj::String serializeJson(V8Ref<T>&& value) { |
| 2433 | // Callers expect the rvalue-reference to be consumed, and to ensure |
| 2434 | // that, explicitly move it into a local variable |
| 2435 | auto moved = kj::mv(value); |
| 2436 | return serializeJson(moved.getHandle(*this)); |
| 2437 | } |
| 2438 | |
| 2439 | void recursivelyFreeze(Value& value); |
| 2440 | |
| 2441 | // --------------------------------------------------------------------------- |
| 2442 | // Exception-related stuff |
| 2443 | |
| 2444 | // Converts the KJ exception to a JS exception. If the KJ exception is a tunneled JavaScript |
| 2445 | // error, this reproduces the original error. If it is not a tunneled error, then it is treated |
| 2446 | // as an internal error: the KJ exception message is logged to stderr, and a JavaScript error |
| 2447 | // is returned with a generic description. |
| 2448 | Value exceptionToJs(kj::Exception&& exception, ExceptionToJsOptions options = {}); |
| 2449 | |
| 2450 | JsRef<JsValue> exceptionToJsValue(kj::Exception&& exception, ExceptionToJsOptions options = {}); |
| 2451 | |
| 2452 | // Encodes the given JavaScript exception into a KJ exception, formatting the description in |
| 2453 | // such a way that hopefully exceptionToJs() can reproduce something equivalent to the original |
| 2454 | // JavaScript error. |
| 2455 | kj::Exception exceptionToKj(const JsValue& exception); |
| 2456 | |
| 2457 | // Encodes the given JavaScript exception into a KJ exception, formatting the description in |
| 2458 | // such a way that hopefully exceptionToJs() can reproduce something equivalent to the original |
| 2459 | // JavaScript error. |
| 2460 | kj::Exception exceptionToKj(Value&& exception); |
| 2461 | |
| 2462 | // Throws a JavaScript exception. The exception is scheduled on the isolate, and then an |
| 2463 | // instance of `JsExceptionThrown` is thrown in C++. All places where JavaScript calls into C++ |
| 2464 | // via JSG understand how to handle this and propagate the exception back to JavaScript. |
| 2465 | [[noreturn]] void throwException(Value&& exception); |
| 2466 | |
| 2467 | [[noreturn]] void throwException(kj::Exception&& exception, ExceptionToJsOptions options = {}) { |
| 2468 | throwException(exceptionToJs(kj::mv(exception), options)); |
| 2469 | } |
| 2470 | |
| 2471 | [[noreturn]] void throwException(const JsValue& exception); |
| 2472 | |
| 2473 | // Invokes `func()` synchronously, catching exceptions. In the event of an exception, |
| 2474 | // `errorHandler()` will be called, passing the exception as type `jsg::Value`. |
| 2475 | // |
| 2476 | // KJ exceptions are also caught and will be converted to JS exceptions using exceptionToJs(). |
| 2477 | // |
| 2478 | // Some kinds of exceptions explicitly will not be caught: |
| 2479 | // - Exceptions where JavaScript execution cannot continue, such as the "uncatchable exception" |
| 2480 | // produced by IsolateBase::TerminateExecution(). |
| 2481 | // - C++ exceptions other than `kj::Exception`, e.g. `std::bad_alloc`. These exceptions are |
| 2482 | // assumed to be serious enough that they cannot be caught as if they were JavaScript errors, |
| 2483 | // and instead unwind must continue until C++ catches them. |
| 2484 | // |
| 2485 | // func() and errorHandler() must return the same type; the value they return will be returned |
| 2486 | // from `tryCatch()` itself. |
| 2487 | template <typename Func, typename ErrorHandler> |
| 2488 | auto tryCatch(Func&& func, |
| 2489 | ErrorHandler&& errorHandler, |
| 2490 | // If an exception occurs, convert KJ exceptions to JS exceptions |
| 2491 | // using these options. |
| 2492 | ExceptionToJsOptions options = {}) -> decltype(func()) { |
| 2493 | Value error = nullptr; |
| 2494 | |
| 2495 | { |
| 2496 | v8::TryCatch tryCatch(v8Isolate); |
| 2497 | try { |
| 2498 | return func(); |
| 2499 | } catch (JsExceptionThrown&) { |
| 2500 | // If tryCatch.HasCaught() is false, it typically means that JsExceptionThrown |
| 2501 | // was thrown without an exception actually being scheduled on the isolate. |
| 2502 | // This may happen in particular when the JsExceptionThrown was the result of |
| 2503 | // TerminateExecution() but V8 has since cleared the terminate flag because all |
| 2504 | // JavaScript call frames have been unwound. Hence, we want to treat this the |
| 2505 | // same as if `CanContinue()` returned false. |
| 2506 | // TODO(cleanup): Do more investigation, maybe explicitly check for the termination |
| 2507 | // flag or arrange to maintain our own separate termination flag to avoid confusion. |
| 2508 | if (!tryCatch.CanContinue() || !tryCatch.HasCaught() || tryCatch.Exception().IsEmpty()) { |
| 2509 | tryCatch.ReThrow(); |
| 2510 | throw; |
| 2511 | } |
| 2512 | |
| 2513 | error = Value(v8Isolate, tryCatch.Exception()); |
| 2514 | } catch (kj::Exception& e) { |
| 2515 | error = exceptionToJs(kj::mv(e), options); |
| 2516 | } |
| 2517 | } |
| 2518 | |
| 2519 | // We have to make sure the `v8::TryCatch` is off the stack before invoking `errorHandler`, |
| 2520 | // otherwise the same `TryCatch` will catch any exceptions the error handler throws, ugh. |
| 2521 | return errorHandler(kj::mv(error)); |
| 2522 | } |
| 2523 | |
| 2524 | // Like tryCatch() but returns a Promise<T> that resolves to the result of func() or |
| 2525 | // rejects with the result of errorHandler() if an exception is thrown. |
| 2526 | template <typename T, typename Func> |
| 2527 | Promise<T> tryOrReject(Func&& func) { |
| 2528 | return tryCatch([&]() -> Promise<T> { return toPromise(func()); }, |
| 2529 | [&](Value&& error) -> Promise<T> { return rejectedPromise<T>(kj::mv(error)); }); |
| 2530 | } |
| 2531 | |
| 2532 | // --------------------------------------------------------------------------- |
| 2533 | // Promise-related stuff |
| 2534 | |
| 2535 | // Get a pair of a Promise<T> and a Promise<T>::Resolver that resolves the promise. You should |
| 2536 | // call this like: |
| 2537 | // |
| 2538 | // auto [promise, resolver] = js.newPromiseAndResolver(); |
| 2539 | template <typename T> |
| 2540 | PromiseResolverPair<T> newPromiseAndResolver(); |
| 2541 | |
| 2542 | // Construct an immediately-resolved promise resolving to the given value. |
| 2543 | template <typename T> |
| 2544 | Promise<T> resolvedPromise(T&& value); |
| 2545 | |
| 2546 | // Construct an immediately-resolved promise resolving to the given value. |
| 2547 | Promise<void> resolvedPromise(); |
| 2548 | |
| 2549 | // Construct an immediately-rejected promise throwing the given exception. |
| 2550 | template <typename T> |
| 2551 | Promise<T> rejectedPromise(v8::Local<v8::Value> exception); |
| 2552 | |
| 2553 | // Construct an immediately-rejected promise throwing the given exception. |
| 2554 | template <typename T> |
| 2555 | Promise<T> rejectedPromise(jsg::Value exception); |
| 2556 | |
| 2557 | // Construct an immediately-rejected promise throwing the given exception. |
| 2558 | template <typename T> |
| 2559 | Promise<T> rejectedPromise(kj::Exception&& exception, ExceptionToJsOptions options = {}); |
| 2560 | |
| 2561 | // Like above, but return a pure-JS promise, not a typed Promise. |
| 2562 | JsPromise rejectedJsPromise(jsg::JsValue exception); |
| 2563 | JsPromise rejectedJsPromise(kj::Exception&& exception, ExceptionToJsOptions options = {}); |
| 2564 | JsPromise resolvedJsPromise(jsg::JsValue value); |
| 2565 | |
| 2566 | // Like `kj::evalNow()`, but returns a jsg::Promise for the result. Synchronous exceptions are |
| 2567 | // caught and returned as a rejected promise. |
| 2568 | // |
| 2569 | // If an exception is caught as a result of TerminateExecution() being called, it is rethrown |
| 2570 | // to the caller, not encapsulated in a promise. |
| 2571 | // |
| 2572 | // Note `func` is NOT expected to take `Lock&` as a parameter, as normally func should be a lambda |
| 2573 | // that captures `[&]`, so will capture the caller's lock reference. Capturing the lock here is |
| 2574 | // allowed since `func` is invoked synchronously. |
| 2575 | template <class Func> |
| 2576 | PromiseForResult<Func, void, false> evalNow(Func&& func); |
| 2577 | |
| 2578 | // --------------------------------------------------------------------------- |
| 2579 | // Name/Symbol stuff |
| 2580 | |
| 2581 | // Creates a Name encapsulating a new unique v8::Symbol. |
| 2582 | Name newSymbol(kj::StringPtr symbol); |
| 2583 | |
| 2584 | // Creates a Name encapsulating a name from the global symbol registry. |
| 2585 | // Equivalent to Symbol.for(symbol) in JavaScript. |
| 2586 | Name newSharedSymbol(kj::StringPtr symbol); |
| 2587 | |
| 2588 | // Similar to newSharedSymbol except that it uses a separate isolate registry |
| 2589 | // that is not accessible by JavaScript. |
| 2590 | Name newApiSymbol(kj::StringPtr symbol); |
| 2591 | |
| 2592 | // --------------------------------------------------------------------------- |
| 2593 | // Logging stuff |
| 2594 | |
| 2595 | inline bool areWarningsLogged() const { |
| 2596 | return warningsLogged; |
| 2597 | } |
| 2598 | |
| 2599 | // Emits the warning only if there is anywhere for the log to go (for instance, |
| 2600 | // if debug logging is enabled or the inspector is being used). |
| 2601 | void logWarning(kj::StringPtr message); |
| 2602 | |
| 2603 | // TODO(later): Add the other log variants from IoContext? eg. logWarningOnce, |
| 2604 | // logErrorOnce, logUncaughtException, etc. |
| 2605 | |
| 2606 | // --------------------------------------------------------------------------- |
| 2607 | // v8 Local handle related stuff |
| 2608 | // TODO(cleanup): Direct use of v8::Local handles is discouraged and is something we are trying |
| 2609 | // to move away from. However, there are still plenty of cases where we need to do so. The |
| 2610 | // methods here help avoid directly using v8::Isolate and serve as an interim until we can |
| 2611 | // eliminate direct use as much as possible. |
| 2612 | // Convenience methods to unwrap various types of V8 values. All of these could be done manually |
| 2613 | // via the V8 API, but these methods are much easier. |
| 2614 | |
| 2615 | v8::Local<v8::Value> v8Undefined(); |
| 2616 | v8::Local<v8::Value> v8Null(); |
| 2617 | |
| 2618 | v8::Local<v8::Value> v8Error(kj::StringPtr message); |
| 2619 | v8::Local<v8::Value> v8TypeError(kj::StringPtr message); |
| 2620 | |
| 2621 | void v8Set(v8::Local<v8::Object> obj, V8Ref<v8::String>& name, Value& value); |
| 2622 | void v8Set(v8::Local<v8::Object> obj, kj::StringPtr name, v8::Local<v8::Value> value); |
| 2623 | void v8Set(v8::Local<v8::Object> obj, kj::StringPtr name, Value& value); |
| 2624 | v8::Local<v8::Value> v8Get(v8::Local<v8::Object> obj, kj::StringPtr name); |
| 2625 | v8::Local<v8::Value> v8Get(v8::Local<v8::Array> obj, uint idx); |
| 2626 | bool v8Has(v8::Local<v8::Object> obj, kj::StringPtr name); |
| 2627 | bool v8HasOwn(v8::Local<v8::Object> obj, kj::StringPtr name); |
| 2628 | |
| 2629 | template <typename T> |
| 2630 | V8Ref<T> v8Ref(v8::Local<T> local); |
| 2631 | Data v8Data(v8::Local<v8::Data> data); |
| 2632 | |
| 2633 | kj::String serializeJson(v8::Local<v8::Value> value); |
| 2634 | |
| 2635 | v8::Local<v8::String> wrapString(kj::StringPtr text); |
| 2636 | virtual v8::Local<v8::ArrayBuffer> wrapBytes(kj::Array<byte> data) = 0; |
| 2637 | virtual v8::Local<v8::Function> wrapSimpleFunction(v8::Local<v8::Context> context, |
| 2638 | jsg::Function<void(const v8::FunctionCallbackInfo<v8::Value>& info)> simpleFunction) = 0; |
| 2639 | |
| 2640 | // A variation on wrapSimpleFunction that allows for a return value. While the wrapSimpleFunction |
| 2641 | // implementation passes the FunctionCallbackInfo into the called function, any call to |
| 2642 | // GetReturnValue().Set(...) to specify a return value will be ignored by the FunctorCallback |
| 2643 | // wrapper. The wrapReturningFunction variation forces the wrapper to use the version that |
| 2644 | // pays attention to the return value. |
| 2645 | virtual v8::Local<v8::Function> wrapReturningFunction(v8::Local<v8::Context> context, |
| 2646 | jsg::Function<v8::Local<v8::Value>(const v8::FunctionCallbackInfo<v8::Value>& info)> |
| 2647 | returningFunction) = 0; |
| 2648 | virtual v8::Local<v8::Function> wrapPromiseReturningFunction(v8::Local<v8::Context> context, |
| 2649 | jsg::Function<jsg::Promise<jsg::Value>(const v8::FunctionCallbackInfo<v8::Value>& info)> |
| 2650 | returningFunction) = 0; |
| 2651 | // TODO(later): See if we can easily combine wrapSimpleFunction and wrapReturningFunction |
| 2652 | // into one. |
| 2653 | |
| 2654 | virtual v8::Local<v8::Promise> wrapSimplePromise(Promise<Value> promise) = 0; |
| 2655 | |
| 2656 | bool toBool(v8::Local<v8::Value> value); |
| 2657 | virtual kj::String toString(v8::Local<v8::Value> value) = 0; |
| 2658 | virtual jsg::Dict<v8::Local<v8::Value>> toDict(v8::Local<v8::Value> value) = 0; |
| 2659 | virtual jsg::Dict<JsValue> toDict(const jsg::JsValue& value) = 0; |
| 2660 | virtual Promise<Value> toPromise(v8::Local<v8::Value> promise) = 0; |
| 2661 | |
| 2662 | // --------------------------------------------------------------------------- |
| 2663 | // Setup stuff |
| 2664 | |
| 2665 | // Use to enable/disable dynamic code evaluation (via eval(), new Function(), or WebAssembly). |
| 2666 | void setAllowEval(bool allow); |
| 2667 | |
| 2668 | void setCaptureThrowsAsRejections(bool capture); |
| 2669 | void setUsingEnhancedErrorSerialization(); |
| 2670 | void setUsingFastJsgStruct(); |
| 2671 | bool isUsingFastJsgStruct() const; |
| 2672 | bool isUsingEnhancedErrorSerialization() const; |
| 2673 | |
| 2674 | void setNodeJsCompatEnabled(); |
| 2675 | void setNodeJsProcessV2Enabled(); |
| 2676 | void setRequireReturnsDefaultExportEnabled(); |
| 2677 | void setThrowOnUnrecognizedImportAssertion(); |
| 2678 | bool getThrowOnUnrecognizedImportAssertion() const; |
| 2679 | void setToStringTag(); |
| 2680 | void setImmutablePrototype(); |
| 2681 | void setSpecCompliantPropertyAttributes(); |
| 2682 | void disableTopLevelAwait(); |
| 2683 | |
| 2684 | using Logger = void(Lock&, kj::StringPtr); |
| 2685 | void setLoggerCallback(kj::Function<Logger>&& logger); |
| 2686 | |
| 2687 | using ErrorReporter = void(Lock&, kj::String, const JsValue&, const JsMessage&); |
| 2688 | void setErrorReporterCallback(kj::Function<ErrorReporter>&& errorReporter); |
| 2689 | |
| 2690 | // --------------------------------------------------------------------------- |
| 2691 | // Misc. Stuff |
| 2692 | |
| 2693 | // Sends an immediate request for full GC, this function is to ONLY be used in testing, otherwise |
| 2694 | // it will throw. If a need for a minor GC is needed look at the call in jsg.c++ and the |
| 2695 | // implementation in setup.c++. Use responsibly. |
| 2696 | void requestGcForTesting() const; |
| 2697 | |
| 2698 | // Runs the given function synchronously with a v8::HandleScope on the stack. |
| 2699 | // If the fn returns a v8::Local<T> or v8::MaybeLocal<T> type, then |
| 2700 | // v8::EscapableHandleScope is used ensuring that the v8::Local<T> return |
| 2701 | // value is properly handled. |
| 2702 | auto withinHandleScope(auto&& fn) { |
| 2703 | using Ret = decltype(fn()); |
| 2704 | if constexpr (IsJsValue<Ret>) { |
| 2705 | v8::EscapableHandleScope scope(v8Isolate); |
| 2706 | v8::Local<v8::Value> value = fn(); |
| 2707 | return Ret(scope.Escape(value)); |
| 2708 | } else if constexpr (isV8Local<Ret>()) { |
| 2709 | v8::EscapableHandleScope scope(v8Isolate); |
| 2710 | return scope.Escape(fn()); |
| 2711 | } else if constexpr (isV8MaybeLocal<Ret>()) { |
| 2712 | v8::EscapableHandleScope scope(v8Isolate); |
| 2713 | return scope.EscapeMaybe(fn()); |
| 2714 | } else { |
| 2715 | v8::HandleScope scope(v8Isolate); |
| 2716 | return fn(); |
| 2717 | } |
| 2718 | } |
| 2719 | |
| 2720 | virtual Ref<DOMException> domException( |
| 2721 | kj::String name, kj::String message, kj::Maybe<kj::String> stackValue = kj::none) = 0; |
| 2722 | |
| 2723 | // Get the prototype object for the given C++ type (which must be a JSG_RESOURCE_TYPE). |
| 2724 | // |
| 2725 | // WARNING: A malicious script can tamper with this by overwriting the `prototype` property |
| 2726 | // of the class object. |
| 2727 | template <typename T> |
| 2728 | JsObject getPrototypeFor(); |
| 2729 | |
| 2730 | // ==================================================================================== |
| 2731 | JsObject global() KJ_WARN_UNUSED_RESULT; |
| 2732 | JsValue undefined() KJ_WARN_UNUSED_RESULT; |
| 2733 | JsValue null() KJ_WARN_UNUSED_RESULT; |
| 2734 | JsBoolean boolean(bool val) KJ_WARN_UNUSED_RESULT; |
| 2735 | JsNumber num(double) KJ_WARN_UNUSED_RESULT; |
| 2736 | JsNumber num(float) KJ_WARN_UNUSED_RESULT; |
| 2737 | JsInt32 num(int8_t) KJ_WARN_UNUSED_RESULT; |
| 2738 | JsInt32 num(int16_t) KJ_WARN_UNUSED_RESULT; |
| 2739 | JsInt32 num(int32_t) KJ_WARN_UNUSED_RESULT; |
| 2740 | JsUint32 num(uint8_t) KJ_WARN_UNUSED_RESULT; |
| 2741 | JsUint32 num(uint16_t) KJ_WARN_UNUSED_RESULT; |
| 2742 | JsUint32 num(uint32_t) KJ_WARN_UNUSED_RESULT; |
| 2743 | JsBigInt bigInt(int64_t) KJ_WARN_UNUSED_RESULT; |
| 2744 | JsBigInt bigInt(uint64_t) KJ_WARN_UNUSED_RESULT; |
| 2745 | JsString str() KJ_WARN_UNUSED_RESULT; |
| 2746 | JsString str(kj::ArrayPtr<const char16_t>) KJ_WARN_UNUSED_RESULT; |
| 2747 | JsString str(kj::ArrayPtr<const uint16_t>) KJ_WARN_UNUSED_RESULT; |
| 2748 | JsString str(kj::ArrayPtr<const char>) KJ_WARN_UNUSED_RESULT; |
| 2749 | JsString str(kj::ArrayPtr<const kj::byte>) KJ_WARN_UNUSED_RESULT; |
| 2750 | JsString strIntern(kj::StringPtr) KJ_WARN_UNUSED_RESULT; |
| 2751 | JsString strExtern(kj::ArrayPtr<const char>) KJ_WARN_UNUSED_RESULT; |
| 2752 | JsString strExtern(kj::ArrayPtr<const uint16_t>) KJ_WARN_UNUSED_RESULT; |
| 2753 | JsSymbol symbol(kj::StringPtr) KJ_WARN_UNUSED_RESULT; |
| 2754 | JsSymbol symbolShared(kj::StringPtr) KJ_WARN_UNUSED_RESULT; |
| 2755 | JsSymbol symbolInternal(kj::StringPtr) KJ_WARN_UNUSED_RESULT; |
| 2756 | JsObject obj() KJ_WARN_UNUSED_RESULT; |
| 2757 | JsObject obj(kj::ArrayPtr<const kj::StringPtr> keys, |
| 2758 | kj::ArrayPtr<jsg::JsValue> values) KJ_WARN_UNUSED_RESULT; |
| 2759 | JsObject objNoProto() KJ_WARN_UNUSED_RESULT; |
| 2760 | JsObject objNoProto( |
| 2761 | kj::ArrayPtr<kj::StringPtr> keys, kj::ArrayPtr<jsg::JsValue> values) KJ_WARN_UNUSED_RESULT; |
| 2762 | JsMap map() KJ_WARN_UNUSED_RESULT; |
| 2763 | JsValue external(void*) KJ_WARN_UNUSED_RESULT; |
| 2764 | JsValue error(kj::StringPtr message) KJ_WARN_UNUSED_RESULT; |
| 2765 | JsValue typeError(kj::StringPtr message) KJ_WARN_UNUSED_RESULT; |
| 2766 | JsValue rangeError(kj::StringPtr message) KJ_WARN_UNUSED_RESULT; |
| 2767 | JsDate date(double timestamp) KJ_WARN_UNUSED_RESULT; |
| 2768 | JsDate date(kj::Date date) KJ_WARN_UNUSED_RESULT; |
| 2769 | JsDate date(kj::StringPtr date) KJ_WARN_UNUSED_RESULT; |
| 2770 | |
| 2771 | // Returns a JsObject that is backed internally by a v8::External object that |
| 2772 | // takes ownership over the inner. |
| 2773 | template <typename T> |
| 2774 | JsObject opaque(T&& inner) KJ_WARN_UNUSED_RESULT; |
| 2775 | |
| 2776 | // Returns a jsg::BufferSource whose underlying JavaScript handle is a Uint8Array. |
| 2777 | BufferSource bytes(kj::Array<kj::byte> data) KJ_WARN_UNUSED_RESULT; |
| 2778 | |
| 2779 | // Returns a jsg::BufferSource whose underlying JavaScript handle is an ArrayBuffer |
| 2780 | // as opposed to the default Uint8Array. May copy and move the bytes if they are |
| 2781 | // not in the right sandbox. |
| 2782 | BufferSource arrayBuffer(kj::Array<kj::byte> data) KJ_WARN_UNUSED_RESULT; |
| 2783 | |
| 2784 | enum class AllocOption { ZERO_INITIALIZED, UNINITIALIZED }; |
| 2785 | |
| 2786 | // Utility method to safely allocate a v8::BackingStore with allocation failure handling. |
| 2787 | // Throws a javascript error if allocation fails. |
| 2788 | // |
| 2789 | // IMPORTANT: This method can trigger garbage collection, which may move or invalidate V8 |
| 2790 | // objects. Do NOT call this method while: |
| 2791 | // - A v8::String::ValueView is alive (it holds internal V8 heap locks) |
| 2792 | // - You have raw pointers to V8 heap data (e.g., from view.data8(), view.data16()) |
| 2793 | // |
| 2794 | // Safe pattern: Copy V8 string data to off-heap memory FIRST (e.g., via JsString::writeInto() |
| 2795 | // into kj::SmallArray), THEN call allocBackingStore(). See TextEncoder::encode() for example. |
| 2796 | std::unique_ptr<v8::BackingStore> allocBackingStore( |
| 2797 | size_t size, AllocOption init_mode = AllocOption::ZERO_INITIALIZED) KJ_WARN_UNUSED_RESULT; |
| 2798 | |
| 2799 | enum RegExpFlags { |
| 2800 | kNONE = v8::RegExp::Flags::kNone, |
| 2801 | kGLOBAL = v8::RegExp::Flags::kGlobal, |
| 2802 | kIGNORE_CASE = v8::RegExp::Flags::kIgnoreCase, |
| 2803 | kMULTILINE = v8::RegExp::Flags::kMultiline, |
| 2804 | kSTICKY = v8::RegExp::Flags::kSticky, |
| 2805 | kUNICODE = v8::RegExp::Flags::kUnicode, |
| 2806 | kDOTALL = v8::RegExp::Flags::kDotAll, |
| 2807 | kLINEAR = v8::RegExp::Flags::kLinear, |
| 2808 | kHAS_INDICES = v8::RegExp::Flags::kHasIndices, |
| 2809 | kUNICODE_SETS = v8::RegExp::Flags::kUnicodeSets, |
| 2810 | }; |
| 2811 | |
| 2812 | JsRegExp regexp(kj::StringPtr pattern, |
| 2813 | RegExpFlags flags = RegExpFlags::kNONE, |
| 2814 | kj::Maybe<uint32_t> backtrackLimit = kj::none) KJ_WARN_UNUSED_RESULT; |
| 2815 | |
| 2816 | template <typename... Args> |
| 2817 | requires(std::assignable_from<JsValue&, Args> && ...) |
| 2818 | JsArray arr(const Args&... args) KJ_WARN_UNUSED_RESULT; |
| 2819 | |
| 2820 | JsArray arr(kj::ArrayPtr<JsValue> values) KJ_WARN_UNUSED_RESULT; |
| 2821 | |
| 2822 | // Create a JavaScript array from the given kj::ArrayPtr, passing each |
| 2823 | // item through the given transformation function to create the appropriate |
| 2824 | // JsValue. |
| 2825 | template <typename T, typename Func> |
| 2826 | JsArray arr(kj::ArrayPtr<T> values, Func fn) KJ_WARN_UNUSED_RESULT; |
| 2827 | |
| 2828 | template <typename... Args> |
| 2829 | requires(std::assignable_from<JsValue&, Args> && ...) |
| 2830 | JsSet set(const Args&... args) KJ_WARN_UNUSED_RESULT; |
| 2831 | |
| 2832 | #define V(Name) JsSymbol symbol##Name() KJ_WARN_UNUSED_RESULT; |
| 2833 | JS_V8_SYMBOLS(V) |
| 2834 | #undef V |
| 2835 | |
| 2836 | void runMicrotasks(); |
| 2837 | |
| 2838 | // Request an extra microtask checkpoint after the current one completes. |
| 2839 | void requestExtraMicrotaskCheckpoint(); |
| 2840 | |
| 2841 | // Sets the terminate-execution flag on the isolate so that the next time code tries to run, it |
| 2842 | // will be terminated. (But note that V8 only checks the flag at certain times, so it's possible |
| 2843 | // some code will actually execute before termination kicks in.) |
| 2844 | void terminateNextExecution(); |
| 2845 | |
| 2846 | // Terminates exution immediately, forcing V8 to see the flag and react to it before returning. |
| 2847 | // Always throws JsExceptionThrown. |
| 2848 | [[noreturn]] void terminateExecutionNow(); |
| 2849 | |
| 2850 | bool pumpMsgLoop(); |
| 2851 | |
| 2852 | // Logs and reports the error to tail workers (if called within an request), |
| 2853 | // the inspector (if attached), or to KJ_LOG(Info). |
| 2854 | virtual void reportError(const JsValue& value) = 0; |
| 2855 | |
| 2856 | // Store the worker environment. |
| 2857 | virtual void setWorkerEnv(V8Ref<v8::Object> value) = 0; |
| 2858 | |
| 2859 | // Retrieve the worker environment. |
| 2860 | virtual kj::Maybe<V8Ref<v8::Object>> getWorkerEnv() = 0; |
| 2861 | |
| 2862 | // Store the worker exports. |
| 2863 | virtual void setWorkerExports(V8Ref<v8::Object> value) = 0; |
| 2864 | |
| 2865 | // Retrieve the worker exports. |
| 2866 | virtual kj::Maybe<V8Ref<v8::Object>> getWorkerExports() = 0; |
| 2867 | |
| 2868 | // Resolve an internal module namespace from the given specifier. |
| 2869 | // This variation can be used only for internal built-ins. |
| 2870 | kj::Maybe<JsObject> resolveInternalModule(kj::StringPtr specifier); |
| 2871 | |
| 2872 | // Resolve a user-importable built-in module namespace from the given specifier. |
| 2873 | // Unlike resolveInternalModule, this only searches user-importable built-ins |
| 2874 | // (PUBLIC_BUILTIN context), excluding internal-only modules and worker bundle |
| 2875 | // modules. Use this for user-facing APIs like process.getBuiltinModule() that |
| 2876 | // must not expose internal modules or return user bundle overrides. |
| 2877 | // Only valid when the new module registry is in use. |
| 2878 | kj::Maybe<JsObject> resolvePublicBuiltinModule(kj::StringPtr specifier); |
| 2879 | |
| 2880 | // Resolve a module namespace from the given specifier. |
| 2881 | // This variation includes modules from the worker bundle. |
| 2882 | kj::Maybe<JsObject> resolveModule( |
| 2883 | kj::StringPtr specifier, RequireEsm requireEsm = RequireEsm::NO); |
| 2884 | |
| 2885 | // Returns the capnp::SchemaLoader for this isolate/context |
| 2886 | template <typename T> |
| 2887 | const capnp::SchemaLoader& getCapnpSchemaLoader() const { |
| 2888 | return KJ_ASSERT_NONNULL( |
| 2889 | jsg::getAlignedPointerFromEmbedderData<T>( |
| 2890 | v8Isolate->GetCurrentContext(), ContextPointerSlot::GLOBAL_WRAPPER)) |
| 2891 | .getSchemaLoader(); |
| 2892 | } |
| 2893 | |
| 2894 | private: |
| 2895 | // Mark the jsg::Lock as being disallowed from being passed as a parameter into |
| 2896 | // a kj promise coroutine. Note that this only blocks directly passing the Lock |
| 2897 | // in. Types that have the Lock included as a member field won't be caught and |
| 2898 | // should themselves be marked with KJ_DISALLOW_AS_COROUTINE_PARAM. Note also |
| 2899 | // that this would not stop someone from passing the v8::Isolate reference into |
| 2900 | // the coroutine and using `Lock::from(...)` to get the Lock. Don't do that. |
| 2901 | // jsg::Lock should NOT be used within a kj promise coroutine. |
| 2902 | KJ_DISALLOW_AS_COROUTINE_PARAM; |
| 2903 | friend class IsolateBase; |
| 2904 | template <typename TypeWrapper> |
| 2905 | friend class Isolate; |
| 2906 | |
| 2907 | Lock(v8::Isolate* v8Isolate); |
| 2908 | ~Lock() noexcept(false); |
| 2909 | |
| 2910 | v8::Locker locker; |
| 2911 | v8::Isolate::Scope isolateScope; |
| 2912 | |
| 2913 | void* previousData; |
| 2914 | |
| 2915 | bool warningsLogged; |
| 2916 | |
| 2917 | friend class JsObject; |
| 2918 | virtual kj::Maybe<Object&> getInstance(v8::Local<v8::Object> obj, const std::type_info& type) = 0; |
| 2919 | virtual v8::Local<v8::Object> getPrototypeFor(const std::type_info& type) = 0; |
| 2920 | }; |
| 2921 | |
| 2922 | // Ensures that the given fn is run within both a handlescope and the context scope. |
| 2923 | // The lock must be assignable to a jsg::Lock, and the context must be or be assignable |
| 2924 | // to a v8::Local<v8::Context>. The context will be evaluated within the handle scope. |
| 2925 | #define JSG_WITHIN_CONTEXT_SCOPE(lock, context, fn) \ |
| 2926 | (static_cast<jsg::Lock&>(lock)).withinHandleScope([&]() -> auto { \ |
| 2927 | v8::Local<v8::Context> ctx = context; \ |
| 2928 | KJ_ASSERT(!ctx.IsEmpty(), "unable to enter invalid v8::Context"); \ |
| 2929 | v8::Context::Scope scope(ctx); \ |
| 2930 | return fn(static_cast<jsg::Lock&>(lock)); \ |
| 2931 | }) |
| 2932 | |
| 2933 | // The V8StackScope is used only as a marker to prove that we are running in the V8 stack |
| 2934 | // established by calling runInV8Stack(...) |
| 2935 | class V8StackScope final { |
| 2936 | public: |
| 2937 | KJ_DISALLOW_COPY_AND_MOVE(V8StackScope); |
| 2938 | |
| 2939 | private: |
| 2940 | V8StackScope() = default; |
| 2941 | KJ_DISALLOW_AS_COROUTINE_PARAM; |
| 2942 | |
| 2943 | static auto runInV8StackImpl(void* pos, auto callback) __attribute__((noinline)) { |
| 2944 | #if V8_HAS_STACK_START_MARKER |
| 2945 | // This currently depends on a V8 patch which hasn't been upstreamed. Note that workerd does |
| 2946 | // not use this patch; it's only used internally. The patch is needed in order to work around |
| 2947 | // oddities of our internal environment which do not apply to workerd. For workerd, V8's default |
| 2948 | // behavior is just fine. |
| 2949 | v8::StackStartMarker marker(pos); |
| 2950 | #endif |
| 2951 | // We create a V8StackScope only as proof that we are running in the V8 stack. |
| 2952 | V8StackScope stackScope; |
| 2953 | return callback(stackScope); |
| 2954 | } |
| 2955 | |
| 2956 | friend auto runInV8Stack(auto callback); |
| 2957 | }; |
| 2958 | |
| 2959 | // Ensures that a v8::StackStartMarker is allocated on the stack before calling the callback. |
| 2960 | // This must be used, for instance, before taking an isolate lock. |
| 2961 | // The reason why Isolate::Lock doesn't take care of this automatically is because it is often |
| 2962 | // allocated on the heap. The purpose of using runInV8Stack is to capture the start of the stack |
| 2963 | // range that V8 must scan when performing conservative stack-scanning garbage collection. |
| 2964 | auto runInV8Stack(auto callback) { |
| 2965 | return V8StackScope::runInV8StackImpl(__builtin_frame_address(0), kj::mv(callback)); |
| 2966 | }; |
| 2967 | |
| 2968 | // Returns true if we are currently executing C++ destructors as a result of garbage collection |
| 2969 | // occurring. |
| 2970 | bool isInGcDestructor(); |
| 2971 | |
| 2972 | // ======================================================================================= |
| 2973 | // inline implementation details |
| 2974 | |
| 2975 | template <typename T> |
| 2976 | template <typename U> |
| 2977 | V8Ref<U> V8Ref<T>::cast(jsg::Lock& js) { |
| 2978 | return js.v8Ref(getHandle(js).template As<U>()); |
| 2979 | } |
| 2980 | |
| 2981 | template <typename T> |
| 2982 | inline kj::Maybe<T> PropertyReflection<T>::get(Lock& js, kj::StringPtr name) { |
| 2983 | return get(js.v8Isolate, name); |
| 2984 | } |
| 2985 | |
| 2986 | template <typename T> |
| 2987 | inline V8Ref<T> Lock::v8Ref(v8::Local<T> local) { |
| 2988 | return V8Ref(v8Isolate, local); |
| 2989 | } |
| 2990 | |
| 2991 | inline Data Lock::v8Data(v8::Local<v8::Data> local) { |
| 2992 | return Data(v8Isolate, local); |
| 2993 | } |
| 2994 | |
| 2995 | inline v8::Local<v8::Value> Lock::v8Undefined() { |
| 2996 | return v8::Undefined(v8Isolate); |
| 2997 | } |
| 2998 | |
| 2999 | inline v8::Local<v8::Value> Lock::v8Null() { |
| 3000 | return v8::Null(v8Isolate); |
| 3001 | } |
| 3002 | |
| 3003 | inline Data Data::addRef(jsg::Lock& js) { |
| 3004 | return Data(js.v8Isolate, getHandle(js)); |
| 3005 | } |
| 3006 | |
| 3007 | template <typename T> |
| 3008 | kj::Maybe<v8::Local<v8::Object>> Ref<T>::tryGetHandle(Lock& js) { |
| 3009 | return tryGetHandle(js.v8Isolate); |
| 3010 | } |
| 3011 | |
| 3012 | template <typename T> |
| 3013 | inline V8Ref<T> V8Ref<T>::addRef(jsg::Lock& js) { |
| 3014 | return js.v8Ref(getHandle(js)); |
| 3015 | } |
| 3016 | |
| 3017 | template <typename T> |
| 3018 | V8Ref<T> V8Ref<T>::deepClone(jsg::Lock& js) { |
| 3019 | return js.v8Ref(jsg::deepClone(js.v8Context(), getHandle(js)).template As<T>()); |
| 3020 | } |
| 3021 | |
| 3022 | template <typename T> |
| 3023 | inline HashableV8Ref<T> HashableV8Ref<T>::addRef(jsg::Lock& js) { |
| 3024 | return HashableV8Ref(js.v8Isolate, this->getHandle(js), identityHash); |
| 3025 | } |
| 3026 | |
| 3027 | template <typename T> |
| 3028 | inline v8::Local<T> V8Ref<T>::getHandle(jsg::Lock& js) const { |
| 3029 | return getHandle(js.v8Isolate); |
| 3030 | } |
| 3031 | |
| 3032 | inline v8::Local<v8::Data> Data::getHandle(jsg::Lock& js) const { |
| 3033 | return getHandle(js.v8Isolate); |
| 3034 | } |
| 3035 | |
| 3036 | template <typename T> |
| 3037 | inline v8::Local<v8::Context> JsContext<T>::getHandle(Lock& js) const { |
| 3038 | return handle.Get(js.v8Isolate); |
| 3039 | } |
| 3040 | |
| 3041 | inline Value SelfRef::asValue(Lock& js) const { |
| 3042 | return Value(js.v8Isolate, getHandle(js).As<v8::Value>()); |
| 3043 | } |
| 3044 | |
| 3045 | namespace _ { |
| 3046 | |
| 3047 | // Helper class for JSG_TRY / JSG_CATCH macros. |
| 3048 | // |
| 3049 | // Sets up a v8::TryCatch on construction and converts caught exceptions to jsg::Value. |
| 3050 | // Handles both JsExceptionThrown (returns V8 exception directly) and kj::Exception |
| 3051 | // (converts via Lock::exceptionToJs()). |
| 3052 | // |
| 3053 | // This class is an implementation detail of the JSG_TRY / JSG_CATCH macros and should |
| 3054 | // not be used directly. |
| 3055 | class JsgCatchScope { |
| 3056 | public: |
| 3057 | explicit JsgCatchScope(Lock& js); |
| 3058 | |
| 3059 | // Converts the in-flight exception to a jsg::Value and stores it. |
| 3060 | // Called by JSG_CATCH macro. |
| 3061 | void catchException(ExceptionToJsOptions options = {}); |
| 3062 | |
| 3063 | // Returns the caught exception. Must be called after catchException(). |
| 3064 | Value& getCaughtException() { |
| 3065 | return KJ_ASSERT_NONNULL(caughtException); |
| 3066 | } |
| 3067 | |
| 3068 | private: |
| 3069 | Lock& js; |
| 3070 | |
| 3071 | // Simple wrapper to work around v8::TryCatch's deleted operator new. |
| 3072 | struct Holder { |
| 3073 | v8::TryCatch tryCatch; |
| 3074 | explicit Holder(v8::Isolate* isolate): tryCatch(isolate) {} |
| 3075 | }; |
| 3076 | |
| 3077 | // We use two separate Maybe members rather than kj::OneOf<Holder, Value> because v8::TryCatch |
| 3078 | // has deleted copy/move constructors, making it incompatible with OneOf's internal storage. |
| 3079 | // The tryCatchHolder is active during the try block and released by catchException(), which |
| 3080 | // then populates caughtException. |
| 3081 | |
| 3082 | // Active during the try block, consumed by catchException(). |
| 3083 | kj::Maybe<Holder> tryCatchHolder; |
| 3084 | |
| 3085 | // Populated by catchException(), returned by getCaughtException(). |
| 3086 | kj::Maybe<Value> caughtException; |
| 3087 | }; |
| 3088 | |
| 3089 | } // namespace _ |
| 3090 | |
| 3091 | // JSG_TRY / JSG_CATCH macros for exception handling in JSG code. |
| 3092 | // |
| 3093 | // These macros provide clean exception handling that automatically converts both JavaScript |
| 3094 | // exceptions (JsExceptionThrown) and KJ exceptions (kj::Exception) to jsg::Value. This is |
| 3095 | // the recommended way to handle exceptions in JSG code. |
| 3096 | // |
| 3097 | // Usage: |
| 3098 | // JSG_TRY(js) { |
| 3099 | // someCodeThatMightThrow(); |
| 3100 | // } JSG_CATCH(exception) { |
| 3101 | // // `exception` is a jsg::Value& containing the caught exception |
| 3102 | // return js.rejectedPromise<void>(kj::mv(exception)); |
| 3103 | // } |
| 3104 | // |
| 3105 | // With ExceptionToJsOptions: |
| 3106 | // JSG_TRY(js) { |
| 3107 | // someCodeThatMightThrow(); |
| 3108 | // } JSG_CATCH(exception, {.ignoreDetail = true}) { |
| 3109 | // // Handle exception with custom conversion options |
| 3110 | // } |
| 3111 | // |
| 3112 | // JSG_TRY(js): Sets up exception handling with the given jsg::Lock. The `js` parameter makes |
| 3113 | // the isolate explicit and enables future coroutine support. |
| 3114 | // |
| 3115 | // JSG_CATCH(name, ...): Catches any exception and converts it to a jsg::Value. The `name` |
| 3116 | // parameter is a user-chosen identifier that will be a `jsg::Value&` in the handler block. |
| 3117 | // Optional ExceptionToJsOptions can be passed as a second argument. |
| 3118 | // |
| 3119 | // IMPORTANT: The code block following JSG_CATCH is NOT a true catch handler: |
| 3120 | // - You CANNOT rethrow with `throw` (there is no current exception) |
| 3121 | // |
| 3122 | // To rethrow the exception, use: js.throwException(kj::mv(exception)); |
| 3123 | |
| 3124 | // Since we have two macros -- JSG_TRY and JSG_CATCH -- which must both access the same state, |
| 3125 | // we use a hard-coded variable name. This causes benign shadowing in nested JSG_TRY/JSG_CATCHes, |
| 3126 | // so we disable shadowing warnings. The `_jsg` prefix makes name collision unlikely. |
| 3127 | #define JSG_TRY(js) \ |
| 3128 | KJ_SILENCE_SHADOWING_BEGIN \ |
| 3129 | if (::workerd::jsg::_::JsgCatchScope _jsgTryCatch(js); true) try KJ_SILENCE_SHADOWING_END |
| 3130 | |
| 3131 | #define JSG_CATCH(exception, ...) \ |
| 3132 | catch (...) { \ |
| 3133 | _jsgTryCatch.catchException(__VA_ARGS__); \ |
| 3134 | goto KJ_UNIQUE_NAME(_jsgTryCatchHandler); \ |
| 3135 | } \ |
| 3136 | else KJ_UNIQUE_NAME(_jsgTryCatchHandler) \ |
| 3137 | : if (auto& exception = _jsgTryCatch.getCaughtException(); false) {} \ |
| 3138 | else |
| 3139 | |
| 3140 | } // namespace workerd::jsg |
| 3141 | |
| 3142 | // clang-format off |
| 3143 | // These includes are needed for the JSG type glue macros to work. |
| 3144 | #include "promise.h" |
| 3145 | #include "modules.h" |
| 3146 | #include "resource.h" |
| 3147 | // JSG has very entrenched include cycles |
| 3148 | // NOLINTNEXTLINE(misc-header-include-cycle) |
| 3149 | #include "jsvalue.h" |
| 3150 | // clang-format on |
| 3151 | |
| 3152 | // The main JSG API no longer depends on the Type Wrapper, but to avoid extensive changes in |
| 3153 | // external code using JSG we still want it to be available when including jsg.h. This technically |
| 3154 | // violates Bazel's encapsulation philosophy (type-wrapper.h should not be visible from jsg.h), so |
| 3155 | // we only make jsg.h available for external code as part of the main jsg target including type-wrapper.h. |
| 3156 | #ifndef JSG_IMPLEMENTATION |
| 3157 | #include <workerd/jsg/type-wrapper.h> |
| 3158 | #endif // JSG_IMPLEMENTATION |