summaryrefslogtreecommitdiff
path: root/runtime/js
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/js')
-rw-r--r--runtime/js/10_dispatch_buffer.js150
-rw-r--r--runtime/js/10_dispatch_minimal.js115
-rw-r--r--runtime/js/11_timers.js4
-rw-r--r--runtime/js/12_io.js10
-rw-r--r--runtime/js/99_main.js8
5 files changed, 158 insertions, 129 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);
diff --git a/runtime/js/10_dispatch_minimal.js b/runtime/js/10_dispatch_minimal.js
deleted file mode 100644
index e74f8c393..000000000
--- a/runtime/js/10_dispatch_minimal.js
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-"use strict";
-
-((window) => {
- const core = window.Deno.core;
- const util = window.__bootstrap.util;
-
- // Using an object without a prototype because `Map` was causing GC problems.
- const promiseTableMin = Object.create(null);
-
- const decoder = new TextDecoder();
-
- // Note it's important that promiseId starts at 1 instead of 0, because sync
- // messages are indicated with promiseId 0. If we ever add wrap around logic for
- // overflows, this should be taken into account.
- let _nextPromiseId = 1;
-
- function nextPromiseId() {
- return _nextPromiseId++;
- }
-
- function recordFromBufMinimal(ui8) {
- const headerLen = 12;
- const header = ui8.subarray(0, headerLen);
- const buf32 = new Int32Array(
- header.buffer,
- header.byteOffset,
- header.byteLength / 4,
- );
- const promiseId = buf32[0];
- const arg = buf32[1];
- const result = buf32[2];
- let err;
-
- if (arg < 0) {
- err = {
- className: decoder.decode(ui8.subarray(headerLen, headerLen + result)),
- message: decoder.decode(ui8.subarray(headerLen + result)),
- };
- } else if (ui8.length != 12) {
- throw new TypeError("Malformed response message");
- }
-
- return {
- promiseId,
- arg,
- result,
- err,
- };
- }
-
- function unwrapResponse(res) {
- if (res.err != null) {
- const [ErrorClass, args] = core.getErrorClassAndArgs(res.err.className);
- 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().`,
- );
- }
- throw new ErrorClass(res.err.message, ...args);
- }
- return res.result;
- }
-
- const scratch32 = new Int32Array(3);
- const scratchBytes = new Uint8Array(
- scratch32.buffer,
- scratch32.byteOffset,
- scratch32.byteLength,
- );
- util.assert(scratchBytes.byteLength === scratch32.length * 4);
-
- function asyncMsgFromRust(ui8) {
- const record = recordFromBufMinimal(ui8);
- const { promiseId } = record;
- const promise = promiseTableMin[promiseId];
- delete promiseTableMin[promiseId];
- util.assert(promise);
- promise.resolve(record);
- }
-
- async function sendAsync(opName, arg, zeroCopy) {
- const promiseId = nextPromiseId(); // AKA cmdId
- scratch32[0] = promiseId;
- scratch32[1] = arg;
- scratch32[2] = 0; // result
- const promise = util.createResolvable();
- const buf = core.dispatchByName(opName, scratchBytes, zeroCopy);
- if (buf != null) {
- const record = recordFromBufMinimal(buf);
- // Sync result.
- promise.resolve(record);
- } else {
- // Async result.
- promiseTableMin[promiseId] = promise;
- }
-
- const res = await promise;
- return unwrapResponse(res);
- }
-
- function sendSync(opName, arg, zeroCopy) {
- scratch32[0] = 0; // promiseId 0 indicates sync
- scratch32[1] = arg;
- const res = core.dispatchByName(opName, scratchBytes, zeroCopy);
- const resRecord = recordFromBufMinimal(res);
- return unwrapResponse(resRecord);
- }
-
- window.__bootstrap.dispatchMinimal = {
- asyncMsgFromRust,
- sendSync,
- sendAsync,
- };
-})(this);
diff --git a/runtime/js/11_timers.js b/runtime/js/11_timers.js
index 4c693aa4a..f07622388 100644
--- a/runtime/js/11_timers.js
+++ b/runtime/js/11_timers.js
@@ -4,7 +4,7 @@
((window) => {
const assert = window.__bootstrap.util.assert;
const core = window.Deno.core;
- const { sendSync } = window.__bootstrap.dispatchMinimal;
+ const { bufferOpSync } = window.__bootstrap.dispatchBuffer;
function opStopGlobalTimer() {
core.jsonOpSync("op_global_timer_stop");
@@ -20,7 +20,7 @@
const nowBytes = new Uint8Array(8);
function opNow() {
- sendSync("op_now", 0, nowBytes);
+ bufferOpSync("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 3818069c1..09e87f990 100644
--- a/runtime/js/12_io.js
+++ b/runtime/js/12_io.js
@@ -7,7 +7,7 @@
((window) => {
const DEFAULT_BUFFER_SIZE = 32 * 1024;
- const { sendSync, sendAsync } = window.__bootstrap.dispatchMinimal;
+ 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 = sendSync("op_read", rid, buffer);
+ const nread = bufferOpSync("op_read_sync", rid, buffer);
if (nread < 0) {
throw new Error("read error");
}
@@ -97,7 +97,7 @@
return 0;
}
- const nread = await sendAsync("op_read", rid, buffer);
+ const nread = await bufferOpAsync("op_read_async", rid, buffer);
if (nread < 0) {
throw new Error("read error");
}
@@ -106,7 +106,7 @@
}
function writeSync(rid, data) {
- const result = sendSync("op_write", rid, data);
+ const result = bufferOpSync("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 sendAsync("op_write", rid, data);
+ const result = await bufferOpAsync("op_write_async", rid, data);
if (result < 0) {
throw new Error("write error");
}
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js
index d96aaaaae..233c5cd43 100644
--- a/runtime/js/99_main.js
+++ b/runtime/js/99_main.js
@@ -11,7 +11,6 @@ delete Object.prototype.__proto__;
const eventTarget = window.__bootstrap.eventTarget;
const globalInterfaces = window.__bootstrap.globalInterfaces;
const location = window.__bootstrap.location;
- const dispatchMinimal = window.__bootstrap.dispatchMinimal;
const build = window.__bootstrap.build;
const version = window.__bootstrap.version;
const errorStack = window.__bootstrap.errorStack;
@@ -142,12 +141,7 @@ delete Object.prototype.__proto__;
}
function runtimeStart(runtimeOptions, source) {
- const opsMap = core.ops();
- for (const [name, opId] of Object.entries(opsMap)) {
- if (name === "op_write" || name === "op_read") {
- core.setAsyncHandler(opId, dispatchMinimal.asyncMsgFromRust);
- }
- }
+ core.ops();
core.setMacrotaskCallback(timers.handleTimerMacrotask);
version.setVersions(