1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";
((window) => {
// Available on start due to bindings.
const core = window.Deno.core;
const { recv, send } = core;
let opsCache = {};
const errorMap = {};
let nextPromiseId = 1;
const promiseMap = new Map();
const RING_SIZE = 4 * 1024;
const NO_PROMISE = null; // Alias to null is faster than plain nulls
const promiseRing = new Array(RING_SIZE).fill(NO_PROMISE);
function init() {
recv(handleAsyncMsgFromRust);
}
function setPromise(promiseId) {
const idx = promiseId % RING_SIZE;
// Move old promise from ring to map
const oldPromise = promiseRing[idx];
if (oldPromise !== NO_PROMISE) {
const oldPromiseId = promiseId - RING_SIZE;
promiseMap.set(oldPromiseId, oldPromise);
}
// Set new promise
return promiseRing[idx] = newPromise();
}
function getPromise(promiseId) {
// Check if out of ring bounds, fallback to map
const outOfBounds = promiseId < nextPromiseId - RING_SIZE;
if (outOfBounds) {
const promise = promiseMap.get(promiseId);
promiseMap.delete(promiseId);
return promise;
}
// Otherwise take from ring
const idx = promiseId % RING_SIZE;
const promise = promiseRing[idx];
promiseRing[idx] = NO_PROMISE;
return promise;
}
function newPromise() {
let resolve, reject;
const promise = new Promise((resolve_, reject_) => {
resolve = resolve_;
reject = reject_;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
}
function ops() {
// op id 0 is a special value to retrieve the map of registered ops.
const newOpsCache = Object.fromEntries(send(0));
opsCache = Object.freeze(newOpsCache);
return opsCache;
}
function handleAsyncMsgFromRust() {
for (let i = 0; i < arguments.length; i += 2) {
const promiseId = arguments[i];
const res = arguments[i + 1];
const promise = getPromise(promiseId);
promise.resolve(res);
}
}
function dispatch(opName, promiseId, control, zeroCopy) {
return send(opsCache[opName], promiseId, control, zeroCopy);
}
function registerErrorClass(errorName, className, args) {
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, []];
}
function unwrapOpResult(res) {
// .$err_class_name is a special key that should only exist on errors
if (res?.$err_class_name) {
const className = res.$err_class_name;
const [ErrorClass, args] = getErrorClassAndArgs(className);
if (!ErrorClass) {
throw new Error(
`Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`,
);
}
throw new ErrorClass(res.message, ...args);
}
return res;
}
function jsonOpAsync(opName, args = null, zeroCopy = null) {
const promiseId = nextPromiseId++;
const maybeError = dispatch(opName, promiseId, args, zeroCopy);
// Handle sync error (e.g: error parsing args)
if (maybeError) return unwrapOpResult(maybeError);
return setPromise(promiseId).then(unwrapOpResult);
}
function jsonOpSync(opName, args = null, zeroCopy = null) {
return unwrapOpResult(dispatch(opName, null, args, zeroCopy));
}
function binOpSync(opName, args = null, zeroCopy = null) {
return jsonOpSync(opName, args, zeroCopy);
}
function binOpAsync(opName, args = null, zeroCopy = null) {
return jsonOpAsync(opName, args, zeroCopy);
}
function resources() {
return Object.fromEntries(jsonOpSync("op_resources"));
}
function close(rid) {
jsonOpSync("op_close", rid);
}
Object.assign(window.Deno.core, {
binOpAsync,
binOpSync,
jsonOpAsync,
jsonOpSync,
dispatch: send,
dispatchByName: dispatch,
ops,
close,
resources,
registerErrorClass,
init,
});
})(this);
|