diff --git a/build.rs b/build.rs index af1d02e2..6bb7c3d8 100644 --- a/build.rs +++ b/build.rs @@ -24,6 +24,8 @@ fn main() { println!("cargo:rerun-if-changed=.gn"); println!("cargo:rerun-if-changed=BUILD.gn"); println!("cargo:rerun-if-changed=src/binding.cc"); + println!("cargo:rerun-if-changed=src/binding.hpp"); + println!("cargo:rerun-if-changed=build.rs"); // These are all the environment variables that we check. This is // probably more than what is needed, but missing an important @@ -147,7 +149,11 @@ fn build_binding() { .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .clang_args(["-x", "c++", "-std=c++20", "-Iv8/include"]) .clang_args(args) + .respect_cxx_access_specs(true) .allowlist_item("RUST_.*") + .blocklist_item("v8::.*") + .blocklist_item("cppgc::.*") + .blocklist_item("std::.*") .generate() .expect("Unable to generate bindings"); diff --git a/src/binding.cc b/src/binding.cc index 290e09fe..b9a8ed64 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1,4 +1,6 @@ // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. +#include "binding.hpp" + #include #include #include @@ -1071,35 +1073,6 @@ v8__String__GetExternalStringResourceBase(const v8::String& self, return self.GetExternalStringResourceBase(encoding_out); } -class ExternalOneByteString : public v8::String::ExternalOneByteStringResource { - public: - using RustDestroy = void (*)(char*, size_t); - ExternalOneByteString(char* data, int length, RustDestroy rustDestroy, - v8::Isolate* isolate) - : _data(data), - _length(length), - _rustDestroy(rustDestroy), - _isolate(isolate) { - _isolate->AdjustAmountOfExternalAllocatedMemory( - static_cast(_length)); - } - ~ExternalOneByteString() override { - (*_rustDestroy)(_data, _length); - _isolate->AdjustAmountOfExternalAllocatedMemory( - -static_cast(-_length)); - } - - const char* data() const override { return _data; } - - size_t length() const override { return _length; } - - private: - char* _data; - const int _length; - RustDestroy _rustDestroy; - v8::Isolate* _isolate; -}; - class ExternalStaticOneByteStringResource : public v8::String::ExternalOneByteStringResource { public: @@ -1138,7 +1111,7 @@ class ExternalConstOneByteStringResource const int _length; }; -const v8::String* v8__String__NewExternalOneByteConst( +const v8::String* v8__String__NewExternalOneByte( v8::Isolate* isolate, v8::String::ExternalOneByteStringResource* resource) { return maybe_local_to_ptr(v8::String::NewExternalOneByte(isolate, resource)); } @@ -1150,13 +1123,6 @@ const v8::String* v8__String__NewExternalOneByteStatic(v8::Isolate* isolate, isolate, new ExternalStaticOneByteStringResource(data, length))); } -const v8::String* v8__String__NewExternalOneByte( - v8::Isolate* isolate, char* data, int length, - ExternalOneByteString::RustDestroy rustDestroy) { - return maybe_local_to_ptr(v8::String::NewExternalOneByte( - isolate, new ExternalOneByteString(data, length, rustDestroy, isolate))); -} - const char* v8__ExternalOneByteStringResource__data( v8::String::ExternalOneByteStringResource* self) { return self->data(); @@ -3787,31 +3753,23 @@ void v8__PropertyDescriptor__set_configurable(v8::PropertyDescriptor* self, self->set_configurable(configurable); } +RUST_ExternalOneByteString::RUST_ExternalOneByteString( + char* data, int length, + RUST_ExternalOneByteString::RustDestroyFn rustDestroy, v8::Isolate* isolate) + : _data(data), + _length(length), + _rustDestroy(rustDestroy), + _isolate(isolate) { + _isolate->AdjustAmountOfExternalAllocatedMemory( + static_cast(_length)); +} + } // extern "C" // cppgc extern "C" { -class RustObj; - -using RustTraceFn = void (*)(const RustObj* obj, cppgc::Visitor*); -using RustDestroyFn = void (*)(const RustObj* obj); - -class RustObj final : public cppgc::GarbageCollected { - public: - explicit RustObj(RustTraceFn trace, RustDestroyFn destroy) - : trace_(trace), destroy_(destroy) {} - - ~RustObj() { destroy_(this); } - - void Trace(cppgc::Visitor* visitor) const { trace_(this, visitor); } - - private: - RustTraceFn trace_; - RustDestroyFn destroy_; -}; - RustObj* v8__Object__Unwrap(v8::Isolate* isolate, const v8::Object& wrapper, v8::CppHeapPointerTag tag) { v8::CppHeapPointerTagRange tag_range(tag, tag); @@ -3858,8 +3816,8 @@ void cppgc__heap__collect_garbage_for_testing( } RustObj* cppgc__make_garbage_collectable(v8::CppHeap* heap, size_t size, - RustTraceFn trace, - RustDestroyFn destroy) { + RustObj::RustTraceFn trace, + RustObj::RustDestroyFn destroy) { return cppgc::MakeGarbageCollected(heap->GetAllocationHandle(), cppgc::AdditionalBytes(size), trace, destroy); @@ -3948,4 +3906,5 @@ RustObj* cppgc__WeakPersistent__Get(cppgc::WeakPersistent* self) { return self->Get(); } +// } // extern "C" diff --git a/src/binding.hpp b/src/binding.hpp index 31e16b09..fde83854 100644 --- a/src/binding.hpp +++ b/src/binding.hpp @@ -1,4 +1,5 @@ #include +#include #include /** @@ -6,12 +7,53 @@ * and made available in `crate::binding` in rust. */ -namespace { +extern "C" { +class RUST_ExternalOneByteString + : public v8::String::ExternalOneByteStringResource { + public: + using RustDestroyFn = void (*)(char*, size_t); + // bindgen doesn't support generating bindings for inline + // constructors/functions + RUST_ExternalOneByteString(char* data, int length, RustDestroyFn rustDestroy, + v8::Isolate* isolate); -class RustObj; + ~RUST_ExternalOneByteString() { + (*_rustDestroy)(_data, _length); + _isolate->AdjustAmountOfExternalAllocatedMemory( + -static_cast(-_length)); + } + const char* data() const { return _data; } + + size_t length() const { return static_cast(_length); } + + private: + char* const _data; + const size_t _length; + RustDestroyFn _rustDestroy; + v8::Isolate* _isolate; +}; + +class RustObj final : public cppgc::GarbageCollected { + public: + using RustTraceFn = void (*)(const RustObj* obj, cppgc::Visitor*); + using RustDestroyFn = void (*)(const RustObj* obj); + explicit RustObj(RustTraceFn trace, RustDestroyFn destroy) + : trace_(trace), destroy_(destroy) {} + + ~RustObj() { destroy_(this); } + + void Trace(cppgc::Visitor* visitor) const { trace_(this, visitor); } + + private: + RustTraceFn trace_; + RustDestroyFn destroy_; +}; } +// Allocate memory using C++'s `new` operator +void* RUST_new(size_t size) { return operator new(size); } + static size_t RUST_v8__ScriptOrigin_SIZE = sizeof(v8::ScriptOrigin); static size_t RUST_cppgc__Member_SIZE = sizeof(cppgc::Member); diff --git a/src/binding.rs b/src/binding.rs index 3ef84eac..b22b1997 100644 --- a/src/binding.rs +++ b/src/binding.rs @@ -1,4 +1,7 @@ #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +#![allow(dead_code)] include!(env!("RUSTY_V8_SRC_BINDING_PATH")); + +pub use crate::Isolate as v8_Isolate; diff --git a/src/string.rs b/src/string.rs index 33926d98..0345b04f 100644 --- a/src/string.rs +++ b/src/string.rs @@ -79,11 +79,6 @@ extern "C" { encoding: *mut Encoding, ) -> *mut ExternalStringResourceBase; - fn v8__String__NewExternalOneByteConst( - isolate: *mut Isolate, - onebyte_const: *const OneByteConst, - ) -> *const String; - fn v8__String__NewExternalOneByteStatic( isolate: *mut Isolate, buffer: *const char, @@ -92,9 +87,7 @@ extern "C" { fn v8__String__NewExternalOneByte( isolate: *mut Isolate, - buffer: *mut char, - length: int, - free: extern "C" fn(*mut char, size_t), + resource: *mut ExternalOneByteStringResource, ) -> *const String; fn v8__String__NewExternalTwoByteStatic( @@ -571,7 +564,11 @@ impl String { ) -> Option> { unsafe { scope.cast_local(|sd| { - v8__String__NewExternalOneByteConst(sd.get_isolate_ptr(), onebyte_const) + v8__String__NewExternalOneByte( + sd.get_isolate_ptr(), + onebyte_const as *const _ as *mut OneByteConst + as *mut ExternalOneByteStringResource, + ) }) } } @@ -606,12 +603,51 @@ impl String { let buffer_len = buffer.len().try_into().ok()?; unsafe { scope.cast_local(|sd| { - v8__String__NewExternalOneByte( - sd.get_isolate_ptr(), + let ptr = crate::support::cpp_new::< + crate::binding::RUST_ExternalOneByteString, + >(); + crate::binding::RUST_ExternalOneByteString_RUST_ExternalOneByteString( + ptr, Box::into_raw(buffer).cast::(), buffer_len, - free_rust_external_onebyte, - ) + Some(free_rust_external_onebyte), + sd.get_isolate_ptr().cast(), + ); + + v8__String__NewExternalOneByte(sd.get_isolate_ptr(), ptr.cast()) + }) + } + } + + /// Creates a `v8::String` from owned bytes, length, and a custom destructor. + /// The bytes must be Latin-1 or ASCII. + /// V8 will take ownership of the buffer and free it when the string is garbage collected. + /// + /// SAFETY: `buffer` must be owned (valid for the lifetime of the string), and + /// `destructor` must be a valid function pointer that can free the buffer. + /// The destructor will be called with the buffer and length when the string is garbage collected. + #[inline(always)] + pub unsafe fn new_external_onebyte_raw<'s>( + scope: &mut HandleScope<'s, ()>, + buffer: *mut char, + buffer_len: usize, + destructor: extern "C" fn(*mut char, usize), + ) -> Option> { + let buffer_len = buffer_len.try_into().ok()?; + unsafe { + scope.cast_local(|sd| { + let ptr = crate::support::cpp_new::< + crate::binding::RUST_ExternalOneByteString, + >(); + crate::binding::RUST_ExternalOneByteString_RUST_ExternalOneByteString( + ptr, + buffer.cast::(), + buffer_len, + Some(destructor), + sd.get_isolate_ptr().cast(), + ); + + v8__String__NewExternalOneByte(sd.get_isolate_ptr(), ptr.cast()) }) } } diff --git a/src/support.rs b/src/support.rs index fedbe444..76787068 100644 --- a/src/support.rs +++ b/src/support.rs @@ -41,6 +41,10 @@ pub use std::os::raw::c_long as long; pub type Opaque = [u8; 0]; +pub(crate) fn cpp_new() -> *mut T { + unsafe { crate::binding::RUST_new(std::mem::size_of::()).cast() } +} + /// Pointer to object allocated on the C++ heap. The pointer may be null. #[repr(transparent)] #[derive(Debug)]