mirror of
https://github.com/denoland/deno.git
synced 2025-10-02 23:24:37 +00:00
feat(ops): fast calls for Wasm (#16776)
This PR introduces Wasm ops. These calls are optimized for entry from Wasm land. The `#[op(wasm)]` attribute is opt-in. Last parameter `Option<&mut [u8]>` is the memory slice of the Wasm module *when entered from a Fast API call*. Otherwise, the user is expected to implement logic to obtain the memory if `None` ```rust #[op(wasm)] pub fn op_args_get( offset: i32, buffer_offset: i32, memory: Option<&mut [u8]>, ) { // ... } ```
This commit is contained in:
parent
9ffc6acdbb
commit
ca66978a5a
11 changed files with 291 additions and 8 deletions
28
core/examples/wasm.js
Normal file
28
core/examples/wasm.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
// asc wasm.ts --exportStart --initialMemory 6400 -O -o wasm.wasm
|
||||||
|
// deno-fmt-ignore
|
||||||
|
const bytes = new Uint8Array([
|
||||||
|
0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 2,
|
||||||
|
15, 1, 3, 111, 112, 115, 7, 111, 112, 95, 119, 97, 115, 109, 0,
|
||||||
|
0, 3, 3, 2, 0, 0, 5, 4, 1, 0, 128, 50, 7, 36, 4,
|
||||||
|
7, 111, 112, 95, 119, 97, 115, 109, 0, 0, 4, 99, 97, 108, 108,
|
||||||
|
0, 1, 6, 109, 101, 109, 111, 114, 121, 2, 0, 6, 95, 115, 116,
|
||||||
|
97, 114, 116, 0, 2, 10, 10, 2, 4, 0, 16, 0, 11, 3, 0,
|
||||||
|
1, 11
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { ops } = Deno.core;
|
||||||
|
|
||||||
|
const module = new WebAssembly.Module(bytes);
|
||||||
|
const instance = new WebAssembly.Instance(module, { ops });
|
||||||
|
ops.op_set_wasm_mem(instance.exports.memory);
|
||||||
|
|
||||||
|
instance.exports.call();
|
||||||
|
|
||||||
|
const memory = instance.exports.memory;
|
||||||
|
const view = new Uint8Array(memory.buffer);
|
||||||
|
|
||||||
|
if (view[0] !== 69) {
|
||||||
|
throw new Error("Expected first byte to be 69");
|
||||||
|
}
|
67
core/examples/wasm.rs
Normal file
67
core/examples/wasm.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::op;
|
||||||
|
use deno_core::Extension;
|
||||||
|
use deno_core::JsRuntime;
|
||||||
|
use deno_core::RuntimeOptions;
|
||||||
|
use std::mem::transmute;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
// This is a hack to make the `#[op]` macro work with
|
||||||
|
// deno_core examples.
|
||||||
|
// You can remove this:
|
||||||
|
|
||||||
|
use deno_core::*;
|
||||||
|
|
||||||
|
struct WasmMemory(NonNull<v8::WasmMemoryObject>);
|
||||||
|
|
||||||
|
fn wasm_memory_unchecked(state: &mut OpState) -> &mut [u8] {
|
||||||
|
let WasmMemory(global) = state.borrow::<WasmMemory>();
|
||||||
|
// SAFETY: `v8::Local` is always non-null pointer; the `HandleScope` is
|
||||||
|
// already on the stack, but we don't have access to it.
|
||||||
|
let memory_object = unsafe {
|
||||||
|
transmute::<NonNull<v8::WasmMemoryObject>, v8::Local<v8::WasmMemoryObject>>(
|
||||||
|
*global,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let backing_store = memory_object.buffer().get_backing_store();
|
||||||
|
let ptr = backing_store.data().unwrap().as_ptr() as *mut u8;
|
||||||
|
let len = backing_store.byte_length();
|
||||||
|
// SAFETY: `ptr` is a valid pointer to `len` bytes.
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(ptr, len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op(wasm)]
|
||||||
|
fn op_wasm(state: &mut OpState, memory: Option<&mut [u8]>) {
|
||||||
|
let memory = memory.unwrap_or_else(|| wasm_memory_unchecked(state));
|
||||||
|
memory[0] = 69;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op(v8)]
|
||||||
|
fn op_set_wasm_mem(
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
state: &mut OpState,
|
||||||
|
memory: serde_v8::Value,
|
||||||
|
) {
|
||||||
|
let memory =
|
||||||
|
v8::Local::<v8::WasmMemoryObject>::try_from(memory.v8_value).unwrap();
|
||||||
|
let global = v8::Global::new(scope, memory);
|
||||||
|
state.put(WasmMemory(global.into_raw()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Build a deno_core::Extension providing custom ops
|
||||||
|
let ext = Extension::builder()
|
||||||
|
.ops(vec![op_wasm::decl(), op_set_wasm_mem::decl()])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Initialize a runtime instance
|
||||||
|
let mut runtime = JsRuntime::new(RuntimeOptions {
|
||||||
|
extensions: vec![ext],
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
runtime
|
||||||
|
.execute_script("<usage>", include_str!("wasm.js"))
|
||||||
|
.unwrap();
|
||||||
|
}
|
7
core/examples/wasm.ts
Normal file
7
core/examples/wasm.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
export declare function op_wasm(): void;
|
||||||
|
|
||||||
|
export function call(): void {
|
||||||
|
op_wasm();
|
||||||
|
}
|
|
@ -34,8 +34,8 @@ Cases where code is optimized away:
|
||||||
The macro will infer and try to auto generate V8 fast API call trait impl for
|
The macro will infer and try to auto generate V8 fast API call trait impl for
|
||||||
`sync` ops with:
|
`sync` ops with:
|
||||||
|
|
||||||
- arguments: integers, bool, `&mut OpState`, `&[u8]`, &mut [u8]`,`&[u32]`,`&mut
|
- arguments: integers, bool, `&mut OpState`, `&[u8]`, `&mut [u8]`, `&[u32]`,
|
||||||
[u32]`
|
`&mut [u32]`
|
||||||
- return_type: integers, bool
|
- return_type: integers, bool
|
||||||
|
|
||||||
The `#[op(fast)]` attribute should be used to enforce fast call generation at
|
The `#[op(fast)]` attribute should be used to enforce fast call generation at
|
||||||
|
@ -43,3 +43,20 @@ compile time.
|
||||||
|
|
||||||
Trait gen for `async` ops & a ZeroCopyBuf equivalent type is planned and will be
|
Trait gen for `async` ops & a ZeroCopyBuf equivalent type is planned and will be
|
||||||
added soon.
|
added soon.
|
||||||
|
|
||||||
|
### Wasm calls
|
||||||
|
|
||||||
|
The `#[op(wasm)]` attribute should be used for calls expected to be called from
|
||||||
|
Wasm. This enables the fast call generation and allows seamless `WasmMemory`
|
||||||
|
integration for generic and fast calls.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[op(wasm)]
|
||||||
|
pub fn op_args_get(
|
||||||
|
offset: i32,
|
||||||
|
buffer_offset: i32,
|
||||||
|
memory: Option<&[u8]>, // Must be last parameter. Some(..) when entered from Wasm.
|
||||||
|
) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
11
ops/attrs.rs
11
ops/attrs.rs
|
@ -11,6 +11,7 @@ pub struct Attributes {
|
||||||
pub is_v8: bool,
|
pub is_v8: bool,
|
||||||
pub must_be_fast: bool,
|
pub must_be_fast: bool,
|
||||||
pub deferred: bool,
|
pub deferred: bool,
|
||||||
|
pub is_wasm: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for Attributes {
|
impl Parse for Attributes {
|
||||||
|
@ -20,18 +21,22 @@ impl Parse for Attributes {
|
||||||
let vars: Vec<_> = vars.iter().map(Ident::to_string).collect();
|
let vars: Vec<_> = vars.iter().map(Ident::to_string).collect();
|
||||||
let vars: Vec<_> = vars.iter().map(String::as_str).collect();
|
let vars: Vec<_> = vars.iter().map(String::as_str).collect();
|
||||||
for var in vars.iter() {
|
for var in vars.iter() {
|
||||||
if !["unstable", "v8", "fast", "deferred"].contains(var) {
|
if !["unstable", "v8", "fast", "deferred", "wasm"].contains(var) {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
input.span(),
|
input.span(),
|
||||||
"invalid attribute, expected one of: unstable, v8, fast, deferred",
|
"invalid attribute, expected one of: unstable, v8, fast, deferred, wasm",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let is_wasm = vars.contains(&"wasm");
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
is_unstable: vars.contains(&"unstable"),
|
is_unstable: vars.contains(&"unstable"),
|
||||||
is_v8: vars.contains(&"v8"),
|
is_v8: vars.contains(&"v8"),
|
||||||
must_be_fast: vars.contains(&"fast"),
|
|
||||||
deferred: vars.contains(&"deferred"),
|
deferred: vars.contains(&"deferred"),
|
||||||
|
must_be_fast: is_wasm || vars.contains(&"fast"),
|
||||||
|
is_wasm,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,6 +139,7 @@ pub(crate) fn generate(
|
||||||
|
|
||||||
// Apply *hard* optimizer hints.
|
// Apply *hard* optimizer hints.
|
||||||
if optimizer.has_fast_callback_option
|
if optimizer.has_fast_callback_option
|
||||||
|
|| optimizer.has_wasm_memory
|
||||||
|| optimizer.needs_opstate()
|
|| optimizer.needs_opstate()
|
||||||
|| optimizer.is_async
|
|| optimizer.is_async
|
||||||
|| optimizer.needs_fast_callback_option
|
|| optimizer.needs_fast_callback_option
|
||||||
|
@ -147,7 +148,7 @@ pub(crate) fn generate(
|
||||||
fast_api_callback_options: *mut #core::v8::fast_api::FastApiCallbackOptions
|
fast_api_callback_options: *mut #core::v8::fast_api::FastApiCallbackOptions
|
||||||
};
|
};
|
||||||
|
|
||||||
if optimizer.has_fast_callback_option {
|
if optimizer.has_fast_callback_option || optimizer.has_wasm_memory {
|
||||||
// Replace last parameter.
|
// Replace last parameter.
|
||||||
assert!(fast_fn_inputs.pop().is_some());
|
assert!(fast_fn_inputs.pop().is_some());
|
||||||
fast_fn_inputs.push(decl);
|
fast_fn_inputs.push(decl);
|
||||||
|
@ -174,6 +175,7 @@ pub(crate) fn generate(
|
||||||
if optimizer.needs_opstate()
|
if optimizer.needs_opstate()
|
||||||
|| optimizer.is_async
|
|| optimizer.is_async
|
||||||
|| optimizer.has_fast_callback_option
|
|| optimizer.has_fast_callback_option
|
||||||
|
|| optimizer.has_wasm_memory
|
||||||
{
|
{
|
||||||
// Dark arts 🪄 ✨
|
// Dark arts 🪄 ✨
|
||||||
//
|
//
|
||||||
|
|
12
ops/lib.rs
12
ops/lib.rs
|
@ -386,7 +386,9 @@ fn codegen_arg(
|
||||||
let ident = quote::format_ident!("{name}");
|
let ident = quote::format_ident!("{name}");
|
||||||
let (pat, ty) = match arg {
|
let (pat, ty) = match arg {
|
||||||
syn::FnArg::Typed(pat) => {
|
syn::FnArg::Typed(pat) => {
|
||||||
if is_optional_fast_callback_option(&pat.ty) {
|
if is_optional_fast_callback_option(&pat.ty)
|
||||||
|
|| is_optional_wasm_memory(&pat.ty)
|
||||||
|
{
|
||||||
return quote! { let #ident = None; };
|
return quote! { let #ident = None; };
|
||||||
}
|
}
|
||||||
(&pat.pat, &pat.ty)
|
(&pat.pat, &pat.ty)
|
||||||
|
@ -663,6 +665,10 @@ fn is_optional_fast_callback_option(ty: impl ToTokens) -> bool {
|
||||||
tokens(&ty).contains("Option < & mut FastApiCallbackOptions")
|
tokens(&ty).contains("Option < & mut FastApiCallbackOptions")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_optional_wasm_memory(ty: impl ToTokens) -> bool {
|
||||||
|
tokens(&ty).contains("Option < & mut [u8]")
|
||||||
|
}
|
||||||
|
|
||||||
/// Detects if the type can be set using `rv.set_uint32` fast path
|
/// Detects if the type can be set using `rv.set_uint32` fast path
|
||||||
fn is_u32_rv(ty: impl ToTokens) -> bool {
|
fn is_u32_rv(ty: impl ToTokens) -> bool {
|
||||||
["u32", "u8", "u16"].iter().any(|&s| tokens(&ty) == s) || is_resource_id(&ty)
|
["u32", "u8", "u16"].iter().any(|&s| tokens(&ty) == s) || is_resource_id(&ty)
|
||||||
|
@ -743,6 +749,10 @@ mod tests {
|
||||||
if source.contains("// @test-attr:fast") {
|
if source.contains("// @test-attr:fast") {
|
||||||
attrs.must_be_fast = true;
|
attrs.must_be_fast = true;
|
||||||
}
|
}
|
||||||
|
if source.contains("// @test-attr:wasm") {
|
||||||
|
attrs.is_wasm = true;
|
||||||
|
attrs.must_be_fast = true;
|
||||||
|
}
|
||||||
|
|
||||||
let item = syn::parse_str(&source).expect("Failed to parse test file");
|
let item = syn::parse_str(&source).expect("Failed to parse test file");
|
||||||
let op = Op::new(item, attrs);
|
let op = Op::new(item, attrs);
|
||||||
|
|
|
@ -26,6 +26,7 @@ enum TransformKind {
|
||||||
SliceU32(bool),
|
SliceU32(bool),
|
||||||
SliceU8(bool),
|
SliceU8(bool),
|
||||||
PtrU8,
|
PtrU8,
|
||||||
|
WasmMemory,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transform {
|
impl Transform {
|
||||||
|
@ -50,6 +51,13 @@ impl Transform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wasm_memory(index: usize) -> Self {
|
||||||
|
Transform {
|
||||||
|
kind: TransformKind::WasmMemory,
|
||||||
|
index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn u8_ptr(index: usize) -> Self {
|
fn u8_ptr(index: usize) -> Self {
|
||||||
Transform {
|
Transform {
|
||||||
kind: TransformKind::PtrU8,
|
kind: TransformKind::PtrU8,
|
||||||
|
@ -124,6 +132,16 @@ impl Transform {
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
TransformKind::WasmMemory => {
|
||||||
|
// Note: `ty` is correctly set to __opts by the fast call tier.
|
||||||
|
q!(Vars { var: &ident, core }, {
|
||||||
|
let var = unsafe {
|
||||||
|
&*(__opts.wasm_memory
|
||||||
|
as *const core::v8::fast_api::FastApiTypedArray<u8>)
|
||||||
|
}
|
||||||
|
.get_storage_if_aligned();
|
||||||
|
})
|
||||||
|
}
|
||||||
// *const u8
|
// *const u8
|
||||||
TransformKind::PtrU8 => {
|
TransformKind::PtrU8 => {
|
||||||
*ty =
|
*ty =
|
||||||
|
@ -201,6 +219,8 @@ pub(crate) struct Optimizer {
|
||||||
// Do we depend on FastApiCallbackOptions?
|
// Do we depend on FastApiCallbackOptions?
|
||||||
pub(crate) needs_fast_callback_option: bool,
|
pub(crate) needs_fast_callback_option: bool,
|
||||||
|
|
||||||
|
pub(crate) has_wasm_memory: bool,
|
||||||
|
|
||||||
pub(crate) fast_result: Option<FastValue>,
|
pub(crate) fast_result: Option<FastValue>,
|
||||||
pub(crate) fast_parameters: Vec<FastValue>,
|
pub(crate) fast_parameters: Vec<FastValue>,
|
||||||
|
|
||||||
|
@ -262,6 +282,9 @@ impl Optimizer {
|
||||||
|
|
||||||
self.is_async = op.is_async;
|
self.is_async = op.is_async;
|
||||||
self.fast_compatible = true;
|
self.fast_compatible = true;
|
||||||
|
// Just assume for now. We will validate later.
|
||||||
|
self.has_wasm_memory = op.attrs.is_wasm;
|
||||||
|
|
||||||
let sig = &op.item.sig;
|
let sig = &op.item.sig;
|
||||||
|
|
||||||
// Analyze return type
|
// Analyze return type
|
||||||
|
@ -419,7 +442,32 @@ impl Optimizer {
|
||||||
TypeReference { elem, .. },
|
TypeReference { elem, .. },
|
||||||
))) = args.last()
|
))) = args.last()
|
||||||
{
|
{
|
||||||
if let Type::Path(TypePath {
|
if self.has_wasm_memory {
|
||||||
|
// -> Option<&mut [u8]>
|
||||||
|
if let Type::Slice(TypeSlice { elem, .. }) = &**elem {
|
||||||
|
if let Type::Path(TypePath {
|
||||||
|
path: Path { segments, .. },
|
||||||
|
..
|
||||||
|
}) = &**elem
|
||||||
|
{
|
||||||
|
let segment = single_segment(segments)?;
|
||||||
|
|
||||||
|
match segment {
|
||||||
|
// Is `T` a u8?
|
||||||
|
PathSegment { ident, .. } if ident == "u8" => {
|
||||||
|
self.needs_fast_callback_option = true;
|
||||||
|
assert!(self
|
||||||
|
.transforms
|
||||||
|
.insert(index, Transform::wasm_memory(index))
|
||||||
|
.is_none());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(BailoutReason::FastUnsupportedParamType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Type::Path(TypePath {
|
||||||
path: Path { segments, .. },
|
path: Path { segments, .. },
|
||||||
..
|
..
|
||||||
}) = &**elem
|
}) = &**elem
|
||||||
|
@ -654,6 +702,10 @@ mod tests {
|
||||||
.expect("Failed to read expected file");
|
.expect("Failed to read expected file");
|
||||||
|
|
||||||
let mut attrs = Attributes::default();
|
let mut attrs = Attributes::default();
|
||||||
|
if source.contains("// @test-attr:wasm") {
|
||||||
|
attrs.must_be_fast = true;
|
||||||
|
attrs.is_wasm = true;
|
||||||
|
}
|
||||||
if source.contains("// @test-attr:fast") {
|
if source.contains("// @test-attr:fast") {
|
||||||
attrs.must_be_fast = true;
|
attrs.must_be_fast = true;
|
||||||
}
|
}
|
||||||
|
|
11
ops/optimizer_tests/wasm_op.expected
Normal file
11
ops/optimizer_tests/wasm_op.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]
|
||||||
|
transforms: {0: Transform { kind: WasmMemory, index: 0 }}
|
||||||
|
is_async: false
|
||||||
|
fast_compatible: true
|
81
ops/optimizer_tests/wasm_op.out
Normal file
81
ops/optimizer_tests/wasm_op.out
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
///Auto-generated by `deno_ops`, i.e: `#[op]`
|
||||||
|
///
|
||||||
|
///Use `op_wasm::decl()` to get an op-declaration
|
||||||
|
///you can include in a `deno_core::Extension`.
|
||||||
|
pub struct op_wasm;
|
||||||
|
#[doc(hidden)]
|
||||||
|
impl op_wasm {
|
||||||
|
pub fn name() -> &'static str {
|
||||||
|
stringify!(op_wasm)
|
||||||
|
}
|
||||||
|
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_wasm_fast {
|
||||||
|
_phantom: ::std::marker::PhantomData,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
is_async: false,
|
||||||
|
is_unstable: false,
|
||||||
|
is_v8: false,
|
||||||
|
argc: 1usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn call(memory: Option<&mut [u8]>) {}
|
||||||
|
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 = None;
|
||||||
|
let result = Self::call(arg_0);
|
||||||
|
let op_state = ::std::cell::RefCell::borrow(&*ctx.state);
|
||||||
|
op_state.tracker.track_sync(ctx.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct op_wasm_fast {
|
||||||
|
_phantom: ::std::marker::PhantomData<()>,
|
||||||
|
}
|
||||||
|
impl<'scope> deno_core::v8::fast_api::FastFunction for op_wasm_fast {
|
||||||
|
fn function(&self) -> *const ::std::ffi::c_void {
|
||||||
|
op_wasm_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, CallbackOptions]
|
||||||
|
}
|
||||||
|
fn return_type(&self) -> deno_core::v8::fast_api::CType {
|
||||||
|
deno_core::v8::fast_api::CType::Void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn op_wasm_fast_fn<'scope>(
|
||||||
|
_: deno_core::v8::Local<deno_core::v8::Object>,
|
||||||
|
fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
|
||||||
|
) -> () {
|
||||||
|
use deno_core::v8;
|
||||||
|
use deno_core::_ops;
|
||||||
|
let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe {
|
||||||
|
&mut *fast_api_callback_options
|
||||||
|
};
|
||||||
|
let memory = unsafe {
|
||||||
|
&*(__opts.wasm_memory as *const deno_core::v8::fast_api::FastApiTypedArray<u8>)
|
||||||
|
}
|
||||||
|
.get_storage_if_aligned();
|
||||||
|
let result = op_wasm::call(memory);
|
||||||
|
result
|
||||||
|
}
|
3
ops/optimizer_tests/wasm_op.rs
Normal file
3
ops/optimizer_tests/wasm_op.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn op_wasm(memory: Option<&mut [u8]>) {
|
||||||
|
// @test-attr:wasm
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue