diff options
Diffstat (limited to 'runtime/js/10_dispatch_buffer.js')
-rw-r--r-- | runtime/js/10_dispatch_buffer.js | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/runtime/js/10_dispatch_buffer.js b/runtime/js/10_dispatch_buffer.js new file mode 100644 index 000000000..091fce504 --- /dev/null +++ b/runtime/js/10_dispatch_buffer.js @@ -0,0 +1,150 @@ +// 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); |