mirror of
https://github.com/denoland/deno.git
synced 2024-11-01 09:24:20 -04:00
151 lines
4.8 KiB
JavaScript
151 lines
4.8 KiB
JavaScript
|
// 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);
|