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:
parent
7ce1a68637
commit
f8435d20b0
10 changed files with 314 additions and 3 deletions
58
cli/tests/unit_node/v8_test.ts
Normal file
58
cli/tests/unit_node/v8_test.ts
Normal 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"));
|
||||
},
|
||||
});
|
|
@ -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()
|
||||
|
|
|
@ -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
29
ext/node/v8.rs
Normal 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;
|
||||
}
|
|
@ -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) }),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
37
ops/lib.rs
37
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"
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
11
ops/optimizer_tests/f64_slice.expected
Normal file
11
ops/optimizer_tests/f64_slice.expected
Normal 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
|
112
ops/optimizer_tests/f64_slice.out
Normal file
112
ops/optimizer_tests/f64_slice.out
Normal 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
|
||||
}
|
3
ops/optimizer_tests/f64_slice.rs
Normal file
3
ops/optimizer_tests/f64_slice.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn op_f64_buf(buffer: &mut [f64]) {
|
||||
// @test-attr:fast
|
||||
}
|
Loading…
Reference in a new issue