File
Blob: src/rust/jsg/ffi.h
| 1 | // Copyright (c) 2026 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 | |
| 7 | #include <workerd/jsg/modules.capnp.h> |
| 8 | #include <workerd/jsg/wrappable.h> |
| 9 | |
| 10 | #include <kj-rs/kj-rs.h> |
| 11 | #include <rust/cxx.h> |
| 12 | #include <v8.h> |
| 13 | |
| 14 | #include <kj/function.h> |
| 15 | #include <kj/memory.h> |
| 16 | #include <kj/refcount.h> |
| 17 | |
| 18 | // Forward declarations needed by v8.rs.h |
| 19 | namespace workerd::rust::jsg { |
| 20 | using Isolate = v8::Isolate; |
| 21 | using FunctionCallbackInfo = v8::FunctionCallbackInfo<v8::Value>; |
| 22 | struct ModuleRegistry; |
| 23 | struct Local; |
| 24 | struct MaybeLocal; |
| 25 | struct Global; |
| 26 | struct TracedReference; |
| 27 | struct Realm; |
| 28 | struct GcVisitor; |
| 29 | enum class BackingStoreInitializationMode : ::std::uint8_t; |
| 30 | enum class ExceptionType : ::std::uint8_t; |
| 31 | // ModuleType enum is generated by CXX in v8.rs.h - forward declare it here |
| 32 | enum class ModuleType : ::std::uint8_t; |
| 33 | using ModuleCallback = ::rust::Fn<Local(Isolate*)>; |
| 34 | |
| 35 | // Mirrors the CXX shared struct defined in v8.rs. Must be kept in sync. |
| 36 | // The CXX-generated v8.rs.h provides the canonical definition; this is |
| 37 | // only used when ffi.h is included before v8.rs.h (e.g. from ffi.c++ |
| 38 | // before the CXX bridge is compiled). |
| 39 | #ifndef CXXBRIDGE1_STRUCT_workerd$rust$jsg$TraitObjectPtr |
| 40 | #define CXXBRIDGE1_STRUCT_workerd$rust$jsg$TraitObjectPtr |
| 41 | struct TraitObjectPtr { |
| 42 | uintptr_t data_ptr = 0; |
| 43 | uintptr_t vtable_ptr = 0; |
| 44 | uintptr_t type_id_lo = 0; |
| 45 | uintptr_t type_id_hi = 0; |
| 46 | }; |
| 47 | #endif |
| 48 | |
| 49 | // A Wrappable subclass for Rust objects. |
| 50 | // |
| 51 | // Lives on the KJ heap (via kj::Refcounted through Wrappable) and integrates |
| 52 | // with cppgc via the CppgcShim bridge pattern that Wrappable provides. |
| 53 | // |
| 54 | // The data[] field stores a Rust fat pointer (data + vtable) to `dyn GarbageCollected` |
| 55 | // from a leaked Box<dyn GarbageCollected>. This lets us call back into Rust for tracing |
| 56 | // and destruction without knowing the concrete type at compile time. |
| 57 | class Wrappable: public ::workerd::jsg::Wrappable { |
| 58 | public: |
| 59 | ~Wrappable(); |
| 60 | void jsgVisitForGc(::workerd::jsg::GcVisitor& visitor) override; |
| 61 | kj::StringPtr jsgGetMemoryName() const override; |
| 62 | size_t jsgGetMemorySelfSize() const override; |
| 63 | |
| 64 | // Returns a new kj::Rc reference to this Wrappable. |
| 65 | kj::Rc<Wrappable> toRc() { |
| 66 | return addRefToThis(); |
| 67 | } |
| 68 | |
| 69 | // Rust trait object pointer + TypeId, stored by Ref::new(). |
| 70 | // Defined as a CXX shared struct in v8.rs; contains data_ptr, vtable_ptr, |
| 71 | // type_id_lo, type_id_hi. |
| 72 | TraitObjectPtr trait_object = {0, 0, 0, 0}; |
| 73 | }; |
| 74 | |
| 75 | struct ResourceDescriptor; |
| 76 | |
| 77 | // Wrappable lifecycle — returns KjRc<Wrappable> to Rust for reference-counted ownership. |
| 78 | // Ref::clone calls wrappable_add_strong_ref; Ref::drop calls wrappable_remove_strong_ref. |
| 79 | // wrappable_remove_strong_ref does addRef(+1) + maybeDeferDestruction(-1) = net 0 on kj::Rc; |
| 80 | // the actual kj::Rc decrement happens when WrappableRc (KjRc<Wrappable>) is dropped. |
| 81 | kj::Rc<Wrappable> wrappable_new(TraitObjectPtr ptr); |
| 82 | kj::Rc<Wrappable> wrappable_to_rc(Wrappable& wrappable); |
| 83 | void wrappable_add_strong_ref(Wrappable& wrappable); |
| 84 | void wrappable_remove_strong_ref(Wrappable& wrappable, bool is_strong); |
| 85 | void wrappable_visit_ref( |
| 86 | Wrappable& wrappable, uintptr_t* ref_parent, bool* ref_strong, GcVisitor* visitor); |
| 87 | |
| 88 | /// Visit a `v8::Global` field during GC tracing, implementing the same |
| 89 | /// strong↔traced dual-mode switching as `jsg::Data` / `jsg::V8Ref<T>`. |
| 90 | /// |
| 91 | /// `global` points to the `ptr` field of `ffi::Global` (the strong handle). |
| 92 | /// `traced` is the `ffi::TracedReference` slot from `Global<T>` on the Rust side. |
| 93 | /// Both are mutated in-place by this function. |
| 94 | void wrappable_visit_global(GcVisitor* visitor, uintptr_t* global, TracedReference& traced); |
| 95 | |
| 96 | /// Resets a `v8::TracedReference`, releasing the weak GC handle. |
| 97 | /// Must be called when a `Global<T>` is dropped in traced mode to avoid |
| 98 | /// leaking a live `v8::TracedReference`. |
| 99 | void traced_reference_reset(TracedReference& traced); |
| 100 | |
| 101 | // Local<T> |
| 102 | void local_drop(Local value); |
| 103 | Local local_clone(const Local& value); |
| 104 | Global local_to_global(Isolate* isolate, Local value); |
| 105 | Local local_new_number(Isolate* isolate, double value); |
| 106 | Local local_new_string(Isolate* isolate, ::rust::Str value); |
| 107 | Local local_new_boolean(Isolate* isolate, bool value); |
| 108 | Local local_new_object(Isolate* isolate); |
| 109 | Local local_new_null(Isolate* isolate); |
| 110 | Local local_new_undefined(Isolate* isolate); |
| 111 | bool local_eq(const Local& lhs, const Local& rhs); |
| 112 | bool local_has_value(const Local& val); |
| 113 | bool local_is_string(const Local& val); |
| 114 | bool local_is_boolean(const Local& val); |
| 115 | bool local_is_number(const Local& val); |
| 116 | bool local_is_null(const Local& val); |
| 117 | bool local_is_undefined(const Local& val); |
| 118 | bool local_is_null_or_undefined(const Local& val); |
| 119 | bool local_is_object(const Local& val); |
| 120 | bool local_is_native_error(const Local& val); |
| 121 | bool local_is_array(const Local& val); |
| 122 | bool local_is_uint8_array(const Local& val); |
| 123 | bool local_is_uint16_array(const Local& val); |
| 124 | bool local_is_uint32_array(const Local& val); |
| 125 | bool local_is_int8_array(const Local& val); |
| 126 | bool local_is_int16_array(const Local& val); |
| 127 | bool local_is_int32_array(const Local& val); |
| 128 | bool local_is_float32_array(const Local& val); |
| 129 | bool local_is_float64_array(const Local& val); |
| 130 | bool local_is_bigint64_array(const Local& val); |
| 131 | bool local_is_biguint64_array(const Local& val); |
| 132 | bool local_is_float16_array(const Local& val); |
| 133 | bool local_is_uint8clamped_array(const Local& val); |
| 134 | bool local_is_array_buffer(const Local& val); |
| 135 | bool local_is_array_buffer_view(const Local& val); |
| 136 | bool local_is_shared_array_buffer(const Local& val); |
| 137 | bool local_is_function(const Local& val); |
| 138 | bool local_is_symbol(const Local& val); |
| 139 | bool local_is_name(const Local& val); |
| 140 | ::rust::String local_type_of(Isolate* isolate, const Local& val); |
| 141 | |
| 142 | // Local<String> |
| 143 | Local local_string_empty(Isolate* isolate); |
| 144 | int32_t local_string_length(const Local& value); |
| 145 | bool local_string_is_one_byte(const Local& value); |
| 146 | bool local_string_contains_only_one_byte(const Local& value); |
| 147 | size_t local_string_utf8_length(Isolate* isolate, const Local& value); |
| 148 | void local_string_write_v2(Isolate* isolate, |
| 149 | const Local& value, |
| 150 | uint32_t offset, |
| 151 | uint32_t length, |
| 152 | uint16_t* buffer, |
| 153 | int32_t flags); |
| 154 | void local_string_write_one_byte_v2(Isolate* isolate, |
| 155 | const Local& value, |
| 156 | uint32_t offset, |
| 157 | uint32_t length, |
| 158 | uint8_t* buffer, |
| 159 | int32_t flags); |
| 160 | size_t local_string_write_utf8_v2( |
| 161 | Isolate* isolate, const Local& value, uint8_t* buffer, size_t capacity, int32_t flags); |
| 162 | bool local_string_equals(const Local& value, const Local& other); |
| 163 | bool local_string_is_flat(const Local& value); |
| 164 | Local local_string_concat(Isolate* isolate, Local left, Local right); |
| 165 | Local local_string_internalize(Isolate* isolate, const Local& value); |
| 166 | MaybeLocal local_string_new_from_utf8( |
| 167 | Isolate* isolate, const uint8_t* data, int32_t length, bool internalized); |
| 168 | MaybeLocal local_string_new_from_one_byte( |
| 169 | Isolate* isolate, const uint8_t* data, int32_t length, bool internalized); |
| 170 | MaybeLocal local_string_new_from_two_byte( |
| 171 | Isolate* isolate, const uint16_t* data, int32_t length, bool internalized); |
| 172 | bool maybe_local_is_empty(const MaybeLocal& value); |
| 173 | |
| 174 | // Local<Name> |
| 175 | int32_t local_name_get_identity_hash(const Local& value); |
| 176 | |
| 177 | // Local<Symbol> |
| 178 | Local local_symbol_new(Isolate* isolate); |
| 179 | Local local_symbol_new_with_description(Isolate* isolate, Local description); |
| 180 | MaybeLocal local_symbol_description(Isolate* isolate, const Local& value); |
| 181 | |
| 182 | // Local<Function> |
| 183 | Local local_function_call( |
| 184 | Isolate* isolate, const Local& function, const Local& recv, ::rust::Slice<const Local> args); |
| 185 | |
| 186 | // Local<Object> |
| 187 | void local_object_set_property(Isolate* isolate, Local& object, ::rust::Str key, Local value); |
| 188 | bool local_object_has_property(Isolate* isolate, const Local& object, ::rust::Str key); |
| 189 | kj::Maybe<Local> local_object_get_property(Isolate* isolate, const Local& object, ::rust::Str key); |
| 190 | |
| 191 | // Local<Array> |
| 192 | Local local_new_array(Isolate* isolate, size_t length); |
| 193 | uint32_t local_array_length(Isolate* isolate, const Local& array); |
| 194 | Local local_array_get(Isolate* isolate, const Local& array, uint32_t index); |
| 195 | void local_array_set(Isolate* isolate, Local& array, uint32_t index, Local value); |
| 196 | ::rust::Vec<Global> local_array_iterate(Isolate* isolate, Local value); |
| 197 | |
| 198 | // Local<TypedArray> |
| 199 | Local local_new_uint8_array(Isolate* isolate, const uint8_t* data, size_t length); |
| 200 | Local local_new_uint16_array(Isolate* isolate, const uint16_t* data, size_t length); |
| 201 | Local local_new_uint32_array(Isolate* isolate, const uint32_t* data, size_t length); |
| 202 | Local local_new_int8_array(Isolate* isolate, const int8_t* data, size_t length); |
| 203 | Local local_new_int16_array(Isolate* isolate, const int16_t* data, size_t length); |
| 204 | Local local_new_int32_array(Isolate* isolate, const int32_t* data, size_t length); |
| 205 | Local local_new_float32_array(Isolate* isolate, const float* data, size_t length); |
| 206 | Local local_new_float64_array(Isolate* isolate, const double* data, size_t length); |
| 207 | Local local_new_bigint64_array(Isolate* isolate, const int64_t* data, size_t length); |
| 208 | Local local_new_biguint64_array(Isolate* isolate, const uint64_t* data, size_t length); |
| 209 | size_t local_typed_array_length(Isolate* isolate, const Local& array); |
| 210 | // Returns a raw pointer to the underlying ArrayBuffer's data (without byte offset). |
| 211 | // Use local_typed_array_byte_offset to compute the start of this view's data. |
| 212 | uintptr_t local_typed_array_buffer_data(Isolate* isolate, const Local& array); |
| 213 | // Returns the byte offset of this TypedArray view within its backing ArrayBuffer. |
| 214 | size_t local_typed_array_byte_offset(Isolate* isolate, const Local& array); |
| 215 | // Returns the byte length of the TypedArray. |
| 216 | size_t local_typed_array_byte_length(Isolate* isolate, const Local& array); |
| 217 | uint8_t local_uint8_array_get(Isolate* isolate, const Local& array, size_t index); |
| 218 | uint16_t local_uint16_array_get(Isolate* isolate, const Local& array, size_t index); |
| 219 | uint32_t local_uint32_array_get(Isolate* isolate, const Local& array, size_t index); |
| 220 | int8_t local_int8_array_get(Isolate* isolate, const Local& array, size_t index); |
| 221 | int16_t local_int16_array_get(Isolate* isolate, const Local& array, size_t index); |
| 222 | int32_t local_int32_array_get(Isolate* isolate, const Local& array, size_t index); |
| 223 | float local_float32_array_get(Isolate* isolate, const Local& array, size_t index); |
| 224 | double local_float64_array_get(Isolate* isolate, const Local& array, size_t index); |
| 225 | int64_t local_bigint64_array_get(Isolate* isolate, const Local& array, size_t index); |
| 226 | uint64_t local_biguint64_array_get(Isolate* isolate, const Local& array, size_t index); |
| 227 | uint8_t local_uint8clamped_array_get(Isolate* isolate, const Local& array, size_t index); |
| 228 | |
| 229 | // Local<ArrayBuffer> |
| 230 | Local local_new_array_buffer(Isolate* isolate, const uint8_t* data, size_t length); |
| 231 | Local local_new_array_buffer_empty(Isolate* isolate, size_t byte_length); |
| 232 | kj::Maybe<Local> array_buffer_new_with_mode( |
| 233 | Isolate* isolate, size_t byte_length, BackingStoreInitializationMode mode); |
| 234 | Local array_buffer_from_backing_store(Isolate* isolate, size_t store); |
| 235 | size_t local_array_buffer_byte_length(Isolate* isolate, const Local& buffer); |
| 236 | uint8_t* local_array_buffer_data(Isolate* isolate, const Local& buffer); |
| 237 | size_t local_array_buffer_get_backing_store(Isolate* isolate, const Local& buffer); |
| 238 | |
| 239 | // Local<ArrayBufferView> |
| 240 | size_t local_array_buffer_view_byte_offset(Isolate* isolate, const Local& view); |
| 241 | size_t local_array_buffer_view_byte_length(Isolate* isolate, const Local& view); |
| 242 | uint8_t* local_array_buffer_view_buffer_data(Isolate* isolate, const Local& view); |
| 243 | Local local_array_buffer_view_get_buffer(Isolate* isolate, const Local& view); |
| 244 | size_t local_array_buffer_view_element_size(Isolate* isolate, const Local& view); |
| 245 | bool local_array_buffer_view_is_integer_type(Isolate* isolate, const Local& view); |
| 246 | |
| 247 | // BackingStore |
| 248 | size_t backing_store_new_resizable(size_t byte_length, size_t max_byte_length); |
| 249 | void backing_store_drop(size_t ptr); |
| 250 | uint8_t* backing_store_data(size_t ptr); |
| 251 | size_t backing_store_byte_length(size_t ptr); |
| 252 | size_t backing_store_max_byte_length(size_t ptr); |
| 253 | bool backing_store_is_shared(size_t ptr); |
| 254 | bool backing_store_is_resizable_by_user_javascript(size_t ptr); |
| 255 | |
| 256 | // Global<T> |
| 257 | void global_reset(Global& value); |
| 258 | Global global_clone(Isolate* isolate, const Global& value); |
| 259 | Local global_to_local(Isolate* isolate, const Global& value); |
| 260 | |
| 261 | // Wrappable - data access |
| 262 | const TraitObjectPtr& wrappable_get_trait_object(const Wrappable& wrappable); |
| 263 | void wrappable_clear_trait_object(Wrappable& wrappable); |
| 264 | kj::uint wrappable_strong_refcount(const Wrappable& wrappable); |
| 265 | |
| 266 | // Wrappers |
| 267 | Local wrap_resource(Isolate* isolate, kj::Rc<Wrappable> wrappable, const Global& tmpl); |
| 268 | void wrappable_attach_wrapper(kj::Rc<Wrappable> wrappable, FunctionCallbackInfo& args); |
| 269 | |
| 270 | // Unwrappers |
| 271 | ::rust::String unwrap_string(Isolate* isolate, Local value); |
| 272 | bool unwrap_boolean(Isolate* isolate, Local value); |
| 273 | double unwrap_number(Isolate* isolate, Local value); |
| 274 | kj::Rc<Wrappable> unwrap_resource(Isolate* isolate, Local value); |
| 275 | ::rust::Vec<uint8_t> unwrap_uint8_array(Isolate* isolate, Local value); |
| 276 | ::rust::Vec<uint16_t> unwrap_uint16_array(Isolate* isolate, Local value); |
| 277 | ::rust::Vec<uint32_t> unwrap_uint32_array(Isolate* isolate, Local value); |
| 278 | ::rust::Vec<int8_t> unwrap_int8_array(Isolate* isolate, Local value); |
| 279 | ::rust::Vec<int16_t> unwrap_int16_array(Isolate* isolate, Local value); |
| 280 | ::rust::Vec<int32_t> unwrap_int32_array(Isolate* isolate, Local value); |
| 281 | ::rust::Vec<float> unwrap_float32_array(Isolate* isolate, Local value); |
| 282 | ::rust::Vec<double> unwrap_float64_array(Isolate* isolate, Local value); |
| 283 | ::rust::Vec<int64_t> unwrap_bigint64_array(Isolate* isolate, Local value); |
| 284 | ::rust::Vec<uint64_t> unwrap_biguint64_array(Isolate* isolate, Local value); |
| 285 | // ArrayBuffer detach/detachable/was-detached |
| 286 | void local_array_buffer_detach(Isolate* isolate, Local& buffer); |
| 287 | bool local_array_buffer_was_detached(Isolate* isolate, const Local& buffer); |
| 288 | bool local_array_buffer_is_detachable(Isolate* isolate, const Local& buffer); |
| 289 | |
| 290 | // Value-level shared check |
| 291 | bool local_array_buffer_is_shared(const Local& value); |
| 292 | |
| 293 | // FunctionCallbackInfo |
| 294 | Isolate* fci_get_isolate(FunctionCallbackInfo* args); |
| 295 | Local fci_get_this(FunctionCallbackInfo* args); |
| 296 | size_t fci_get_length(FunctionCallbackInfo* args); |
| 297 | Local fci_get_arg(FunctionCallbackInfo* args, size_t index); |
| 298 | void fci_set_return_value(FunctionCallbackInfo* args, Local value); |
| 299 | |
| 300 | // Utf8Value — ptr holds a heap-allocated v8::String::Utf8Value*. |
| 301 | // The complete struct definition is generated by CXX from the shared struct in v8.rs. |
| 302 | struct Utf8Value; |
| 303 | |
| 304 | // utf8_value_new converts `value` to a UTF-8 string, producing a heap-allocated copy |
| 305 | // of the UTF-8 bytes that is independent of the V8 heap. The caller must call |
| 306 | // utf8_value_drop to free it. |
| 307 | // utf8_value_data returns a pointer into this copy (not into V8 memory); it is valid |
| 308 | // for the lifetime of the Utf8Value and must not be used after utf8_value_drop. |
| 309 | // utf8_value_length returns the number of bytes (excluding the null terminator). |
| 310 | Utf8Value utf8_value_new(Isolate* isolate, Local value); |
| 311 | void utf8_value_drop(Utf8Value value); |
| 312 | size_t utf8_value_length(const Utf8Value& value); |
| 313 | const uint8_t* utf8_value_data(const Utf8Value& value); |
| 314 | |
| 315 | struct ModuleRegistry { |
| 316 | virtual ~ModuleRegistry() = default; |
| 317 | virtual void addBuiltinModule( |
| 318 | ::rust::Str specifier, ModuleCallback moduleCallback, ModuleType moduleType) = 0; |
| 319 | }; |
| 320 | |
| 321 | inline void register_add_builtin_module(ModuleRegistry& registry, |
| 322 | ::rust::Str specifier, |
| 323 | ModuleCallback callback, |
| 324 | ModuleType moduleType) { |
| 325 | registry.addBuiltinModule(specifier, kj::mv(callback), moduleType); |
| 326 | } |
| 327 | |
| 328 | Global create_resource_template(Isolate* isolate, const ResourceDescriptor& descriptor); |
| 329 | |
| 330 | // FunctionTemplate |
| 331 | Local function_template_get_function(Isolate* isolate, const Global& tmpl); |
| 332 | |
| 333 | // Realm |
| 334 | Realm* realm_from_isolate(Isolate* isolate); |
| 335 | |
| 336 | // Errors |
| 337 | Local exception_create(Isolate* isolate, ExceptionType exception_type, ::rust::Str message); |
| 338 | |
| 339 | // Isolate |
| 340 | void isolate_throw_exception(Isolate* isolate, Local exception); |
| 341 | void isolate_throw_error(Isolate* isolate, ::rust::Str message); |
| 342 | void isolate_throw_internal_error(Isolate* isolate, ::rust::Str internalMessage); |
| 343 | void isolate_terminate_execution(Isolate* isolate); |
| 344 | bool isolate_is_locked(Isolate* isolate); |
| 345 | |
| 346 | } // namespace workerd::rust::jsg |