summaryrefslogtreecommitdiff
path: root/core/01_core.js
diff options
context:
space:
mode:
Diffstat (limited to 'core/01_core.js')
-rw-r--r--core/01_core.js465
1 files changed, 423 insertions, 42 deletions
diff --git a/core/01_core.js b/core/01_core.js
index a8bdeb2a8..3972dec33 100644
--- a/core/01_core.js
+++ b/core/01_core.js
@@ -16,11 +16,15 @@
ObjectAssign,
ObjectFreeze,
ObjectFromEntries,
+ ObjectKeys,
Promise,
+ PromiseReject,
+ PromiseResolve,
PromisePrototypeThen,
RangeError,
ReferenceError,
ReflectHas,
+ ReflectApply,
SafeArrayIterator,
SafeMap,
SafePromisePrototypeFinally,
@@ -32,7 +36,7 @@
TypeError,
URIError,
} = window.__bootstrap.primordials;
- const { ops } = window.Deno.core;
+ const { ops, asyncOps } = window.Deno.core;
const build = {
target: "unknown",
@@ -85,6 +89,17 @@
return opCallTracingEnabled;
}
+ function movePromise(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;
+ MapPrototypeSet(promiseMap, oldPromiseId, oldPromise);
+ }
+ return promiseRing[idx] = NO_PROMISE;
+ }
+
function setPromise(promiseId) {
const idx = promiseId % RING_SIZE;
// Move old promise from ring to map
@@ -208,7 +223,29 @@
return error;
}
- function unwrapOpResult(res) {
+ function unwrapOpError(hideFunction) {
+ return (res) => {
+ // .$err_class_name is a special key that should only exist on errors
+ const className = res?.$err_class_name;
+ if (!className) {
+ return res;
+ }
+
+ const errorBuilder = errorMap[className];
+ const err = errorBuilder ? errorBuilder(res.message) : new Error(
+ `Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`,
+ );
+ // Set .code if error was a known OS error, see error_codes.rs
+ if (res.code) {
+ err.code = res.code;
+ }
+ // Strip unwrapOpResult() and errorBuilder() calls from stack trace
+ ErrorCaptureStackTrace(err, hideFunction);
+ throw err;
+ };
+ }
+
+ function unwrapOpResultNewPromise(id, res, hideFunction) {
// .$err_class_name is a special key that should only exist on errors
if (res?.$err_class_name) {
const className = res.$err_class_name;
@@ -221,59 +258,359 @@
err.code = res.code;
}
// Strip unwrapOpResult() and errorBuilder() calls from stack trace
- ErrorCaptureStackTrace(err, unwrapOpResult);
- throw err;
+ ErrorCaptureStackTrace(err, hideFunction);
+ return PromiseReject(err);
}
- return res;
+ const promise = PromiseResolve(res);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ }
+
+ /*
+Basic codegen.
+
+TODO(mmastrac): automate this (handlebars?)
+
+let s = "";
+const vars = "abcdefghijklm";
+for (let i = 0; i < 10; i++) {
+ let args = "";
+ for (let j = 0; j < i; j++) {
+ args += `${vars[j]},`;
+ }
+ s += `
+ case ${i}:
+ fn = function async_op_${i}(${args}) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, ${args});
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_${i});
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_${i});
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(setPromise(id), unwrapOpError(eventLoopTick));
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+ `;
+}
+ */
+
+ // This function is called once per async stub
+ function asyncStub(opName, args) {
+ setUpAsyncStub(opName);
+ return ReflectApply(ops[opName], undefined, args);
+ }
+
+ function setUpAsyncStub(opName) {
+ const originalOp = asyncOps[opName];
+ let fn;
+ // The body of this switch statement can be generated using the script above.
+ switch (originalOp.length - 1) {
+ case 0:
+ fn = function async_op_0() {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_0);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_0);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ case 1:
+ fn = function async_op_1(a) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, a);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_1);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_1);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ case 2:
+ fn = function async_op_2(a, b) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, a, b);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_2);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_2);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ case 3:
+ fn = function async_op_3(a, b, c) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, a, b, c);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_3);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_3);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ case 4:
+ fn = function async_op_4(a, b, c, d) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, a, b, c, d);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_4);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_4);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ case 5:
+ fn = function async_op_5(a, b, c, d, e) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, a, b, c, d, e);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_5);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_5);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ case 6:
+ fn = function async_op_6(a, b, c, d, e, f) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, a, b, c, d, e, f);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_6);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_6);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ case 7:
+ fn = function async_op_7(a, b, c, d, e, f, g) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, a, b, c, d, e, f, g);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_7);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_7);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ case 8:
+ fn = function async_op_8(a, b, c, d, e, f, g, h) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, a, b, c, d, e, f, g, h);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_8);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_8);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ case 9:
+ fn = function async_op_9(a, b, c, d, e, f, g, h, i) {
+ const id = nextPromiseId++;
+ try {
+ const maybeResult = originalOp(id, a, b, c, d, e, f, g, h, i);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, async_op_9);
+ }
+ } catch (err) {
+ movePromise(id);
+ ErrorCaptureStackTrace(err, async_op_9);
+ return PromiseReject(err);
+ }
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
+ promise = handleOpCallTracing(opName, id, promise);
+ promise[promiseIdSymbol] = id;
+ return promise;
+ };
+ break;
+
+ default:
+ throw new Error(
+ `Too many arguments for async op codegen (length of ${opName} was ${
+ originalOp.length - 1
+ })`,
+ );
+ }
+ return (ops[opName] = fn);
}
function opAsync2(name, arg0, arg1) {
const id = nextPromiseId++;
- let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult);
- let maybeResult;
try {
- maybeResult = ops[name](id, arg0, arg1);
+ const maybeResult = asyncOps[name](id, arg0, arg1);
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, opAsync2);
+ }
} catch (err) {
- // Cleanup the just-created promise
- getPromise(id);
- if (!ReflectHas(ops, name)) {
- throw new TypeError(`${name} is not a registered op`);
+ movePromise(id);
+ if (!ReflectHas(asyncOps, name)) {
+ return PromiseReject(new TypeError(`${name} is not a registered op`));
}
- // Rethrow the error
- throw err;
+ ErrorCaptureStackTrace(err, opAsync2);
+ return PromiseReject(err);
}
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
promise = handleOpCallTracing(name, id, promise);
promise[promiseIdSymbol] = id;
- if (typeof maybeResult !== "undefined") {
- const promise = getPromise(id);
- promise.resolve(maybeResult);
- }
-
return promise;
}
function opAsync(name, ...args) {
const id = nextPromiseId++;
- let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult);
- let maybeResult;
try {
- maybeResult = ops[name](id, ...new SafeArrayIterator(args));
+ const maybeResult = asyncOps[name](id, ...new SafeArrayIterator(args));
+ if (maybeResult !== undefined) {
+ movePromise(id);
+ return unwrapOpResultNewPromise(id, maybeResult, opAsync);
+ }
} catch (err) {
- // Cleanup the just-created promise
- getPromise(id);
- if (!ReflectHas(ops, name)) {
- throw new TypeError(`${name} is not a registered op`);
+ movePromise(id);
+ if (!ReflectHas(asyncOps, name)) {
+ return PromiseReject(new TypeError(`${name} is not a registered op`));
}
- // Rethrow the error
- throw err;
+ ErrorCaptureStackTrace(err, opAsync);
+ return PromiseReject(err);
}
+ let promise = PromisePrototypeThen(
+ setPromise(id),
+ unwrapOpError(eventLoopTick),
+ );
promise = handleOpCallTracing(name, id, promise);
promise[promiseIdSymbol] = id;
- if (typeof maybeResult !== "undefined") {
- const promise = getPromise(id);
- promise.resolve(maybeResult);
- }
-
return promise;
}
@@ -439,8 +776,52 @@
);
}
+ // Eagerly initialize ops for snapshot purposes
+ for (const opName of new SafeArrayIterator(ObjectKeys(asyncOps))) {
+ setUpAsyncStub(opName);
+ }
+
+ function generateAsyncOpHandler(/* opNames... */) {
+ const fastOps = {};
+ for (const opName of new SafeArrayIterator(arguments)) {
+ if (ops[opName] === undefined) {
+ throw new Error(`Unknown or disabled op '${opName}'`);
+ }
+ if (asyncOps[opName] !== undefined) {
+ fastOps[opName] = setUpAsyncStub(opName);
+ } else {
+ fastOps[opName] = ops[opName];
+ }
+ }
+ return fastOps;
+ }
+
+ const {
+ op_close: close,
+ op_try_close: tryClose,
+ op_read: read,
+ op_read_all: readAll,
+ op_write: write,
+ op_write_all: writeAll,
+ op_read_sync: readSync,
+ op_write_sync: writeSync,
+ op_shutdown: shutdown,
+ } = generateAsyncOpHandler(
+ "op_close",
+ "op_try_close",
+ "op_read",
+ "op_read_all",
+ "op_write",
+ "op_write_all",
+ "op_read_sync",
+ "op_write_sync",
+ "op_shutdown",
+ );
+
// Extra Deno.core.* exports
const core = ObjectAssign(globalThis.Deno.core, {
+ asyncStub,
+ generateAsyncOpHandler,
opAsync,
opAsync2,
resources,
@@ -460,15 +841,15 @@
unrefOp,
setReportExceptionCallback,
setPromiseHooks,
- close: (rid) => ops.op_close(rid),
- tryClose: (rid) => ops.op_try_close(rid),
- read: opAsync.bind(null, "op_read"),
- readAll: opAsync.bind(null, "op_read_all"),
- write: opAsync.bind(null, "op_write"),
- writeAll: opAsync.bind(null, "op_write_all"),
- readSync: (rid, buffer) => ops.op_read_sync(rid, buffer),
- writeSync: (rid, buffer) => ops.op_write_sync(rid, buffer),
- shutdown: opAsync.bind(null, "op_shutdown"),
+ close,
+ tryClose,
+ read,
+ readAll,
+ write,
+ writeAll,
+ readSync,
+ writeSync,
+ shutdown,
print: (msg, isErr) => ops.op_print(msg, isErr),
setMacrotaskCallback,
setNextTickCallback,