1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 08:33:43 -05:00

feat(ext/node): implement node:v8 (#17806)

Closes https://github.com/denoland/deno/issues/17115 

Implements `cachedDataVersionTag` and `getHeapStatistics`.
This commit is contained in:
Divy Srivastava 2023-02-17 18:48:09 +05:30 committed by GitHub
parent 7ce1a68637
commit f8435d20b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 314 additions and 3 deletions

View file

@ -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"));
},
});

View file

@ -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()

View file

@ -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");
}

29
ext/node/v8.rs Normal file
View file

@ -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;
}

View file

@ -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) }),
}
}

View file

@ -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"
}

View file

@ -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),
}
}

View file

@ -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

View file

@ -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
}

View file

@ -0,0 +1,3 @@
fn op_f64_buf(buffer: &mut [f64]) {
// @test-attr:fast
}