diff options
-rw-r--r-- | cli/tests/unit_node/v8_test.ts | 58 | ||||
-rw-r--r-- | ext/node/lib.rs | 3 | ||||
-rw-r--r-- | ext/node/polyfills/v8.ts | 27 | ||||
-rw-r--r-- | ext/node/v8.rs | 29 | ||||
-rw-r--r-- | ops/fast_call.rs | 5 | ||||
-rw-r--r-- | ops/lib.rs | 37 | ||||
-rw-r--r-- | ops/optimizer.rs | 32 | ||||
-rw-r--r-- | ops/optimizer_tests/f64_slice.expected | 11 | ||||
-rw-r--r-- | ops/optimizer_tests/f64_slice.out | 112 | ||||
-rw-r--r-- | ops/optimizer_tests/f64_slice.rs | 3 |
10 files changed, 314 insertions, 3 deletions
diff --git a/cli/tests/unit_node/v8_test.ts b/cli/tests/unit_node/v8_test.ts new file mode 100644 index 000000000..ab1903596 --- /dev/null +++ b/cli/tests/unit_node/v8_test.ts @@ -0,0 +1,58 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + cachedDataVersionTag, + getHeapStatistics, + setFlagsFromString, +} from "node:v8"; +import { + assertEquals, + assertThrows, +} from "../../../test_util/std/testing/asserts.ts"; + +// https://github.com/nodejs/node/blob/a2bbe5ff216bc28f8dac1c36a8750025a93c3827/test/parallel/test-v8-version-tag.js#L6 +Deno.test({ + name: "cachedDataVersionTag success", + fn() { + const tag = cachedDataVersionTag(); + assertEquals(typeof tag, "number"); + assertEquals(cachedDataVersionTag(), tag); + }, +}); + +// https://github.com/nodejs/node/blob/a2bbe5ff216bc28f8dac1c36a8750025a93c3827/test/parallel/test-v8-stats.js#L6 +Deno.test({ + name: "getHeapStatistics success", + fn() { + const s = getHeapStatistics(); + const keys = [ + "does_zap_garbage", + "external_memory", + "heap_size_limit", + "malloced_memory", + "number_of_detached_contexts", + "number_of_native_contexts", + "peak_malloced_memory", + "total_available_size", + "total_global_handles_size", + "total_heap_size", + "total_heap_size_executable", + "total_physical_size", + "used_global_handles_size", + "used_heap_size", + ]; + assertEquals(Object.keys(s).sort(), keys); + for (const k of keys) { + assertEquals( + typeof (s as unknown as Record<string, unknown>)[k], + "number", + ); + } + }, +}); + +Deno.test({ + name: "setFlagsFromString throws", + fn() { + assertThrows(() => setFlagsFromString("--allow_natives_syntax")); + }, +}); diff --git a/ext/node/lib.rs b/ext/node/lib.rs index f4c23a16a..cb273ef9e 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -20,6 +20,7 @@ mod package_json; mod path; mod polyfill; mod resolution; +mod v8; mod winerror; pub use package_json::PackageJson; @@ -413,6 +414,8 @@ pub fn init_polyfill() -> Extension { crypto::op_node_hash_digest::decl(), crypto::op_node_hash_clone::decl(), winerror::op_node_sys_to_uv_error::decl(), + v8::op_v8_cached_data_version_tag::decl(), + v8::op_v8_get_heap_statistics::decl(), op_node_build_os::decl(), ]) .build() diff --git a/ext/node/polyfills/v8.ts b/ext/node/polyfills/v8.ts index f186ff023..c7875e654 100644 --- a/ext/node/polyfills/v8.ts +++ b/ext/node/polyfills/v8.ts @@ -3,8 +3,10 @@ import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +const { ops } = globalThis.__bootstrap.core; + export function cachedDataVersionTag() { - notImplemented("v8.cachedDataVersionTag"); + return ops.op_v8_cached_data_version_tag(); } export function getHeapCodeStatistics() { notImplemented("v8.getHeapCodeStatistics"); @@ -15,9 +17,30 @@ export function getHeapSnapshot() { export function getHeapSpaceStatistics() { notImplemented("v8.getHeapSpaceStatistics"); } + +const buffer = new Float64Array(14); + export function getHeapStatistics() { - notImplemented("v8.getHeapStatistics"); + ops.op_v8_get_heap_statistics(buffer); + + return { + total_heap_size: buffer[0], + total_heap_size_executable: buffer[1], + total_physical_size: buffer[2], + total_available_size: buffer[3], + used_heap_size: buffer[4], + heap_size_limit: buffer[5], + malloced_memory: buffer[6], + peak_malloced_memory: buffer[7], + does_zap_garbage: buffer[8], + number_of_native_contexts: buffer[9], + number_of_detached_contexts: buffer[10], + total_global_handles_size: buffer[11], + used_global_handles_size: buffer[12], + external_memory: buffer[13], + }; } + export function setFlagsFromString() { notImplemented("v8.setFlagsFromString"); } diff --git a/ext/node/v8.rs b/ext/node/v8.rs new file mode 100644 index 000000000..5307f7107 --- /dev/null +++ b/ext/node/v8.rs @@ -0,0 +1,29 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use deno_core::op; +use deno_core::v8; + +#[op] +fn op_v8_cached_data_version_tag() -> u32 { + v8::script_compiler::cached_data_version_tag() +} + +#[op(v8)] +fn op_v8_get_heap_statistics(scope: &mut v8::HandleScope, buffer: &mut [f64]) { + let mut stats = v8::HeapStatistics::default(); + scope.get_heap_statistics(&mut stats); + + buffer[0] = stats.total_heap_size() as f64; + buffer[1] = stats.total_heap_size_executable() as f64; + buffer[2] = stats.total_physical_size() as f64; + buffer[3] = stats.total_available_size() as f64; + buffer[4] = stats.used_heap_size() as f64; + buffer[5] = stats.heap_size_limit() as f64; + buffer[6] = stats.malloced_memory() as f64; + buffer[7] = stats.peak_malloced_memory() as f64; + buffer[8] = stats.does_zap_garbage() as f64; + buffer[9] = stats.number_of_native_contexts() as f64; + buffer[10] = stats.number_of_detached_contexts() as f64; + buffer[11] = stats.total_global_handles_size() as f64; + buffer[12] = stats.used_global_handles_size() as f64; + buffer[13] = stats.external_memory() as f64; +} diff --git a/ops/fast_call.rs b/ops/fast_call.rs index 9093190b2..fe6455f37 100644 --- a/ops/fast_call.rs +++ b/ops/fast_call.rs @@ -426,7 +426,9 @@ fn q_fast_ty(v: &FastValue) -> Quote { FastValue::F64 => q!({ f64 }), FastValue::Bool => q!({ bool }), FastValue::V8Value => q!({ v8::Local<v8::Value> }), - FastValue::Uint8Array | FastValue::Uint32Array => unreachable!(), + FastValue::Uint8Array + | FastValue::Uint32Array + | FastValue::Float64Array => unreachable!(), } } @@ -444,6 +446,7 @@ fn q_fast_ty_variant(v: &FastValue) -> Quote { FastValue::V8Value => q!({ V8Value }), FastValue::Uint8Array => q!({ TypedArray(CType::Uint8) }), FastValue::Uint32Array => q!({ TypedArray(CType::Uint32) }), + FastValue::Float64Array => q!({ TypedArray(CType::Float64) }), } } diff --git a/ops/lib.rs b/ops/lib.rs index 35796d41a..d8f28dd37 100644 --- a/ops/lib.rs +++ b/ops/lib.rs @@ -452,6 +452,13 @@ fn codegen_arg( let #ident = #blck; }; } + Some(SliceType::F64Mut) => { + assert!(!asyncness, "Memory slices are not allowed in async ops"); + let blck = codegen_f64_mut_slice(core, idx); + return quote! { + let #ident = #blck; + }; + } Some(_) => { assert!(!asyncness, "Memory slices are not allowed in async ops"); let blck = codegen_u8_slice(core, idx); @@ -576,6 +583,28 @@ fn codegen_u32_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 { } } +fn codegen_f64_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 { + quote! { + if let Ok(view) = #core::v8::Local::<#core::v8::Float64Array>::try_from(args.get(#idx as i32)) { + let (offset, len) = (view.byte_offset(), view.byte_length()); + let buffer = match view.buffer(scope) { + Some(v) => v, + None => { + return #core::_ops::throw_type_error(scope, format!("Expected Float64Array at position {}", #idx)); + } + }; + if let Some(data) = buffer.data() { + let store = data.cast::<u8>().as_ptr(); + unsafe { ::std::slice::from_raw_parts_mut(store.add(offset) as *mut f64, len / 8) } + } else { + &mut [] + } + } else { + return #core::_ops::throw_type_error(scope, format!("Expected Float64Array at position {}", #idx)); + } + } +} + fn codegen_sync_ret( core: &TokenStream2, output: &syn::ReturnType, @@ -655,6 +684,7 @@ enum SliceType { U8, U8Mut, U32Mut, + F64Mut, } fn is_ref_slice(ty: impl ToTokens) -> Option<SliceType> { @@ -667,6 +697,9 @@ fn is_ref_slice(ty: impl ToTokens) -> Option<SliceType> { if is_u32_slice_mut(&ty) { return Some(SliceType::U32Mut); } + if is_f64_slice_mut(&ty) { + return Some(SliceType::F64Mut); + } None } @@ -682,6 +715,10 @@ fn is_u32_slice_mut(ty: impl ToTokens) -> bool { tokens(ty) == "& mut [u32]" } +fn is_f64_slice_mut(ty: impl ToTokens) -> bool { + tokens(ty) == "& mut [f64]" +} + fn is_ptr_u8(ty: impl ToTokens) -> bool { tokens(ty) == "* const u8" } diff --git a/ops/optimizer.rs b/ops/optimizer.rs index 7c19a7cfd..ae3175511 100644 --- a/ops/optimizer.rs +++ b/ops/optimizer.rs @@ -43,6 +43,7 @@ enum TransformKind { V8Value, SliceU32(bool), SliceU8(bool), + SliceF64(bool), PtrU8, WasmMemory, } @@ -69,6 +70,13 @@ impl Transform { } } + fn slice_f64(index: usize, is_mut: bool) -> Self { + Transform { + kind: TransformKind::SliceF64(is_mut), + index, + } + } + fn wasm_memory(index: usize) -> Self { Transform { kind: TransformKind::WasmMemory, @@ -150,6 +158,20 @@ impl Transform { unsafe { (&*var).get_storage_if_aligned().unwrap_unchecked() }; }) } + TransformKind::SliceF64(_) => { + *ty = + parse_quote! { *const #core::v8::fast_api::FastApiTypedArray<f64> }; + + q!(Vars { var: &ident }, { + let var = match unsafe { &*var }.get_storage_if_aligned() { + Some(v) => v, + None => { + unsafe { &mut *fast_api_callback_options }.fallback = true; + return Default::default(); + } + }; + }) + } TransformKind::WasmMemory => { // Note: `ty` is correctly set to __opts by the fast call tier. // U8 slice is always byte-aligned. @@ -214,6 +236,7 @@ pub(crate) enum FastValue { V8Value, Uint8Array, Uint32Array, + Float64Array, } impl Default for FastValue { @@ -620,6 +643,15 @@ impl Optimizer { .insert(index, Transform::slice_u32(index, is_mut_ref)) .is_none()); } + // Is `T` a f64? + PathSegment { ident, .. } if ident == "f64" => { + self.needs_fast_callback_option = true; + self.fast_parameters.push(FastValue::Float64Array); + assert!(self + .transforms + .insert(index, Transform::slice_f64(index, is_mut_ref)) + .is_none()); + } _ => return Err(BailoutReason::FastUnsupportedParamType), } } diff --git a/ops/optimizer_tests/f64_slice.expected b/ops/optimizer_tests/f64_slice.expected new file mode 100644 index 000000000..32182b004 --- /dev/null +++ b/ops/optimizer_tests/f64_slice.expected @@ -0,0 +1,11 @@ +=== Optimizer Dump === +returns_result: false +has_ref_opstate: false +has_rc_opstate: false +has_fast_callback_option: false +needs_fast_callback_option: true +fast_result: Some(Void) +fast_parameters: [V8Value, Float64Array] +transforms: {0: Transform { kind: SliceF64(true), index: 0 }} +is_async: false +fast_compatible: true diff --git a/ops/optimizer_tests/f64_slice.out b/ops/optimizer_tests/f64_slice.out new file mode 100644 index 000000000..88ccd232a --- /dev/null +++ b/ops/optimizer_tests/f64_slice.out @@ -0,0 +1,112 @@ +#[allow(non_camel_case_types)] +///Auto-generated by `deno_ops`, i.e: `#[op]` +/// +///Use `op_f64_buf::decl()` to get an op-declaration +///you can include in a `deno_core::Extension`. +pub struct op_f64_buf; +#[doc(hidden)] +impl op_f64_buf { + pub fn name() -> &'static str { + stringify!(op_f64_buf) + } + pub fn v8_fn_ptr<'scope>() -> deno_core::v8::FunctionCallback { + use deno_core::v8::MapFnTo; + Self::v8_func.map_fn_to() + } + pub fn decl<'scope>() -> deno_core::OpDecl { + deno_core::OpDecl { + name: Self::name(), + v8_fn_ptr: Self::v8_fn_ptr(), + enabled: true, + fast_fn: Some( + Box::new(op_f64_buf_fast { + _phantom: ::std::marker::PhantomData, + }), + ), + is_async: false, + is_unstable: false, + is_v8: false, + argc: 1usize, + } + } + #[inline] + #[allow(clippy::too_many_arguments)] + fn call(buffer: &mut [f64]) {} + pub fn v8_func<'scope>( + scope: &mut deno_core::v8::HandleScope<'scope>, + args: deno_core::v8::FunctionCallbackArguments, + mut rv: deno_core::v8::ReturnValue, + ) { + let ctx = unsafe { + &*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value() + as *const deno_core::_ops::OpCtx) + }; + let arg_0 = if let Ok(view) + = deno_core::v8::Local::< + deno_core::v8::Float64Array, + >::try_from(args.get(0usize as i32)) { + let (offset, len) = (view.byte_offset(), view.byte_length()); + let buffer = match view.buffer(scope) { + Some(v) => v, + None => { + return deno_core::_ops::throw_type_error( + scope, + format!("Expected Float64Array at position {}", 0usize), + ); + } + }; + if let Some(data) = buffer.data() { + let store = data.cast::<u8>().as_ptr(); + unsafe { + ::std::slice::from_raw_parts_mut( + store.add(offset) as *mut f64, + len / 8, + ) + } + } else { + &mut [] + } + } else { + return deno_core::_ops::throw_type_error( + scope, + format!("Expected Float64Array at position {}", 0usize), + ); + }; + let result = Self::call(arg_0); + let op_state = ::std::cell::RefCell::borrow(&*ctx.state); + op_state.tracker.track_sync(ctx.id); + } +} +struct op_f64_buf_fast { + _phantom: ::std::marker::PhantomData<()>, +} +impl<'scope> deno_core::v8::fast_api::FastFunction for op_f64_buf_fast { + fn function(&self) -> *const ::std::ffi::c_void { + op_f64_buf_fast_fn as *const ::std::ffi::c_void + } + fn args(&self) -> &'static [deno_core::v8::fast_api::Type] { + use deno_core::v8::fast_api::Type::*; + use deno_core::v8::fast_api::CType; + &[V8Value, TypedArray(CType::Float64), CallbackOptions] + } + fn return_type(&self) -> deno_core::v8::fast_api::CType { + deno_core::v8::fast_api::CType::Void + } +} +fn op_f64_buf_fast_fn<'scope>( + _: deno_core::v8::Local<deno_core::v8::Object>, + buffer: *const deno_core::v8::fast_api::FastApiTypedArray<f64>, + fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, +) -> () { + use deno_core::v8; + use deno_core::_ops; + let buffer = match unsafe { &*buffer }.get_storage_if_aligned() { + Some(v) => v, + None => { + unsafe { &mut *fast_api_callback_options }.fallback = true; + return Default::default(); + } + }; + let result = op_f64_buf::call(buffer); + result +} diff --git a/ops/optimizer_tests/f64_slice.rs b/ops/optimizer_tests/f64_slice.rs new file mode 100644 index 000000000..fa2778531 --- /dev/null +++ b/ops/optimizer_tests/f64_slice.rs @@ -0,0 +1,3 @@ +fn op_f64_buf(buffer: &mut [f64]) { + // @test-attr:fast +} |