diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/js/10_dispatch_buffer.js | 150 | ||||
-rw-r--r-- | runtime/js/11_timers.js | 3 | ||||
-rw-r--r-- | runtime/js/12_io.js | 10 | ||||
-rw-r--r-- | runtime/ops/io.rs | 8 | ||||
-rw-r--r-- | runtime/ops/mod.rs | 20 | ||||
-rw-r--r-- | runtime/ops/ops_buffer.rs | 377 | ||||
-rw-r--r-- | runtime/ops/timers.rs | 2 |
7 files changed, 19 insertions, 551 deletions
diff --git a/runtime/js/10_dispatch_buffer.js b/runtime/js/10_dispatch_buffer.js deleted file mode 100644 index 091fce504..000000000 --- a/runtime/js/10_dispatch_buffer.js +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -"use strict"; - -((window) => { - const core = window.Deno.core; - - function assert(cond) { - if (!cond) { - throw Error("assert"); - } - } - - //////////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////// General async handling ////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////////////////// - - // General Async response handling - let nextRequestId = 1; - const promiseTable = {}; - - function opAsync(opName, opRequestBuilder, opResultParser) { - // Make sure requests of this type are handled by the asyncHandler - // The asyncHandler's role is to call the "promiseTable[requestId]" function - core.setAsyncHandlerByName(opName, (bufUi8, _) => { - const [requestId, result, error] = opResultParser(bufUi8, true); - if (error !== null) { - promiseTable[requestId][1](error); - } else { - promiseTable[requestId][0](result); - } - delete promiseTable[requestId]; - }); - - const requestId = nextRequestId++; - - // Create and store promise - const promise = new Promise((resolve, reject) => { - promiseTable[requestId] = [resolve, reject]; - }); - - // Synchronously dispatch async request - core.dispatchByName(opName, ...opRequestBuilder(requestId)); - - // Wait for async response - return promise; - } - - function opSync(opName, opRequestBuilder, opResultParser) { - const rawResult = core.dispatchByName(opName, ...opRequestBuilder()); - - const [_, result, error] = opResultParser(rawResult, false); - if (error !== null) throw error; - return result; - } - - //////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////// Error handling ///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////////////////// - - function handleError(className, message) { - const [ErrorClass, args] = core.getErrorClassAndArgs(className); - if (!ErrorClass) { - return new Error( - `Unregistered error class: "${className}"\n` + - ` ${message}\n` + - ` Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`, - ); - } - return new ErrorClass(message, ...args); - } - - //////////////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////// Buffer ops handling ////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////////////////// - - const scratchBytes = new ArrayBuffer(3 * 4); - const scratchView = new DataView( - scratchBytes, - scratchBytes.byteOffset, - scratchBytes.byteLength, - ); - - function bufferOpBuildRequest(requestId, argument, zeroCopy) { - scratchView.setBigUint64(0, BigInt(requestId), true); - scratchView.setUint32(8, argument, true); - return [scratchView, ...zeroCopy]; - } - - function bufferOpParseResult(bufUi8, isCopyNeeded) { - // Decode header value from ui8 buffer - const headerByteLength = 4 * 4; - assert(bufUi8.byteLength >= headerByteLength); - assert(bufUi8.byteLength % 4 == 0); - const view = new DataView( - bufUi8.buffer, - bufUi8.byteOffset + bufUi8.byteLength - headerByteLength, - headerByteLength, - ); - - const requestId = Number(view.getBigUint64(0, true)); - const status = view.getUint32(8, true); - const result = view.getUint32(12, true); - - // Error handling - if (status !== 0) { - const className = core.decode(bufUi8.subarray(0, result)); - const message = core.decode(bufUi8.subarray(result, -headerByteLength)) - .trim(); - - return [requestId, null, handleError(className, message)]; - } - - if (bufUi8.byteLength === headerByteLength) { - return [requestId, result, null]; - } - - // Rest of response buffer is passed as reference or as a copy - let respBuffer = null; - if (isCopyNeeded) { - // Copy part of the response array (if sent through shared array buf) - respBuffer = bufUi8.slice(0, result); - } else { - // Create view on existing array (if sent through overflow) - respBuffer = bufUi8.subarray(0, result); - } - - return [requestId, respBuffer, null]; - } - - function bufferOpAsync(opName, argument = 0, ...zeroCopy) { - return opAsync( - opName, - (requestId) => bufferOpBuildRequest(requestId, argument, zeroCopy), - bufferOpParseResult, - ); - } - - function bufferOpSync(opName, argument = 0, ...zeroCopy) { - return opSync( - opName, - () => bufferOpBuildRequest(0, argument, zeroCopy), - bufferOpParseResult, - ); - } - - window.__bootstrap.dispatchBuffer = { - bufferOpSync, - bufferOpAsync, - }; -})(this); diff --git a/runtime/js/11_timers.js b/runtime/js/11_timers.js index f07622388..7a0307c06 100644 --- a/runtime/js/11_timers.js +++ b/runtime/js/11_timers.js @@ -4,7 +4,6 @@ ((window) => { const assert = window.__bootstrap.util.assert; const core = window.Deno.core; - const { bufferOpSync } = window.__bootstrap.dispatchBuffer; function opStopGlobalTimer() { core.jsonOpSync("op_global_timer_stop"); @@ -20,7 +19,7 @@ const nowBytes = new Uint8Array(8); function opNow() { - bufferOpSync("op_now", 0, nowBytes); + core.binOpSync("op_now", 0, nowBytes); return new DataView(nowBytes.buffer).getFloat64(); } diff --git a/runtime/js/12_io.js b/runtime/js/12_io.js index 09e87f990..fe815c7ed 100644 --- a/runtime/js/12_io.js +++ b/runtime/js/12_io.js @@ -6,8 +6,8 @@ "use strict"; ((window) => { + const core = window.Deno.core; const DEFAULT_BUFFER_SIZE = 32 * 1024; - const { bufferOpSync, bufferOpAsync } = window.__bootstrap.dispatchBuffer; // Seek whence values. // https://golang.org/pkg/io/#pkg-constants const SeekMode = { @@ -81,7 +81,7 @@ return 0; } - const nread = bufferOpSync("op_read_sync", rid, buffer); + const nread = core.binOpSync("op_read_sync", rid, buffer); if (nread < 0) { throw new Error("read error"); } @@ -97,7 +97,7 @@ return 0; } - const nread = await bufferOpAsync("op_read_async", rid, buffer); + const nread = await core.binOpAsync("op_read_async", rid, buffer); if (nread < 0) { throw new Error("read error"); } @@ -106,7 +106,7 @@ } function writeSync(rid, data) { - const result = bufferOpSync("op_write_sync", rid, data); + const result = core.binOpSync("op_write_sync", rid, data); if (result < 0) { throw new Error("write error"); } @@ -115,7 +115,7 @@ } async function write(rid, data) { - const result = await bufferOpAsync("op_write_async", rid, data); + const result = await core.binOpAsync("op_write_async", rid, data); if (result < 0) { throw new Error("write error"); } diff --git a/runtime/ops/io.rs b/runtime/ops/io.rs index e1520b2c5..1260452b6 100644 --- a/runtime/ops/io.rs +++ b/runtime/ops/io.rs @@ -99,11 +99,11 @@ lazy_static! { } pub fn init(rt: &mut JsRuntime) { - super::reg_buffer_async(rt, "op_read_async", op_read_async); - super::reg_buffer_async(rt, "op_write_async", op_write_async); + super::reg_bin_async(rt, "op_read_async", op_read_async); + super::reg_bin_async(rt, "op_write_async", op_write_async); - super::reg_buffer_sync(rt, "op_read_sync", op_read_sync); - super::reg_buffer_sync(rt, "op_write_sync", op_write_sync); + super::reg_bin_sync(rt, "op_read_sync", op_read_sync); + super::reg_bin_sync(rt, "op_write_sync", op_write_sync); super::reg_json_async(rt, "op_shutdown", op_shutdown); } diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs index e082c5d3a..2e94d99f5 100644 --- a/runtime/ops/mod.rs +++ b/runtime/ops/mod.rs @@ -8,7 +8,6 @@ pub mod io; pub mod net; #[cfg(unix)] mod net_unix; -mod ops_buffer; pub mod os; pub mod permissions; pub mod plugin; @@ -25,6 +24,8 @@ pub mod websocket; pub mod worker_host; use crate::metrics::metrics_op; +use deno_core::bin_op_async; +use deno_core::bin_op_sync; use deno_core::error::AnyError; use deno_core::json_op_async; use deno_core::json_op_sync; @@ -33,10 +34,8 @@ use deno_core::serde::Serialize; use deno_core::BufVec; use deno_core::JsRuntime; use deno_core::OpState; +use deno_core::ValueOrVector; use deno_core::ZeroCopyBuf; -use ops_buffer::buffer_op_async; -use ops_buffer::buffer_op_sync; -use ops_buffer::ValueOrVector; use std::cell::RefCell; use std::future::Future; use std::rc::Rc; @@ -63,24 +62,21 @@ where rt.register_op(name, metrics_op(name, json_op_sync(op_fn))); } -pub fn reg_buffer_async<F, R, RV>( - rt: &mut JsRuntime, - name: &'static str, - op_fn: F, -) where +pub fn reg_bin_async<F, R, RV>(rt: &mut JsRuntime, name: &'static str, op_fn: F) +where F: Fn(Rc<RefCell<OpState>>, u32, BufVec) -> R + 'static, R: Future<Output = Result<RV, AnyError>> + 'static, RV: ValueOrVector, { - rt.register_op(name, metrics_op(name, buffer_op_async(op_fn))); + rt.register_op(name, metrics_op(name, bin_op_async(op_fn))); } -pub fn reg_buffer_sync<F, R>(rt: &mut JsRuntime, name: &'static str, op_fn: F) +pub fn reg_bin_sync<F, R>(rt: &mut JsRuntime, name: &'static str, op_fn: F) where F: Fn(&mut OpState, u32, &mut [ZeroCopyBuf]) -> Result<R, AnyError> + 'static, R: ValueOrVector, { - rt.register_op(name, metrics_op(name, buffer_op_sync(op_fn))); + rt.register_op(name, metrics_op(name, bin_op_sync(op_fn))); } /// `UnstableChecker` is a struct so it can be placed inside `GothamState`; diff --git a/runtime/ops/ops_buffer.rs b/runtime/ops/ops_buffer.rs deleted file mode 100644 index 6998144cf..000000000 --- a/runtime/ops/ops_buffer.rs +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -use deno_core::error::AnyError; -use deno_core::futures::future::FutureExt; -use deno_core::BufVec; -use deno_core::Op; -use deno_core::OpFn; -use deno_core::OpState; -use deno_core::ZeroCopyBuf; -use std::boxed::Box; -use std::cell::RefCell; -use std::convert::TryInto; -use std::future::Future; -use std::rc::Rc; - -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct RequestHeader { - pub request_id: u64, - pub argument: u32, -} - -impl RequestHeader { - pub fn from_raw(bytes: &[u8]) -> Option<Self> { - if bytes.len() < 3 * 4 { - return None; - } - - Some(Self { - request_id: u64::from_le_bytes(bytes[0..8].try_into().unwrap()), - argument: u32::from_le_bytes(bytes[8..12].try_into().unwrap()), - }) - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct ResponseHeader { - pub request_id: u64, - pub status: u32, - pub result: u32, -} - -impl Into<[u8; 16]> for ResponseHeader { - fn into(self) -> [u8; 16] { - let mut resp_header = [0u8; 16]; - resp_header[0..8].copy_from_slice(&self.request_id.to_le_bytes()); - resp_header[8..12].copy_from_slice(&self.status.to_le_bytes()); - resp_header[12..16].copy_from_slice(&self.result.to_le_bytes()); - resp_header - } -} - -pub trait ValueOrVector { - fn value(&self) -> u32; - fn vector(self) -> Option<Vec<u8>>; -} - -impl ValueOrVector for Vec<u8> { - fn value(&self) -> u32 { - self.len() as u32 - } - fn vector(self) -> Option<Vec<u8>> { - Some(self) - } -} - -impl ValueOrVector for u32 { - fn value(&self) -> u32 { - *self - } - fn vector(self) -> Option<Vec<u8>> { - None - } -} - -fn gen_padding_32bit(len: usize) -> &'static [u8] { - &[b' ', b' ', b' '][0..(4 - (len & 3)) & 3] -} - -/// Creates an op that passes data synchronously using raw ui8 buffer. -/// -/// The provided function `op_fn` has the following parameters: -/// * `&mut OpState`: the op state, can be used to read/write resources in the runtime from an op. -/// * `argument`: the i32 value that is passed to the Rust function. -/// * `&mut [ZeroCopyBuf]`: raw bytes passed along. -/// -/// `op_fn` returns an array buffer value, which is directly returned to JavaScript. -/// -/// When registering an op like this... -/// ```ignore -/// let mut runtime = JsRuntime::new(...); -/// runtime.register_op("hello", deno_core::buffer_op_sync(Self::hello_op)); -/// ``` -/// -/// ...it can be invoked from JS using the provided name, for example: -/// ```js -/// Deno.core.ops(); -/// let result = Deno.core.bufferOpSync("function_name", args); -/// ``` -/// -/// The `Deno.core.ops()` statement is needed once before any op calls, for initialization. -/// A more complete example is available in the examples directory. -pub fn buffer_op_sync<F, R>(op_fn: F) -> Box<OpFn> -where - F: Fn(&mut OpState, u32, &mut [ZeroCopyBuf]) -> Result<R, AnyError> + 'static, - R: ValueOrVector, -{ - Box::new(move |state: Rc<RefCell<OpState>>, bufs: BufVec| -> Op { - let mut bufs_iter = bufs.into_iter(); - let record_buf = bufs_iter.next().expect("Expected record at position 0"); - let mut zero_copy = bufs_iter.collect::<BufVec>(); - - let req_header = match RequestHeader::from_raw(&record_buf) { - Some(r) => r, - None => { - let error_class = b"TypeError"; - let error_message = b"Unparsable control buffer"; - let len = error_class.len() + error_message.len(); - let padding = gen_padding_32bit(len); - let resp_header = ResponseHeader { - request_id: 0, - status: 1, - result: error_class.len() as u32, - }; - return Op::Sync( - error_class - .iter() - .chain(error_message.iter()) - .chain(padding) - .chain(&Into::<[u8; 16]>::into(resp_header)) - .cloned() - .collect(), - ); - } - }; - - match op_fn(&mut state.borrow_mut(), req_header.argument, &mut zero_copy) { - Ok(possibly_vector) => { - let resp_header = ResponseHeader { - request_id: req_header.request_id, - status: 0, - result: possibly_vector.value(), - }; - let resp_encoded_header = Into::<[u8; 16]>::into(resp_header); - - let resp_vector = match possibly_vector.vector() { - Some(mut vector) => { - let padding = gen_padding_32bit(vector.len()); - vector.extend(padding); - vector.extend(&resp_encoded_header); - vector - } - None => resp_encoded_header.to_vec(), - }; - Op::Sync(resp_vector.into_boxed_slice()) - } - Err(error) => { - let error_class = - (state.borrow().get_error_class_fn)(&error).as_bytes(); - let error_message = error.to_string().as_bytes().to_owned(); - let len = error_class.len() + error_message.len(); - let padding = gen_padding_32bit(len); - let resp_header = ResponseHeader { - request_id: req_header.request_id, - status: 1, - result: error_class.len() as u32, - }; - return Op::Sync( - error_class - .iter() - .chain(error_message.iter()) - .chain(padding) - .chain(&Into::<[u8; 16]>::into(resp_header)) - .cloned() - .collect(), - ); - } - } - }) -} - -/// Creates an op that passes data asynchronously using raw ui8 buffer. -/// -/// The provided function `op_fn` has the following parameters: -/// * `Rc<RefCell<OpState>>`: the op state, can be used to read/write resources in the runtime from an op. -/// * `argument`: the i32 value that is passed to the Rust function. -/// * `BufVec`: raw bytes passed along, usually not needed if the JSON value is used. -/// -/// `op_fn` returns a future, whose output is a JSON value. This value will be asynchronously -/// returned to JavaScript. -/// -/// When registering an op like this... -/// ```ignore -/// let mut runtime = JsRuntime::new(...); -/// runtime.register_op("hello", deno_core::json_op_async(Self::hello_op)); -/// ``` -/// -/// ...it can be invoked from JS using the provided name, for example: -/// ```js -/// Deno.core.ops(); -/// let future = Deno.core.jsonOpAsync("function_name", args); -/// ``` -/// -/// The `Deno.core.ops()` statement is needed once before any op calls, for initialization. -/// A more complete example is available in the examples directory. -pub fn buffer_op_async<F, R, RV>(op_fn: F) -> Box<OpFn> -where - F: Fn(Rc<RefCell<OpState>>, u32, BufVec) -> R + 'static, - R: Future<Output = Result<RV, AnyError>> + 'static, - RV: ValueOrVector, -{ - Box::new(move |state: Rc<RefCell<OpState>>, bufs: BufVec| -> Op { - let mut bufs_iter = bufs.into_iter(); - let record_buf = bufs_iter.next().expect("Expected record at position 0"); - let zero_copy = bufs_iter.collect::<BufVec>(); - - let req_header = match RequestHeader::from_raw(&record_buf) { - Some(r) => r, - None => { - let error_class = b"TypeError"; - let error_message = b"Unparsable control buffer"; - let len = error_class.len() + error_message.len(); - let padding = gen_padding_32bit(len); - let resp_header = ResponseHeader { - request_id: 0, - status: 1, - result: error_class.len() as u32, - }; - return Op::Sync( - error_class - .iter() - .chain(error_message.iter()) - .chain(padding) - .chain(&Into::<[u8; 16]>::into(resp_header)) - .cloned() - .collect(), - ); - } - }; - - let fut = - op_fn(state.clone(), req_header.argument, zero_copy).map(move |result| { - match result { - Ok(possibly_vector) => { - let resp_header = ResponseHeader { - request_id: req_header.request_id, - status: 0, - result: possibly_vector.value(), - }; - let resp_encoded_header = Into::<[u8; 16]>::into(resp_header); - - let resp_vector = match possibly_vector.vector() { - Some(mut vector) => { - let padding = gen_padding_32bit(vector.len()); - vector.extend(padding); - vector.extend(&resp_encoded_header); - vector - } - None => resp_encoded_header.to_vec(), - }; - resp_vector.into_boxed_slice() - } - Err(error) => { - let error_class = - (state.borrow().get_error_class_fn)(&error).as_bytes(); - let error_message = error.to_string().as_bytes().to_owned(); - let len = error_class.len() + error_message.len(); - let padding = gen_padding_32bit(len); - let resp_header = ResponseHeader { - request_id: req_header.request_id, - status: 1, - result: error_class.len() as u32, - }; - - error_class - .iter() - .chain(error_message.iter()) - .chain(padding) - .chain(&Into::<[u8; 16]>::into(resp_header)) - .cloned() - .collect() - } - } - }); - let temp = Box::pin(fut); - Op::Async(temp) - }) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn padding() { - assert_eq!(gen_padding_32bit(0), &[] as &[u8]); - assert_eq!(gen_padding_32bit(1), &[b' ', b' ', b' ']); - assert_eq!(gen_padding_32bit(2), &[b' ', b' ']); - assert_eq!(gen_padding_32bit(3), &[b' ']); - assert_eq!(gen_padding_32bit(4), &[] as &[u8]); - assert_eq!(gen_padding_32bit(5), &[b' ', b' ', b' ']); - } - - #[test] - fn response_header_to_bytes() { - // Max size of an js Number is 1^53 - 1, so use this value as max for 64bit ´request_id´ - let resp_header = ResponseHeader { - request_id: 0x0102030405060708u64, - status: 0x090A0B0Cu32, - result: 0x0D0E0F10u32, - }; - - // All numbers are always little-endian encoded, as the js side also wants this to be fixed - assert_eq!( - &Into::<[u8; 16]>::into(resp_header), - &[8, 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, 9, 16, 15, 14, 13] - ); - } - - #[test] - fn response_header_to_bytes_max_value() { - // Max size of an js Number is 1^53 - 1, so use this value as max for 64bit ´request_id´ - let resp_header = ResponseHeader { - request_id: (1u64 << 53u64) - 1u64, - status: 0xFFFFFFFFu32, - result: 0xFFFFFFFFu32, - }; - - // All numbers are always little-endian encoded, as the js side also wants this to be fixed - assert_eq!( - &Into::<[u8; 16]>::into(resp_header), - &[ - 255, 255, 255, 255, 255, 255, 31, 0, 255, 255, 255, 255, 255, 255, 255, - 255 - ] - ); - } - - #[test] - fn request_header_from_bytes() { - let req_header = - RequestHeader::from_raw(&[8, 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, 9]) - .unwrap(); - - assert_eq!(req_header.request_id, 0x0102030405060708u64); - assert_eq!(req_header.argument, 0x090A0B0Cu32); - } - - #[test] - fn request_header_from_bytes_max_value() { - let req_header = RequestHeader::from_raw(&[ - 255, 255, 255, 255, 255, 255, 31, 0, 255, 255, 255, 255, - ]) - .unwrap(); - - assert_eq!(req_header.request_id, (1u64 << 53u64) - 1u64); - assert_eq!(req_header.argument, 0xFFFFFFFFu32); - } - - #[test] - fn request_header_from_bytes_too_short() { - let req_header = - RequestHeader::from_raw(&[8, 7, 6, 5, 4, 3, 2, 1, 12, 11, 10]); - - assert_eq!(req_header, None); - } - - #[test] - fn request_header_from_bytes_long() { - let req_header = RequestHeader::from_raw(&[ - 8, 7, 6, 5, 4, 3, 2, 1, 12, 11, 10, 9, 13, 14, 15, 16, 17, 18, 19, 20, 21, - ]) - .unwrap(); - - assert_eq!(req_header.request_id, 0x0102030405060708u64); - assert_eq!(req_header.argument, 0x090A0B0Cu32); - } -} diff --git a/runtime/ops/timers.rs b/runtime/ops/timers.rs index 445b7366c..4395b4885 100644 --- a/runtime/ops/timers.rs +++ b/runtime/ops/timers.rs @@ -77,7 +77,7 @@ pub fn init(rt: &mut deno_core::JsRuntime) { super::reg_json_sync(rt, "op_global_timer_stop", op_global_timer_stop); super::reg_json_sync(rt, "op_global_timer_start", op_global_timer_start); super::reg_json_async(rt, "op_global_timer", op_global_timer); - super::reg_buffer_sync(rt, "op_now", op_now); + super::reg_bin_sync(rt, "op_now", op_now); super::reg_json_sync(rt, "op_sleep_sync", op_sleep_sync); } |