summaryrefslogtreecommitdiff
path: root/runtime/js/10_dispatch_minimal.js
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/js/10_dispatch_minimal.js')
-rw-r--r--runtime/js/10_dispatch_minimal.js114
1 files changed, 114 insertions, 0 deletions
diff --git a/runtime/js/10_dispatch_minimal.js b/runtime/js/10_dispatch_minimal.js
new file mode 100644
index 000000000..dceb23e5f
--- /dev/null
+++ b/runtime/js/10_dispatch_minimal.js
@@ -0,0 +1,114 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+((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 = core.getErrorClass(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);
+ }
+ 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);