mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 20:29:11 +00:00
refactor: Move bin ops to deno_core and unify logic with json ops (#9457)
This commit moves implementation of bin ops to "deno_core" crates as well as unifying logic between bin ops and json ops to reuse as much code as possible (both in Rust and JavaScript).
This commit is contained in:
parent
0d26a82ea9
commit
1251c89321
16 changed files with 410 additions and 606 deletions
|
@ -8,9 +8,9 @@ import {
|
||||||
|
|
||||||
const readErrorStackPattern = new RegExp(
|
const readErrorStackPattern = new RegExp(
|
||||||
`^.*
|
`^.*
|
||||||
at handleError \\(.*10_dispatch_buffer\\.js:.*\\)
|
at handleError \\(.*core\\.js:.*\\)
|
||||||
at bufferOpParseResult \\(.*10_dispatch_buffer\\.js:.*\\)
|
at binOpParseResult \\(.*core\\.js:.*\\)
|
||||||
at Array.<anonymous> \\(.*10_dispatch_buffer\\.js:.*\\).*$`,
|
at asyncHandle \\(.*core\\.js:.*\\).*$`,
|
||||||
"ms",
|
"ms",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ declare global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unitTest(function bufferOpsHeaderTooShort(): void {
|
unitTest(function binOpsHeaderTooShort(): void {
|
||||||
for (const op of ["op_read_sync", "op_read_async"]) {
|
for (const op of ["op_read_sync", "op_read_async"]) {
|
||||||
const readOpId = Deno.core.ops()[op];
|
const readOpId = Deno.core.ops()[op];
|
||||||
const res = Deno.core.send(
|
const res = Deno.core.send(
|
|
@ -19,7 +19,7 @@ unitTest(function malformedJsonControlBuffer(): void {
|
||||||
assertMatch(resObj.err.message, /\bexpected value\b/);
|
assertMatch(resObj.err.message, /\bexpected value\b/);
|
||||||
});
|
});
|
||||||
|
|
||||||
unitTest(function invalidPromiseId(): void {
|
unitTest(function invalidRequestId(): void {
|
||||||
const opId = Deno.core.ops()["op_open_async"];
|
const opId = Deno.core.ops()["op_open_async"];
|
||||||
const reqBuf = new Uint8Array([0, 0, 0, 0, 0, 0, 0]);
|
const reqBuf = new Uint8Array([0, 0, 0, 0, 0, 0, 0]);
|
||||||
const resBuf = Deno.core.send(opId, reqBuf);
|
const resBuf = Deno.core.send(opId, reqBuf);
|
||||||
|
@ -28,5 +28,5 @@ unitTest(function invalidPromiseId(): void {
|
||||||
console.error(resText);
|
console.error(resText);
|
||||||
assertStrictEquals(resObj.ok, undefined);
|
assertStrictEquals(resObj.ok, undefined);
|
||||||
assertStrictEquals(resObj.err.className, "TypeError");
|
assertStrictEquals(resObj.err.className, "TypeError");
|
||||||
assertMatch(resObj.err.message, /\bpromiseId\b/);
|
assertMatch(resObj.err.message, /\brequestId\b/);
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,7 +15,7 @@ import "./console_test.ts";
|
||||||
import "./copy_file_test.ts";
|
import "./copy_file_test.ts";
|
||||||
import "./custom_event_test.ts";
|
import "./custom_event_test.ts";
|
||||||
import "./dir_test.ts";
|
import "./dir_test.ts";
|
||||||
import "./dispatch_buffer_test.ts";
|
import "./dispatch_bin_test.ts";
|
||||||
import "./dispatch_json_test.ts";
|
import "./dispatch_json_test.ts";
|
||||||
import "./error_stack_test.ts";
|
import "./error_stack_test.ts";
|
||||||
import "./event_test.ts";
|
import "./event_test.ts";
|
||||||
|
|
278
core/core.js
278
core/core.js
|
@ -30,13 +30,22 @@ SharedQueue Binary Layout
|
||||||
const core = window.Deno.core;
|
const core = window.Deno.core;
|
||||||
const { recv, send } = core;
|
const { recv, send } = core;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////// Dispatch /////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const dispatch = send;
|
||||||
|
const dispatchByName = (opName, control, ...zeroCopy) =>
|
||||||
|
dispatch(opsCache[opName], control, ...zeroCopy);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////// Shared array buffer ///////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
let sharedBytes;
|
let sharedBytes;
|
||||||
let shared32;
|
let shared32;
|
||||||
|
|
||||||
let asyncHandlers;
|
|
||||||
|
|
||||||
let opsCache = {};
|
let opsCache = {};
|
||||||
const errorMap = {};
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
const shared = core.shared;
|
const shared = core.shared;
|
||||||
|
@ -45,6 +54,7 @@ SharedQueue Binary Layout
|
||||||
assert(shared32 == null);
|
assert(shared32 == null);
|
||||||
sharedBytes = new Uint8Array(shared);
|
sharedBytes = new Uint8Array(shared);
|
||||||
shared32 = new Int32Array(shared);
|
shared32 = new Int32Array(shared);
|
||||||
|
|
||||||
asyncHandlers = [];
|
asyncHandlers = [];
|
||||||
// Callers should not call core.recv, use setAsyncHandler.
|
// Callers should not call core.recv, use setAsyncHandler.
|
||||||
recv(handleAsyncMsgFromRust);
|
recv(handleAsyncMsgFromRust);
|
||||||
|
@ -150,15 +160,43 @@ SharedQueue Binary Layout
|
||||||
return [opId, buf];
|
return [opId, buf];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////// Error handling //////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const errorMap = {};
|
||||||
|
|
||||||
|
function registerErrorClass(errorName, className, args) {
|
||||||
|
if (typeof errorMap[errorName] !== "undefined") {
|
||||||
|
throw new TypeError(`Error class for "${errorName}" already registered`);
|
||||||
|
}
|
||||||
|
errorMap[errorName] = [className, args ?? []];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(className, message) {
|
||||||
|
if (typeof errorMap[className] === "undefined") {
|
||||||
|
return new Error(
|
||||||
|
`Unregistered error class: "${className}"\n` +
|
||||||
|
` ${message}\n` +
|
||||||
|
` Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [ErrorClass, args] = errorMap[className];
|
||||||
|
return new ErrorClass(message, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////// Async handling //////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
let asyncHandlers = [];
|
||||||
|
|
||||||
function setAsyncHandler(opId, cb) {
|
function setAsyncHandler(opId, cb) {
|
||||||
assert(opId != null);
|
assert(opId != null);
|
||||||
asyncHandlers[opId] = cb;
|
asyncHandlers[opId] = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAsyncHandlerByName(opName, cb) {
|
|
||||||
setAsyncHandler(opsCache[opName], cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAsyncMsgFromRust() {
|
function handleAsyncMsgFromRust() {
|
||||||
while (true) {
|
while (true) {
|
||||||
const opIdBuf = shift();
|
const opIdBuf = shift();
|
||||||
|
@ -166,108 +204,196 @@ SharedQueue Binary Layout
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(asyncHandlers[opIdBuf[0]] != null);
|
assert(asyncHandlers[opIdBuf[0]] != null);
|
||||||
asyncHandlers[opIdBuf[0]](opIdBuf[1]);
|
asyncHandlers[opIdBuf[0]](opIdBuf[1], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < arguments.length; i += 2) {
|
for (let i = 0; i < arguments.length; i += 2) {
|
||||||
asyncHandlers[arguments[i]](arguments[i + 1]);
|
asyncHandlers[arguments[i]](arguments[i + 1], false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispatch(opName, control, ...zeroCopy) {
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
return send(opsCache[opName], control, ...zeroCopy);
|
///////////////////////////// General sync & async ops handling ////////////////////////////
|
||||||
}
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function registerErrorClass(errorName, className, args) {
|
let nextRequestId = 1;
|
||||||
if (typeof errorMap[errorName] !== "undefined") {
|
|
||||||
throw new TypeError(`Error class for "${errorName}" already registered`);
|
|
||||||
}
|
|
||||||
errorMap[errorName] = [className, args ?? []];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getErrorClassAndArgs(errorName) {
|
|
||||||
return errorMap[errorName] ?? [undefined, []];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns Uint8Array
|
|
||||||
function encodeJson(args) {
|
|
||||||
const s = JSON.stringify(args);
|
|
||||||
return core.encode(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeJson(ui8) {
|
|
||||||
const s = core.decode(ui8);
|
|
||||||
return JSON.parse(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
let nextPromiseId = 1;
|
|
||||||
const promiseTable = {};
|
const promiseTable = {};
|
||||||
|
|
||||||
function processResponse(res) {
|
function asyncHandle(u8Array, isCopyNeeded, opResultParser) {
|
||||||
if ("ok" in res) {
|
const [requestId, result, error] = opResultParser(u8Array, isCopyNeeded);
|
||||||
return res.ok;
|
if (error !== null) {
|
||||||
|
promiseTable[requestId][1](error);
|
||||||
|
} else {
|
||||||
|
promiseTable[requestId][0](result);
|
||||||
}
|
}
|
||||||
const [ErrorClass, args] = getErrorClassAndArgs(res.err.className);
|
delete promiseTable[requestId];
|
||||||
if (!ErrorClass) {
|
}
|
||||||
throw new Error(
|
|
||||||
`Unregistered error class: "${res.err.className}"\n ${res.err.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`,
|
function opAsync(opName, opRequestBuilder, opResultParser) {
|
||||||
|
const opId = opsCache[opName];
|
||||||
|
// Make sure requests of this type are handled by the asyncHandler
|
||||||
|
// The asyncHandler's role is to call the "promiseTable[requestId]" function
|
||||||
|
if (typeof asyncHandlers[opId] === "undefined") {
|
||||||
|
asyncHandlers[opId] = (buffer, isCopyNeeded) =>
|
||||||
|
asyncHandle(buffer, isCopyNeeded, opResultParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestId = nextRequestId++;
|
||||||
|
|
||||||
|
// Create and store promise
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
promiseTable[requestId] = [resolve, reject];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Synchronously dispatch async request
|
||||||
|
core.dispatch(opId, ...opRequestBuilder(requestId));
|
||||||
|
|
||||||
|
// Wait for async response
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function opSync(opName, opRequestBuilder, opResultParser) {
|
||||||
|
const opId = opsCache[opName];
|
||||||
|
const u8Array = core.dispatch(opId, ...opRequestBuilder());
|
||||||
|
|
||||||
|
const [_, result, error] = opResultParser(u8Array, false);
|
||||||
|
if (error !== null) throw error;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////// Bin ops handling /////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const binRequestHeaderByteLength = 8 + 4;
|
||||||
|
const scratchBuffer = new ArrayBuffer(binRequestHeaderByteLength);
|
||||||
|
const scratchView = new DataView(scratchBuffer);
|
||||||
|
|
||||||
|
function binOpBuildRequest(requestId, argument, zeroCopy) {
|
||||||
|
scratchView.setBigUint64(0, BigInt(requestId), true);
|
||||||
|
scratchView.setUint32(8, argument, true);
|
||||||
|
return [scratchView, ...zeroCopy];
|
||||||
|
}
|
||||||
|
|
||||||
|
function binOpParseResult(u8Array, isCopyNeeded) {
|
||||||
|
// Decode header value from u8Array
|
||||||
|
const headerByteLength = 8 + 2 * 4;
|
||||||
|
assert(u8Array.byteLength >= headerByteLength);
|
||||||
|
assert(u8Array.byteLength % 4 == 0);
|
||||||
|
const view = new DataView(
|
||||||
|
u8Array.buffer,
|
||||||
|
u8Array.byteOffset + u8Array.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(u8Array.subarray(0, result));
|
||||||
|
const message = core.decode(u8Array.subarray(result, -headerByteLength))
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
return [requestId, null, handleError(className, message)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u8Array.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 = u8Array.slice(0, result);
|
||||||
|
} else {
|
||||||
|
// Create view on existing array (if sent through overflow)
|
||||||
|
respBuffer = u8Array.subarray(0, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [requestId, respBuffer, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
function binOpAsync(opName, argument = 0, ...zeroCopy) {
|
||||||
|
return opAsync(
|
||||||
|
opName,
|
||||||
|
(requestId) => binOpBuildRequest(requestId, argument, zeroCopy),
|
||||||
|
binOpParseResult,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw new ErrorClass(res.err.message, ...args);
|
|
||||||
|
function binOpSync(opName, argument = 0, ...zeroCopy) {
|
||||||
|
return opSync(
|
||||||
|
opName,
|
||||||
|
() => binOpBuildRequest(0, argument, zeroCopy),
|
||||||
|
binOpParseResult,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function jsonOpAsync(opName, args = null, ...zeroCopy) {
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
setAsyncHandler(opsCache[opName], jsonOpAsyncHandler);
|
///////////////////////////////////// Json ops handling ////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const promiseId = nextPromiseId++;
|
const jsonRequestHeaderLength = 8;
|
||||||
const reqBuf = core.encode("\0".repeat(8) + JSON.stringify(args));
|
|
||||||
new DataView(reqBuf.buffer).setBigUint64(0, BigInt(promiseId));
|
function jsonOpBuildRequest(requestId, argument, zeroCopy) {
|
||||||
dispatch(opName, reqBuf, ...zeroCopy);
|
const u8Array = core.encode(
|
||||||
let resolve, reject;
|
"\0".repeat(jsonRequestHeaderLength) + JSON.stringify(argument),
|
||||||
const promise = new Promise((resolve_, reject_) => {
|
);
|
||||||
resolve = resolve_;
|
new DataView(u8Array.buffer).setBigUint64(0, BigInt(requestId), true);
|
||||||
reject = reject_;
|
return [u8Array, ...zeroCopy];
|
||||||
});
|
|
||||||
promise.resolve = resolve;
|
|
||||||
promise.reject = reject;
|
|
||||||
promiseTable[promiseId] = promise;
|
|
||||||
return processResponse(await promise);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function jsonOpSync(opName, args = null, ...zeroCopy) {
|
function jsonOpParseResult(u8Array, _) {
|
||||||
const argsBuf = encodeJson(args);
|
const data = JSON.parse(core.decode(u8Array));
|
||||||
const res = dispatch(opName, argsBuf, ...zeroCopy);
|
|
||||||
return processResponse(decodeJson(res));
|
if ("err" in data) {
|
||||||
|
return [
|
||||||
|
data.requestId,
|
||||||
|
null,
|
||||||
|
handleError(data.err.className, data.err.message),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function jsonOpAsyncHandler(buf) {
|
return [data.requestId, data.ok, null];
|
||||||
// Json Op.
|
}
|
||||||
const res = decodeJson(buf);
|
|
||||||
const promise = promiseTable[res.promiseId];
|
function jsonOpAsync(opName, argument = null, ...zeroCopy) {
|
||||||
delete promiseTable[res.promiseId];
|
return opAsync(
|
||||||
promise.resolve(res);
|
opName,
|
||||||
|
(requestId) => jsonOpBuildRequest(requestId, argument, zeroCopy),
|
||||||
|
jsonOpParseResult,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function jsonOpSync(opName, argument = null, ...zeroCopy) {
|
||||||
|
return opSync(
|
||||||
|
opName,
|
||||||
|
() => [core.encode(JSON.stringify(argument)), ...zeroCopy],
|
||||||
|
jsonOpParseResult,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resources() {
|
function resources() {
|
||||||
return jsonOpSync("op_resources");
|
return jsonOpSync("op_resources");
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(rid) {
|
function close(rid) {
|
||||||
jsonOpSync("op_close", { rid });
|
return jsonOpSync("op_close", { rid });
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(window.Deno.core, {
|
Object.assign(window.Deno.core, {
|
||||||
jsonOpAsync,
|
jsonOpAsync,
|
||||||
jsonOpSync,
|
jsonOpSync,
|
||||||
setAsyncHandler,
|
binOpAsync,
|
||||||
setAsyncHandlerByName,
|
binOpSync,
|
||||||
dispatch: send,
|
dispatch,
|
||||||
dispatchByName: dispatch,
|
dispatchByName,
|
||||||
ops,
|
ops,
|
||||||
close,
|
close,
|
||||||
resources,
|
resources,
|
||||||
registerErrorClass,
|
registerErrorClass,
|
||||||
getErrorClassAndArgs,
|
|
||||||
sharedQueueInit: init,
|
sharedQueueInit: init,
|
||||||
// sharedQueue is private but exposed for testing.
|
// sharedQueue is private but exposed for testing.
|
||||||
sharedQueue: {
|
sharedQueue: {
|
||||||
|
@ -279,5 +405,7 @@ SharedQueue Binary Layout
|
||||||
reset,
|
reset,
|
||||||
shift,
|
shift,
|
||||||
},
|
},
|
||||||
|
// setAsyncHandler is private but exposed for testing.
|
||||||
|
setAsyncHandler,
|
||||||
});
|
});
|
||||||
})(this);
|
})(this);
|
||||||
|
|
|
@ -8,85 +8,15 @@ const responseBuf = new Uint8Array(
|
||||||
.split("")
|
.split("")
|
||||||
.map((c) => c.charCodeAt(0)),
|
.map((c) => c.charCodeAt(0)),
|
||||||
);
|
);
|
||||||
const promiseMap = new Map();
|
|
||||||
let nextPromiseId = 1;
|
|
||||||
|
|
||||||
function assert(cond) {
|
|
||||||
if (!cond) {
|
|
||||||
throw Error("assert");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createResolvable() {
|
|
||||||
let resolve;
|
|
||||||
let reject;
|
|
||||||
const promise = new Promise((res, rej) => {
|
|
||||||
resolve = res;
|
|
||||||
reject = rej;
|
|
||||||
});
|
|
||||||
promise.resolve = resolve;
|
|
||||||
promise.reject = reject;
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scratch32 = new Int32Array(3);
|
|
||||||
const scratchBytes = new Uint8Array(
|
|
||||||
scratch32.buffer,
|
|
||||||
scratch32.byteOffset,
|
|
||||||
scratch32.byteLength,
|
|
||||||
);
|
|
||||||
assert(scratchBytes.byteLength === 3 * 4);
|
|
||||||
|
|
||||||
function send(promiseId, opId, rid, ...zeroCopy) {
|
|
||||||
scratch32[0] = promiseId;
|
|
||||||
scratch32[1] = rid;
|
|
||||||
scratch32[2] = -1;
|
|
||||||
return Deno.core.dispatch(opId, scratchBytes, ...zeroCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns Promise<number> */
|
|
||||||
function sendAsync(opId, rid, ...zeroCopy) {
|
|
||||||
const promiseId = nextPromiseId++;
|
|
||||||
const p = createResolvable();
|
|
||||||
const buf = send(promiseId, opId, rid, ...zeroCopy);
|
|
||||||
if (buf) {
|
|
||||||
const record = recordFromBuf(buf);
|
|
||||||
// Sync result.
|
|
||||||
p.resolve(record.result);
|
|
||||||
} else {
|
|
||||||
// Async result.
|
|
||||||
promiseMap.set(promiseId, p);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns i32 number */
|
|
||||||
function sendSync(opId, rid) {
|
|
||||||
const buf = send(0, opId, rid);
|
|
||||||
const record = recordFromBuf(buf);
|
|
||||||
return record[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
function recordFromBuf(buf) {
|
|
||||||
assert(buf.byteLength === 3 * 4);
|
|
||||||
return new Int32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAsyncMsgFromRust(buf) {
|
|
||||||
const record = recordFromBuf(buf);
|
|
||||||
const p = promiseMap.get(record[0]);
|
|
||||||
promiseMap.delete(record[0]);
|
|
||||||
p.resolve(record[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Listens on 0.0.0.0:4500, returns rid. */
|
/** Listens on 0.0.0.0:4500, returns rid. */
|
||||||
function listen() {
|
function listen() {
|
||||||
return sendSync(ops["listen"], -1);
|
return Deno.core.binOpSync("listen");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Accepts a connection, returns rid. */
|
/** Accepts a connection, returns rid. */
|
||||||
function accept(rid) {
|
function accept(rid) {
|
||||||
return sendAsync(ops["accept"], rid);
|
return Deno.core.binOpAsync("accept", rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,16 +24,16 @@ function accept(rid) {
|
||||||
* Returns bytes read.
|
* Returns bytes read.
|
||||||
*/
|
*/
|
||||||
function read(rid, data) {
|
function read(rid, data) {
|
||||||
return sendAsync(ops["read"], rid, data);
|
return Deno.core.binOpAsync("read", rid, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Writes a fixed HTTP response to the socket rid. Returns bytes written. */
|
/** Writes a fixed HTTP response to the socket rid. Returns bytes written. */
|
||||||
function write(rid, data) {
|
function write(rid, data) {
|
||||||
return sendAsync(ops["write"], rid, data);
|
return Deno.core.binOpAsync("write", rid, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(rid) {
|
function close(rid) {
|
||||||
return sendSync(ops["close"], rid);
|
Deno.core.binOpSync("close", rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function serve(rid) {
|
async function serve(rid) {
|
||||||
|
@ -121,16 +51,14 @@ async function serve(rid) {
|
||||||
close(rid);
|
close(rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ops;
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
ops = Deno.core.ops();
|
Deno.core.ops();
|
||||||
for (const opName in ops) {
|
Deno.core.registerErrorClass("Error", Error);
|
||||||
Deno.core.setAsyncHandler(ops[opName], handleAsyncMsgFromRust);
|
|
||||||
}
|
|
||||||
|
|
||||||
const listenerRid = listen();
|
const listenerRid = listen();
|
||||||
Deno.core.print(`http_bench_bin_ops listening on http://127.0.0.1:4544/\n`);
|
Deno.core.print(
|
||||||
|
`http_bench_bin_ops listening on http://127.0.0.1:4544/\n`,
|
||||||
|
);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const rid = await accept(listenerRid);
|
const rid = await accept(listenerRid);
|
||||||
|
|
|
@ -3,30 +3,23 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
use deno_core::error::bad_resource_id;
|
||||||
|
use deno_core::error::AnyError;
|
||||||
use deno_core::AsyncRefCell;
|
use deno_core::AsyncRefCell;
|
||||||
use deno_core::BufVec;
|
use deno_core::BufVec;
|
||||||
use deno_core::CancelHandle;
|
use deno_core::CancelHandle;
|
||||||
use deno_core::CancelTryFuture;
|
use deno_core::CancelTryFuture;
|
||||||
use deno_core::JsRuntime;
|
use deno_core::JsRuntime;
|
||||||
use deno_core::Op;
|
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
use deno_core::RcRef;
|
use deno_core::RcRef;
|
||||||
use deno_core::Resource;
|
use deno_core::Resource;
|
||||||
use deno_core::ResourceId;
|
use deno_core::ResourceId;
|
||||||
use deno_core::ZeroCopyBuf;
|
use deno_core::ZeroCopyBuf;
|
||||||
use futures::future::FutureExt;
|
|
||||||
use futures::future::TryFuture;
|
|
||||||
use futures::future::TryFutureExt;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::io::Error;
|
use std::io::Error;
|
||||||
use std::io::ErrorKind;
|
|
||||||
use std::mem::size_of;
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::ptr;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
@ -120,52 +113,21 @@ impl From<tokio::net::TcpStream> for TcpStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
struct Record {
|
|
||||||
promise_id: u32,
|
|
||||||
rid: ResourceId,
|
|
||||||
result: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
type RecordBuf = [u8; size_of::<Record>()];
|
|
||||||
|
|
||||||
impl From<&[u8]> for Record {
|
|
||||||
fn from(buf: &[u8]) -> Self {
|
|
||||||
assert_eq!(buf.len(), size_of::<RecordBuf>());
|
|
||||||
unsafe { *(buf as *const _ as *const RecordBuf) }.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RecordBuf> for Record {
|
|
||||||
fn from(buf: RecordBuf) -> Self {
|
|
||||||
unsafe {
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
ptr::read_unaligned(&buf as *const _ as *const Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Record> for RecordBuf {
|
|
||||||
fn from(record: Record) -> Self {
|
|
||||||
unsafe { ptr::read(&record as *const _ as *const Self) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_js_runtime() -> JsRuntime {
|
fn create_js_runtime() -> JsRuntime {
|
||||||
let mut js_runtime = JsRuntime::new(Default::default());
|
let mut runtime = JsRuntime::new(Default::default());
|
||||||
register_op_bin_sync(&mut js_runtime, "listen", op_listen);
|
runtime.register_op("listen", deno_core::bin_op_sync(op_listen));
|
||||||
register_op_bin_sync(&mut js_runtime, "close", op_close);
|
runtime.register_op("close", deno_core::bin_op_sync(op_close));
|
||||||
register_op_bin_async(&mut js_runtime, "accept", op_accept);
|
runtime.register_op("accept", deno_core::bin_op_async(op_accept));
|
||||||
register_op_bin_async(&mut js_runtime, "read", op_read);
|
runtime.register_op("read", deno_core::bin_op_async(op_read));
|
||||||
register_op_bin_async(&mut js_runtime, "write", op_write);
|
runtime.register_op("write", deno_core::bin_op_async(op_write));
|
||||||
js_runtime
|
runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_listen(
|
fn op_listen(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
_rid: ResourceId,
|
_rid: ResourceId,
|
||||||
_bufs: &mut [ZeroCopyBuf],
|
_bufs: &mut [ZeroCopyBuf],
|
||||||
) -> Result<u32, Error> {
|
) -> Result<u32, AnyError> {
|
||||||
debug!("listen");
|
debug!("listen");
|
||||||
let addr = "127.0.0.1:4544".parse::<SocketAddr>().unwrap();
|
let addr = "127.0.0.1:4544".parse::<SocketAddr>().unwrap();
|
||||||
let std_listener = std::net::TcpListener::bind(&addr)?;
|
let std_listener = std::net::TcpListener::bind(&addr)?;
|
||||||
|
@ -179,7 +141,7 @@ fn op_close(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
rid: ResourceId,
|
rid: ResourceId,
|
||||||
_bufs: &mut [ZeroCopyBuf],
|
_bufs: &mut [ZeroCopyBuf],
|
||||||
) -> Result<u32, Error> {
|
) -> Result<u32, AnyError> {
|
||||||
debug!("close rid={}", rid);
|
debug!("close rid={}", rid);
|
||||||
state
|
state
|
||||||
.resource_table
|
.resource_table
|
||||||
|
@ -192,7 +154,7 @@ async fn op_accept(
|
||||||
state: Rc<RefCell<OpState>>,
|
state: Rc<RefCell<OpState>>,
|
||||||
rid: ResourceId,
|
rid: ResourceId,
|
||||||
_bufs: BufVec,
|
_bufs: BufVec,
|
||||||
) -> Result<u32, Error> {
|
) -> Result<u32, AnyError> {
|
||||||
debug!("accept rid={}", rid);
|
debug!("accept rid={}", rid);
|
||||||
|
|
||||||
let listener = state
|
let listener = state
|
||||||
|
@ -209,7 +171,7 @@ async fn op_read(
|
||||||
state: Rc<RefCell<OpState>>,
|
state: Rc<RefCell<OpState>>,
|
||||||
rid: ResourceId,
|
rid: ResourceId,
|
||||||
mut bufs: BufVec,
|
mut bufs: BufVec,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<u32, AnyError> {
|
||||||
assert_eq!(bufs.len(), 1, "Invalid number of arguments");
|
assert_eq!(bufs.len(), 1, "Invalid number of arguments");
|
||||||
debug!("read rid={}", rid);
|
debug!("read rid={}", rid);
|
||||||
|
|
||||||
|
@ -218,14 +180,15 @@ async fn op_read(
|
||||||
.resource_table
|
.resource_table
|
||||||
.get::<TcpStream>(rid)
|
.get::<TcpStream>(rid)
|
||||||
.ok_or_else(bad_resource_id)?;
|
.ok_or_else(bad_resource_id)?;
|
||||||
stream.read(&mut bufs[0]).await
|
let nread = stream.read(&mut bufs[0]).await?;
|
||||||
|
Ok(nread as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn op_write(
|
async fn op_write(
|
||||||
state: Rc<RefCell<OpState>>,
|
state: Rc<RefCell<OpState>>,
|
||||||
rid: ResourceId,
|
rid: ResourceId,
|
||||||
bufs: BufVec,
|
bufs: BufVec,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<u32, AnyError> {
|
||||||
assert_eq!(bufs.len(), 1, "Invalid number of arguments");
|
assert_eq!(bufs.len(), 1, "Invalid number of arguments");
|
||||||
debug!("write rid={}", rid);
|
debug!("write rid={}", rid);
|
||||||
|
|
||||||
|
@ -234,70 +197,8 @@ async fn op_write(
|
||||||
.resource_table
|
.resource_table
|
||||||
.get::<TcpStream>(rid)
|
.get::<TcpStream>(rid)
|
||||||
.ok_or_else(bad_resource_id)?;
|
.ok_or_else(bad_resource_id)?;
|
||||||
stream.write(&bufs[0]).await
|
let nwritten = stream.write(&bufs[0]).await?;
|
||||||
}
|
Ok(nwritten as u32)
|
||||||
|
|
||||||
fn register_op_bin_sync<F>(
|
|
||||||
js_runtime: &mut JsRuntime,
|
|
||||||
name: &'static str,
|
|
||||||
op_fn: F,
|
|
||||||
) where
|
|
||||||
F: Fn(&mut OpState, u32, &mut [ZeroCopyBuf]) -> Result<u32, Error> + 'static,
|
|
||||||
{
|
|
||||||
let base_op_fn = move |state: Rc<RefCell<OpState>>, mut bufs: BufVec| -> Op {
|
|
||||||
let record = Record::from(bufs[0].as_ref());
|
|
||||||
let is_sync = record.promise_id == 0;
|
|
||||||
assert!(is_sync);
|
|
||||||
|
|
||||||
let zero_copy_bufs = &mut bufs[1..];
|
|
||||||
let result: i32 =
|
|
||||||
match op_fn(&mut state.borrow_mut(), record.rid, zero_copy_bufs) {
|
|
||||||
Ok(r) => r as i32,
|
|
||||||
Err(_) => -1,
|
|
||||||
};
|
|
||||||
let buf = RecordBuf::from(Record { result, ..record })[..].into();
|
|
||||||
Op::Sync(buf)
|
|
||||||
};
|
|
||||||
|
|
||||||
js_runtime.register_op(name, base_op_fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_op_bin_async<F, R>(
|
|
||||||
js_runtime: &mut JsRuntime,
|
|
||||||
name: &'static str,
|
|
||||||
op_fn: F,
|
|
||||||
) where
|
|
||||||
F: Fn(Rc<RefCell<OpState>>, u32, BufVec) -> R + Copy + 'static,
|
|
||||||
R: TryFuture,
|
|
||||||
R::Ok: TryInto<i32>,
|
|
||||||
<R::Ok as TryInto<i32>>::Error: Debug,
|
|
||||||
{
|
|
||||||
let base_op_fn = move |state: Rc<RefCell<OpState>>, bufs: BufVec| -> Op {
|
|
||||||
let mut bufs_iter = bufs.into_iter();
|
|
||||||
let record_buf = bufs_iter.next().unwrap();
|
|
||||||
let zero_copy_bufs = bufs_iter.collect::<BufVec>();
|
|
||||||
|
|
||||||
let record = Record::from(record_buf.as_ref());
|
|
||||||
let is_sync = record.promise_id == 0;
|
|
||||||
assert!(!is_sync);
|
|
||||||
|
|
||||||
let fut = async move {
|
|
||||||
let op = op_fn(state, record.rid, zero_copy_bufs);
|
|
||||||
let result = op
|
|
||||||
.map_ok(|r| r.try_into().expect("op result does not fit in i32"))
|
|
||||||
.unwrap_or_else(|_| -1)
|
|
||||||
.await;
|
|
||||||
RecordBuf::from(Record { result, ..record })[..].into()
|
|
||||||
};
|
|
||||||
|
|
||||||
Op::Async(fut.boxed_local())
|
|
||||||
};
|
|
||||||
|
|
||||||
js_runtime.register_op(name, base_op_fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bad_resource_id() -> Error {
|
|
||||||
Error::new(ErrorKind::NotFound, "bad resource id")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -329,18 +230,3 @@ fn main() {
|
||||||
};
|
};
|
||||||
runtime.block_on(future).unwrap();
|
runtime.block_on(future).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_record_from() {
|
|
||||||
let expected = Record {
|
|
||||||
promise_id: 1,
|
|
||||||
rid: 3,
|
|
||||||
result: 4,
|
|
||||||
};
|
|
||||||
let buf = RecordBuf::from(expected);
|
|
||||||
if cfg!(target_endian = "little") {
|
|
||||||
assert_eq!(buf, [1u8, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0]);
|
|
||||||
}
|
|
||||||
let actual = Record::from(buf);
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ mod module_specifier;
|
||||||
mod modules;
|
mod modules;
|
||||||
mod normalize_path;
|
mod normalize_path;
|
||||||
mod ops;
|
mod ops;
|
||||||
|
mod ops_bin;
|
||||||
|
mod ops_json;
|
||||||
pub mod plugin_api;
|
pub mod plugin_api;
|
||||||
mod resources;
|
mod resources;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
|
@ -58,8 +60,6 @@ pub use crate::modules::ModuleSourceFuture;
|
||||||
pub use crate::modules::NoopModuleLoader;
|
pub use crate::modules::NoopModuleLoader;
|
||||||
pub use crate::modules::RecursiveModuleLoad;
|
pub use crate::modules::RecursiveModuleLoad;
|
||||||
pub use crate::normalize_path::normalize_path;
|
pub use crate::normalize_path::normalize_path;
|
||||||
pub use crate::ops::json_op_async;
|
|
||||||
pub use crate::ops::json_op_sync;
|
|
||||||
pub use crate::ops::op_close;
|
pub use crate::ops::op_close;
|
||||||
pub use crate::ops::op_resources;
|
pub use crate::ops::op_resources;
|
||||||
pub use crate::ops::Op;
|
pub use crate::ops::Op;
|
||||||
|
@ -68,6 +68,11 @@ pub use crate::ops::OpFn;
|
||||||
pub use crate::ops::OpId;
|
pub use crate::ops::OpId;
|
||||||
pub use crate::ops::OpState;
|
pub use crate::ops::OpState;
|
||||||
pub use crate::ops::OpTable;
|
pub use crate::ops::OpTable;
|
||||||
|
pub use crate::ops_bin::bin_op_async;
|
||||||
|
pub use crate::ops_bin::bin_op_sync;
|
||||||
|
pub use crate::ops_bin::ValueOrVector;
|
||||||
|
pub use crate::ops_json::json_op_async;
|
||||||
|
pub use crate::ops_json::json_op_sync;
|
||||||
pub use crate::resources::Resource;
|
pub use crate::resources::Resource;
|
||||||
pub use crate::resources::ResourceId;
|
pub use crate::resources::ResourceId;
|
||||||
pub use crate::resources::ResourceTable;
|
pub use crate::resources::ResourceTable;
|
||||||
|
|
122
core/ops.rs
122
core/ops.rs
|
@ -10,13 +10,10 @@ use crate::BufVec;
|
||||||
use crate::ZeroCopyBuf;
|
use crate::ZeroCopyBuf;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
@ -117,125 +114,6 @@ impl Default for OpTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an op that passes data synchronously using JSON.
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
/// * `V`: the deserializable value that is passed to the Rust function.
|
|
||||||
/// * `&mut [ZeroCopyBuf]`: raw bytes passed along, usually not needed if the JSON value is used.
|
|
||||||
///
|
|
||||||
/// `op_fn` returns a serializable 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::json_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.jsonOpSync("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 json_op_sync<F, V, R>(op_fn: F) -> Box<OpFn>
|
|
||||||
where
|
|
||||||
F: Fn(&mut OpState, V, &mut [ZeroCopyBuf]) -> Result<R, AnyError> + 'static,
|
|
||||||
V: DeserializeOwned,
|
|
||||||
R: Serialize,
|
|
||||||
{
|
|
||||||
Box::new(move |state: Rc<RefCell<OpState>>, mut bufs: BufVec| -> Op {
|
|
||||||
let result = serde_json::from_slice(&bufs[0])
|
|
||||||
.map_err(AnyError::from)
|
|
||||||
.and_then(|args| op_fn(&mut state.borrow_mut(), args, &mut bufs[1..]));
|
|
||||||
let buf =
|
|
||||||
json_serialize_op_result(None, result, state.borrow().get_error_class_fn);
|
|
||||||
Op::Sync(buf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an op that passes data asynchronously using JSON.
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
/// * `V`: the deserializable 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 serializable 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 json_op_async<F, V, R, RV>(op_fn: F) -> Box<OpFn>
|
|
||||||
where
|
|
||||||
F: Fn(Rc<RefCell<OpState>>, V, BufVec) -> R + 'static,
|
|
||||||
V: DeserializeOwned,
|
|
||||||
R: Future<Output = Result<RV, AnyError>> + 'static,
|
|
||||||
RV: Serialize,
|
|
||||||
{
|
|
||||||
let try_dispatch_op =
|
|
||||||
move |state: Rc<RefCell<OpState>>, bufs: BufVec| -> Result<Op, AnyError> {
|
|
||||||
let promise_id = bufs[0]
|
|
||||||
.get(0..8)
|
|
||||||
.map(|b| u64::from_be_bytes(b.try_into().unwrap()))
|
|
||||||
.ok_or_else(|| type_error("missing or invalid `promiseId`"))?;
|
|
||||||
let args = serde_json::from_slice(&bufs[0][8..])?;
|
|
||||||
let bufs = bufs[1..].into();
|
|
||||||
use crate::futures::FutureExt;
|
|
||||||
let fut = op_fn(state.clone(), args, bufs).map(move |result| {
|
|
||||||
json_serialize_op_result(
|
|
||||||
Some(promise_id),
|
|
||||||
result,
|
|
||||||
state.borrow().get_error_class_fn,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
Ok(Op::Async(Box::pin(fut)))
|
|
||||||
};
|
|
||||||
|
|
||||||
Box::new(move |state: Rc<RefCell<OpState>>, bufs: BufVec| -> Op {
|
|
||||||
match try_dispatch_op(state.clone(), bufs) {
|
|
||||||
Ok(op) => op,
|
|
||||||
Err(err) => Op::Sync(json_serialize_op_result(
|
|
||||||
None,
|
|
||||||
Err::<(), AnyError>(err),
|
|
||||||
state.borrow().get_error_class_fn,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn json_serialize_op_result<R: Serialize>(
|
|
||||||
promise_id: Option<u64>,
|
|
||||||
result: Result<R, AnyError>,
|
|
||||||
get_error_class_fn: crate::runtime::GetErrorClassFn,
|
|
||||||
) -> Box<[u8]> {
|
|
||||||
let value = match result {
|
|
||||||
Ok(v) => serde_json::json!({ "ok": v, "promiseId": promise_id }),
|
|
||||||
Err(err) => serde_json::json!({
|
|
||||||
"promiseId": promise_id ,
|
|
||||||
"err": {
|
|
||||||
"className": (get_error_class_fn)(&err),
|
|
||||||
"message": err.to_string(),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
serde_json::to_vec(&value).unwrap().into_boxed_slice()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return map of resources with id as key
|
/// Return map of resources with id as key
|
||||||
/// and string representation as value.
|
/// and string representation as value.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use deno_core::error::AnyError;
|
use crate::error::AnyError;
|
||||||
use deno_core::futures::future::FutureExt;
|
use crate::futures::future::FutureExt;
|
||||||
use deno_core::BufVec;
|
use crate::BufVec;
|
||||||
use deno_core::Op;
|
use crate::Op;
|
||||||
use deno_core::OpFn;
|
use crate::OpFn;
|
||||||
use deno_core::OpState;
|
use crate::OpState;
|
||||||
use deno_core::ZeroCopyBuf;
|
use crate::ZeroCopyBuf;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
@ -88,18 +88,18 @@ fn gen_padding_32bit(len: usize) -> &'static [u8] {
|
||||||
/// When registering an op like this...
|
/// When registering an op like this...
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// let mut runtime = JsRuntime::new(...);
|
/// let mut runtime = JsRuntime::new(...);
|
||||||
/// runtime.register_op("hello", deno_core::buffer_op_sync(Self::hello_op));
|
/// runtime.register_op("hello", deno_core::bin_op_sync(Self::hello_op));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ...it can be invoked from JS using the provided name, for example:
|
/// ...it can be invoked from JS using the provided name, for example:
|
||||||
/// ```js
|
/// ```js
|
||||||
/// Deno.core.ops();
|
/// Deno.core.ops();
|
||||||
/// let result = Deno.core.bufferOpSync("function_name", args);
|
/// let result = Deno.core.binOpSync("function_name", args);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The `Deno.core.ops()` statement is needed once before any op calls, for initialization.
|
/// The `Deno.core.ops()` statement is needed once before any op calls, for initialization.
|
||||||
/// A more complete example is available in the examples directory.
|
/// A more complete example is available in the examples directory.
|
||||||
pub fn buffer_op_sync<F, R>(op_fn: F) -> Box<OpFn>
|
pub fn bin_op_sync<F, R>(op_fn: F) -> Box<OpFn>
|
||||||
where
|
where
|
||||||
F: Fn(&mut OpState, u32, &mut [ZeroCopyBuf]) -> Result<R, AnyError> + 'static,
|
F: Fn(&mut OpState, u32, &mut [ZeroCopyBuf]) -> Result<R, AnyError> + 'static,
|
||||||
R: ValueOrVector,
|
R: ValueOrVector,
|
||||||
|
@ -202,7 +202,7 @@ where
|
||||||
///
|
///
|
||||||
/// The `Deno.core.ops()` statement is needed once before any op calls, for initialization.
|
/// The `Deno.core.ops()` statement is needed once before any op calls, for initialization.
|
||||||
/// A more complete example is available in the examples directory.
|
/// A more complete example is available in the examples directory.
|
||||||
pub fn buffer_op_async<F, R, RV>(op_fn: F) -> Box<OpFn>
|
pub fn bin_op_async<F, R, RV>(op_fn: F) -> Box<OpFn>
|
||||||
where
|
where
|
||||||
F: Fn(Rc<RefCell<OpState>>, u32, BufVec) -> R + 'static,
|
F: Fn(Rc<RefCell<OpState>>, u32, BufVec) -> R + 'static,
|
||||||
R: Future<Output = Result<RV, AnyError>> + 'static,
|
R: Future<Output = Result<RV, AnyError>> + 'static,
|
134
core/ops_json.rs
Normal file
134
core/ops_json.rs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use crate::error::type_error;
|
||||||
|
use crate::error::AnyError;
|
||||||
|
use crate::BufVec;
|
||||||
|
use crate::Op;
|
||||||
|
use crate::OpFn;
|
||||||
|
use crate::OpState;
|
||||||
|
use crate::ZeroCopyBuf;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
fn json_serialize_op_result<R: Serialize>(
|
||||||
|
request_id: Option<u64>,
|
||||||
|
result: Result<R, AnyError>,
|
||||||
|
get_error_class_fn: crate::runtime::GetErrorClassFn,
|
||||||
|
) -> Box<[u8]> {
|
||||||
|
let value = match result {
|
||||||
|
Ok(v) => serde_json::json!({ "ok": v, "requestId": request_id }),
|
||||||
|
Err(err) => serde_json::json!({
|
||||||
|
"requestId": request_id,
|
||||||
|
"err": {
|
||||||
|
"className": (get_error_class_fn)(&err),
|
||||||
|
"message": err.to_string(),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
serde_json::to_vec(&value).unwrap().into_boxed_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an op that passes data synchronously using JSON.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
/// * `V`: the deserializable value that is passed to the Rust function.
|
||||||
|
/// * `&mut [ZeroCopyBuf]`: raw bytes passed along, usually not needed if the JSON value is used.
|
||||||
|
///
|
||||||
|
/// `op_fn` returns a serializable 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::json_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.jsonOpSync("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 json_op_sync<F, V, R>(op_fn: F) -> Box<OpFn>
|
||||||
|
where
|
||||||
|
F: Fn(&mut OpState, V, &mut [ZeroCopyBuf]) -> Result<R, AnyError> + 'static,
|
||||||
|
V: DeserializeOwned,
|
||||||
|
R: Serialize,
|
||||||
|
{
|
||||||
|
Box::new(move |state: Rc<RefCell<OpState>>, mut bufs: BufVec| -> Op {
|
||||||
|
let result = serde_json::from_slice(&bufs[0])
|
||||||
|
.map_err(AnyError::from)
|
||||||
|
.and_then(|args| op_fn(&mut state.borrow_mut(), args, &mut bufs[1..]));
|
||||||
|
let buf =
|
||||||
|
json_serialize_op_result(None, result, state.borrow().get_error_class_fn);
|
||||||
|
Op::Sync(buf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an op that passes data asynchronously using JSON.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
/// * `V`: the deserializable 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 serializable 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 json_op_async<F, V, R, RV>(op_fn: F) -> Box<OpFn>
|
||||||
|
where
|
||||||
|
F: Fn(Rc<RefCell<OpState>>, V, BufVec) -> R + 'static,
|
||||||
|
V: DeserializeOwned,
|
||||||
|
R: Future<Output = Result<RV, AnyError>> + 'static,
|
||||||
|
RV: Serialize,
|
||||||
|
{
|
||||||
|
let try_dispatch_op =
|
||||||
|
move |state: Rc<RefCell<OpState>>, bufs: BufVec| -> Result<Op, AnyError> {
|
||||||
|
let request_id = bufs[0]
|
||||||
|
.get(0..8)
|
||||||
|
.map(|b| u64::from_le_bytes(b.try_into().unwrap()))
|
||||||
|
.ok_or_else(|| type_error("missing or invalid `requestId`"))?;
|
||||||
|
let args = serde_json::from_slice(&bufs[0][8..])?;
|
||||||
|
let bufs = bufs[1..].into();
|
||||||
|
use crate::futures::FutureExt;
|
||||||
|
let fut = op_fn(state.clone(), args, bufs).map(move |result| {
|
||||||
|
json_serialize_op_result(
|
||||||
|
Some(request_id),
|
||||||
|
result,
|
||||||
|
state.borrow().get_error_class_fn,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
Ok(Op::Async(Box::pin(fut)))
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::new(move |state: Rc<RefCell<OpState>>, bufs: BufVec| -> Op {
|
||||||
|
match try_dispatch_op(state.clone(), bufs) {
|
||||||
|
Ok(op) => op,
|
||||||
|
Err(err) => Op::Sync(json_serialize_op_result(
|
||||||
|
None,
|
||||||
|
Err::<(), AnyError>(err),
|
||||||
|
state.borrow().get_error_class_fn,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -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);
|
|
|
@ -4,7 +4,6 @@
|
||||||
((window) => {
|
((window) => {
|
||||||
const assert = window.__bootstrap.util.assert;
|
const assert = window.__bootstrap.util.assert;
|
||||||
const core = window.Deno.core;
|
const core = window.Deno.core;
|
||||||
const { bufferOpSync } = window.__bootstrap.dispatchBuffer;
|
|
||||||
|
|
||||||
function opStopGlobalTimer() {
|
function opStopGlobalTimer() {
|
||||||
core.jsonOpSync("op_global_timer_stop");
|
core.jsonOpSync("op_global_timer_stop");
|
||||||
|
@ -20,7 +19,7 @@
|
||||||
|
|
||||||
const nowBytes = new Uint8Array(8);
|
const nowBytes = new Uint8Array(8);
|
||||||
function opNow() {
|
function opNow() {
|
||||||
bufferOpSync("op_now", 0, nowBytes);
|
core.binOpSync("op_now", 0, nowBytes);
|
||||||
return new DataView(nowBytes.buffer).getFloat64();
|
return new DataView(nowBytes.buffer).getFloat64();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
((window) => {
|
((window) => {
|
||||||
|
const core = window.Deno.core;
|
||||||
const DEFAULT_BUFFER_SIZE = 32 * 1024;
|
const DEFAULT_BUFFER_SIZE = 32 * 1024;
|
||||||
const { bufferOpSync, bufferOpAsync } = window.__bootstrap.dispatchBuffer;
|
|
||||||
// Seek whence values.
|
// Seek whence values.
|
||||||
// https://golang.org/pkg/io/#pkg-constants
|
// https://golang.org/pkg/io/#pkg-constants
|
||||||
const SeekMode = {
|
const SeekMode = {
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nread = bufferOpSync("op_read_sync", rid, buffer);
|
const nread = core.binOpSync("op_read_sync", rid, buffer);
|
||||||
if (nread < 0) {
|
if (nread < 0) {
|
||||||
throw new Error("read error");
|
throw new Error("read error");
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nread = await bufferOpAsync("op_read_async", rid, buffer);
|
const nread = await core.binOpAsync("op_read_async", rid, buffer);
|
||||||
if (nread < 0) {
|
if (nread < 0) {
|
||||||
throw new Error("read error");
|
throw new Error("read error");
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeSync(rid, data) {
|
function writeSync(rid, data) {
|
||||||
const result = bufferOpSync("op_write_sync", rid, data);
|
const result = core.binOpSync("op_write_sync", rid, data);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
throw new Error("write error");
|
throw new Error("write error");
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function write(rid, data) {
|
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) {
|
if (result < 0) {
|
||||||
throw new Error("write error");
|
throw new Error("write error");
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,11 +99,11 @@ lazy_static! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(rt: &mut JsRuntime) {
|
pub fn init(rt: &mut JsRuntime) {
|
||||||
super::reg_buffer_async(rt, "op_read_async", op_read_async);
|
super::reg_bin_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_write_async", op_write_async);
|
||||||
|
|
||||||
super::reg_buffer_sync(rt, "op_read_sync", op_read_sync);
|
super::reg_bin_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_write_sync", op_write_sync);
|
||||||
|
|
||||||
super::reg_json_async(rt, "op_shutdown", op_shutdown);
|
super::reg_json_async(rt, "op_shutdown", op_shutdown);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ pub mod io;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod net_unix;
|
mod net_unix;
|
||||||
mod ops_buffer;
|
|
||||||
pub mod os;
|
pub mod os;
|
||||||
pub mod permissions;
|
pub mod permissions;
|
||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
|
@ -25,6 +24,8 @@ pub mod websocket;
|
||||||
pub mod worker_host;
|
pub mod worker_host;
|
||||||
|
|
||||||
use crate::metrics::metrics_op;
|
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::error::AnyError;
|
||||||
use deno_core::json_op_async;
|
use deno_core::json_op_async;
|
||||||
use deno_core::json_op_sync;
|
use deno_core::json_op_sync;
|
||||||
|
@ -33,10 +34,8 @@ use deno_core::serde::Serialize;
|
||||||
use deno_core::BufVec;
|
use deno_core::BufVec;
|
||||||
use deno_core::JsRuntime;
|
use deno_core::JsRuntime;
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
|
use deno_core::ValueOrVector;
|
||||||
use deno_core::ZeroCopyBuf;
|
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::cell::RefCell;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -63,24 +62,21 @@ where
|
||||||
rt.register_op(name, metrics_op(name, json_op_sync(op_fn)));
|
rt.register_op(name, metrics_op(name, json_op_sync(op_fn)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reg_buffer_async<F, R, RV>(
|
pub fn reg_bin_async<F, R, RV>(rt: &mut JsRuntime, name: &'static str, op_fn: F)
|
||||||
rt: &mut JsRuntime,
|
where
|
||||||
name: &'static str,
|
|
||||||
op_fn: F,
|
|
||||||
) where
|
|
||||||
F: Fn(Rc<RefCell<OpState>>, u32, BufVec) -> R + 'static,
|
F: Fn(Rc<RefCell<OpState>>, u32, BufVec) -> R + 'static,
|
||||||
R: Future<Output = Result<RV, AnyError>> + 'static,
|
R: Future<Output = Result<RV, AnyError>> + 'static,
|
||||||
RV: ValueOrVector,
|
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
|
where
|
||||||
F: Fn(&mut OpState, u32, &mut [ZeroCopyBuf]) -> Result<R, AnyError> + 'static,
|
F: Fn(&mut OpState, u32, &mut [ZeroCopyBuf]) -> Result<R, AnyError> + 'static,
|
||||||
R: ValueOrVector,
|
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`;
|
/// `UnstableChecker` is a struct so it can be placed inside `GothamState`;
|
||||||
|
|
|
@ -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_stop", op_global_timer_stop);
|
||||||
super::reg_json_sync(rt, "op_global_timer_start", op_global_timer_start);
|
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_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);
|
super::reg_json_sync(rt, "op_sleep_sync", op_sleep_sync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue