summaryrefslogtreecommitdiff
path: root/ext/node/ops/v8.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node/ops/v8.rs')
-rw-r--r--ext/node/ops/v8.rs361
1 files changed, 361 insertions, 0 deletions
diff --git a/ext/node/ops/v8.rs b/ext/node/ops/v8.rs
index ebcf6b080..8813d2e18 100644
--- a/ext/node/ops/v8.rs
+++ b/ext/node/ops/v8.rs
@@ -1,6 +1,15 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use deno_core::error::generic_error;
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::v8;
+use deno_core::FastString;
+use deno_core::GarbageCollected;
+use deno_core::ToJsBuffer;
+use std::ptr::NonNull;
+use v8::ValueDeserializerHelper;
+use v8::ValueSerializerHelper;
#[op2(fast)]
pub fn op_v8_cached_data_version_tag() -> u32 {
@@ -30,3 +39,355 @@ pub fn op_v8_get_heap_statistics(
buffer[12] = stats.used_global_handles_size() as f64;
buffer[13] = stats.external_memory() as f64;
}
+
+pub struct Serializer<'a> {
+ inner: v8::ValueSerializer<'a>,
+}
+
+pub struct SerializerDelegate {
+ obj: v8::Global<v8::Object>,
+}
+
+impl<'a> v8::cppgc::GarbageCollected for Serializer<'a> {
+ fn trace(&self, _visitor: &v8::cppgc::Visitor) {}
+}
+
+impl SerializerDelegate {
+ fn obj<'s>(
+ &self,
+ scope: &mut v8::HandleScope<'s>,
+ ) -> v8::Local<'s, v8::Object> {
+ v8::Local::new(scope, &self.obj)
+ }
+}
+
+impl v8::ValueSerializerImpl for SerializerDelegate {
+ fn get_shared_array_buffer_id<'s>(
+ &self,
+ scope: &mut v8::HandleScope<'s>,
+ shared_array_buffer: v8::Local<'s, v8::SharedArrayBuffer>,
+ ) -> Option<u32> {
+ let obj = self.obj(scope);
+ let key = FastString::from_static("_getSharedArrayBufferId")
+ .v8_string(scope)
+ .into();
+ if let Some(v) = obj.get(scope, key) {
+ if let Ok(fun) = v.try_cast::<v8::Function>() {
+ return fun
+ .call(scope, obj.into(), &[shared_array_buffer.into()])
+ .and_then(|ret| ret.uint32_value(scope));
+ }
+ }
+ None
+ }
+ fn has_custom_host_object(&self, _isolate: &mut v8::Isolate) -> bool {
+ false
+ }
+ fn throw_data_clone_error<'s>(
+ &self,
+ scope: &mut v8::HandleScope<'s>,
+ message: v8::Local<'s, v8::String>,
+ ) {
+ let obj = self.obj(scope);
+ let key = FastString::from_static("_getDataCloneError")
+ .v8_string(scope)
+ .into();
+ if let Some(v) = obj.get(scope, key) {
+ let fun = v
+ .try_cast::<v8::Function>()
+ .expect("_getDataCloneError should be a function");
+ if let Some(error) = fun.call(scope, obj.into(), &[message.into()]) {
+ scope.throw_exception(error);
+ return;
+ }
+ }
+ let error = v8::Exception::type_error(scope, message);
+ scope.throw_exception(error);
+ }
+
+ fn write_host_object<'s>(
+ &self,
+ scope: &mut v8::HandleScope<'s>,
+ object: v8::Local<'s, v8::Object>,
+ _value_serializer: &dyn ValueSerializerHelper,
+ ) -> Option<bool> {
+ let obj = self.obj(scope);
+ let key = FastString::from_static("_writeHostObject")
+ .v8_string(scope)
+ .into();
+ if let Some(v) = obj.get(scope, key) {
+ if let Ok(v) = v.try_cast::<v8::Function>() {
+ v.call(scope, obj.into(), &[object.into()])?;
+ return Some(true);
+ }
+ }
+
+ None
+ }
+
+ fn is_host_object<'s>(
+ &self,
+ _scope: &mut v8::HandleScope<'s>,
+ _object: v8::Local<'s, v8::Object>,
+ ) -> Option<bool> {
+ // should never be called because has_custom_host_object returns false
+ None
+ }
+}
+
+#[op2]
+#[cppgc]
+pub fn op_v8_new_serializer(
+ scope: &mut v8::HandleScope,
+ obj: v8::Local<v8::Object>,
+) -> Serializer<'static> {
+ let obj = v8::Global::new(scope, obj);
+ let inner =
+ v8::ValueSerializer::new(scope, Box::new(SerializerDelegate { obj }));
+ Serializer { inner }
+}
+
+#[op2(fast)]
+pub fn op_v8_set_treat_array_buffer_views_as_host_objects(
+ #[cppgc] ser: &Serializer,
+ value: bool,
+) {
+ ser
+ .inner
+ .set_treat_array_buffer_views_as_host_objects(value);
+}
+
+#[op2]
+#[serde]
+pub fn op_v8_release_buffer(#[cppgc] ser: &Serializer) -> ToJsBuffer {
+ ser.inner.release().into()
+}
+
+#[op2(fast)]
+pub fn op_v8_transfer_array_buffer(
+ #[cppgc] ser: &Serializer,
+ #[smi] id: u32,
+ array_buffer: v8::Local<v8::ArrayBuffer>,
+) {
+ ser.inner.transfer_array_buffer(id, array_buffer);
+}
+
+#[op2(fast)]
+pub fn op_v8_write_double(#[cppgc] ser: &Serializer, double: f64) {
+ ser.inner.write_double(double);
+}
+
+#[op2(fast)]
+pub fn op_v8_write_header(#[cppgc] ser: &Serializer) {
+ ser.inner.write_header();
+}
+
+#[op2]
+pub fn op_v8_write_raw_bytes(
+ #[cppgc] ser: &Serializer,
+ #[anybuffer] source: &[u8],
+) {
+ ser.inner.write_raw_bytes(source);
+}
+
+#[op2(fast)]
+pub fn op_v8_write_uint32(#[cppgc] ser: &Serializer, num: u32) {
+ ser.inner.write_uint32(num);
+}
+
+#[op2(fast)]
+pub fn op_v8_write_uint64(#[cppgc] ser: &Serializer, hi: u32, lo: u32) {
+ let num = ((hi as u64) << 32) | (lo as u64);
+ ser.inner.write_uint64(num);
+}
+
+#[op2(nofast, reentrant)]
+pub fn op_v8_write_value(
+ scope: &mut v8::HandleScope,
+ #[cppgc] ser: &Serializer,
+ value: v8::Local<v8::Value>,
+) -> Result<(), AnyError> {
+ let context = scope.get_current_context();
+ ser.inner.write_value(context, value);
+ Ok(())
+}
+
+struct DeserBuffer {
+ ptr: Option<NonNull<u8>>,
+ // Hold onto backing store to keep the underlying buffer
+ // alive while we hold a reference to it.
+ _backing_store: v8::SharedRef<v8::BackingStore>,
+}
+
+pub struct Deserializer<'a> {
+ buf: DeserBuffer,
+ inner: v8::ValueDeserializer<'a>,
+}
+
+impl<'a> deno_core::GarbageCollected for Deserializer<'a> {}
+
+pub struct DeserializerDelegate {
+ obj: v8::Global<v8::Object>,
+}
+
+impl GarbageCollected for DeserializerDelegate {
+ fn trace(&self, _visitor: &v8::cppgc::Visitor) {}
+}
+
+impl v8::ValueDeserializerImpl for DeserializerDelegate {
+ fn read_host_object<'s>(
+ &self,
+ scope: &mut v8::HandleScope<'s>,
+ _value_deserializer: &dyn v8::ValueDeserializerHelper,
+ ) -> Option<v8::Local<'s, v8::Object>> {
+ let obj = v8::Local::new(scope, &self.obj);
+ let key = FastString::from_static("_readHostObject")
+ .v8_string(scope)
+ .into();
+ let scope = &mut v8::AllowJavascriptExecutionScope::new(scope);
+ if let Some(v) = obj.get(scope, key) {
+ if let Ok(v) = v.try_cast::<v8::Function>() {
+ let result = v.call(scope, obj.into(), &[])?;
+ match result.try_cast() {
+ Ok(res) => return Some(res),
+ Err(_) => {
+ let msg =
+ FastString::from_static("readHostObject must return an object")
+ .v8_string(scope);
+ let error = v8::Exception::type_error(scope, msg);
+ scope.throw_exception(error);
+ return None;
+ }
+ }
+ }
+ }
+ None
+ }
+}
+
+#[op2]
+#[cppgc]
+pub fn op_v8_new_deserializer(
+ scope: &mut v8::HandleScope,
+ obj: v8::Local<v8::Object>,
+ buffer: v8::Local<v8::ArrayBufferView>,
+) -> Result<Deserializer<'static>, AnyError> {
+ let offset = buffer.byte_offset();
+ let len = buffer.byte_length();
+ let backing_store = buffer.get_backing_store().ok_or_else(|| {
+ generic_error("deserialization buffer has no backing store")
+ })?;
+ let (buf_slice, buf_ptr) = if let Some(data) = backing_store.data() {
+ // SAFETY: the offset is valid for the underlying buffer because we're getting it directly from v8
+ let data_ptr = unsafe { data.as_ptr().cast::<u8>().add(offset) };
+ (
+ // SAFETY: the len is valid, from v8, and the data_ptr is valid (as above)
+ unsafe { std::slice::from_raw_parts(data_ptr.cast_const().cast(), len) },
+ Some(data.cast()),
+ )
+ } else {
+ (&[] as &[u8], None::<NonNull<u8>>)
+ };
+ let obj = v8::Global::new(scope, obj);
+ let inner = v8::ValueDeserializer::new(
+ scope,
+ Box::new(DeserializerDelegate { obj }),
+ buf_slice,
+ );
+ Ok(Deserializer {
+ inner,
+ buf: DeserBuffer {
+ _backing_store: backing_store,
+ ptr: buf_ptr,
+ },
+ })
+}
+
+#[op2(fast)]
+pub fn op_v8_transfer_array_buffer_de(
+ #[cppgc] deser: &Deserializer,
+ #[smi] id: u32,
+ array_buffer: v8::Local<v8::ArrayBuffer>,
+) {
+ // TODO(nathanwhit): also need binding for TransferSharedArrayBuffer, then call that if
+ // array_buffer is shared
+ deser.inner.transfer_array_buffer(id, array_buffer);
+}
+
+#[op2(fast)]
+pub fn op_v8_read_double(
+ #[cppgc] deser: &Deserializer,
+) -> Result<f64, AnyError> {
+ let mut double = 0f64;
+ if !deser.inner.read_double(&mut double) {
+ return Err(type_error("ReadDouble() failed"));
+ }
+ Ok(double)
+}
+
+#[op2(nofast)]
+pub fn op_v8_read_header(
+ scope: &mut v8::HandleScope,
+ #[cppgc] deser: &Deserializer,
+) -> bool {
+ let context = scope.get_current_context();
+ let res = deser.inner.read_header(context);
+ res.unwrap_or_default()
+}
+
+#[op2(fast)]
+#[number]
+pub fn op_v8_read_raw_bytes(
+ #[cppgc] deser: &Deserializer,
+ #[number] length: usize,
+) -> usize {
+ let Some(buf_ptr) = deser.buf.ptr else {
+ return 0;
+ };
+ if let Some(buf) = deser.inner.read_raw_bytes(length) {
+ let ptr = buf.as_ptr();
+ (ptr as usize) - (buf_ptr.as_ptr() as usize)
+ } else {
+ 0
+ }
+}
+
+#[op2(fast)]
+pub fn op_v8_read_uint32(
+ #[cppgc] deser: &Deserializer,
+) -> Result<u32, AnyError> {
+ let mut value = 0;
+ if !deser.inner.read_uint32(&mut value) {
+ return Err(type_error("ReadUint32() failed"));
+ }
+
+ Ok(value)
+}
+
+#[op2]
+#[serde]
+pub fn op_v8_read_uint64(
+ #[cppgc] deser: &Deserializer,
+) -> Result<(u32, u32), AnyError> {
+ let mut val = 0;
+ if !deser.inner.read_uint64(&mut val) {
+ return Err(type_error("ReadUint64() failed"));
+ }
+
+ Ok(((val >> 32) as u32, val as u32))
+}
+
+#[op2(fast)]
+pub fn op_v8_get_wire_format_version(#[cppgc] deser: &Deserializer) -> u32 {
+ deser.inner.get_wire_format_version()
+}
+
+#[op2(reentrant)]
+pub fn op_v8_read_value<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ #[cppgc] deser: &Deserializer,
+) -> v8::Local<'s, v8::Value> {
+ let context = scope.get_current_context();
+ let val = deser.inner.read_value(context);
+ val.unwrap_or_else(|| v8::null(scope).into())
+}