mirror of
https://github.com/denoland/deno.git
synced 2024-12-11 01:58:05 -05:00
3d0b879c0d
I would like to get this change into Deno before merging https://github.com/denoland/deno_lint/pull/1152
898 lines
26 KiB
JavaScript
898 lines
26 KiB
JavaScript
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
"use strict";
|
|
|
|
((window) => {
|
|
const {
|
|
Array,
|
|
ArrayPrototypeFill,
|
|
ArrayPrototypeMap,
|
|
ArrayPrototypePush,
|
|
Error,
|
|
ErrorCaptureStackTrace,
|
|
MapPrototypeDelete,
|
|
MapPrototypeGet,
|
|
MapPrototypeHas,
|
|
MapPrototypeSet,
|
|
ObjectAssign,
|
|
ObjectFreeze,
|
|
ObjectFromEntries,
|
|
ObjectKeys,
|
|
Promise,
|
|
PromiseReject,
|
|
PromiseResolve,
|
|
PromisePrototypeThen,
|
|
RangeError,
|
|
ReferenceError,
|
|
ReflectHas,
|
|
ReflectApply,
|
|
SafeArrayIterator,
|
|
SafeMap,
|
|
SafePromisePrototypeFinally,
|
|
StringPrototypeSlice,
|
|
StringPrototypeSplit,
|
|
SymbolFor,
|
|
SyntaxError,
|
|
TypeError,
|
|
URIError,
|
|
setQueueMicrotask,
|
|
} = window.__bootstrap.primordials;
|
|
const { ops, asyncOps } = window.Deno.core;
|
|
|
|
const build = {
|
|
target: "unknown",
|
|
arch: "unknown",
|
|
os: "unknown",
|
|
vendor: "unknown",
|
|
env: undefined,
|
|
};
|
|
|
|
function setBuildInfo(target) {
|
|
const { 0: arch, 1: vendor, 2: os, 3: env } = StringPrototypeSplit(
|
|
target,
|
|
"-",
|
|
4,
|
|
);
|
|
build.target = target;
|
|
build.arch = arch;
|
|
build.vendor = vendor;
|
|
build.os = os;
|
|
build.env = env;
|
|
ObjectFreeze(build);
|
|
}
|
|
|
|
const errorMap = {};
|
|
// Builtin v8 / JS errors
|
|
registerErrorClass("Error", Error);
|
|
registerErrorClass("RangeError", RangeError);
|
|
registerErrorClass("ReferenceError", ReferenceError);
|
|
registerErrorClass("SyntaxError", SyntaxError);
|
|
registerErrorClass("TypeError", TypeError);
|
|
registerErrorClass("URIError", URIError);
|
|
|
|
let nextPromiseId = 1;
|
|
const promiseMap = new SafeMap();
|
|
const RING_SIZE = 4 * 1024;
|
|
const NO_PROMISE = null; // Alias to null is faster than plain nulls
|
|
const promiseRing = ArrayPrototypeFill(new Array(RING_SIZE), NO_PROMISE);
|
|
// TODO(bartlomieju): it future use `v8::Private` so it's not visible
|
|
// to users. Currently missing bindings.
|
|
const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId");
|
|
|
|
let opCallTracingEnabled = false;
|
|
const opCallTraces = new SafeMap();
|
|
|
|
function enableOpCallTracing() {
|
|
opCallTracingEnabled = true;
|
|
}
|
|
|
|
function isOpCallTracingEnabled() {
|
|
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
|
|
const oldPromise = promiseRing[idx];
|
|
if (oldPromise !== NO_PROMISE) {
|
|
const oldPromiseId = promiseId - RING_SIZE;
|
|
MapPrototypeSet(promiseMap, 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 = MapPrototypeGet(promiseMap, promiseId);
|
|
MapPrototypeDelete(promiseMap, 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 hasPromise(promiseId) {
|
|
// Check if out of ring bounds, fallback to map
|
|
const outOfBounds = promiseId < nextPromiseId - RING_SIZE;
|
|
if (outOfBounds) {
|
|
return MapPrototypeHas(promiseMap, promiseId);
|
|
}
|
|
// Otherwise check it in ring
|
|
const idx = promiseId % RING_SIZE;
|
|
return promiseRing[idx] != NO_PROMISE;
|
|
}
|
|
|
|
const macrotaskCallbacks = [];
|
|
const nextTickCallbacks = [];
|
|
|
|
function setMacrotaskCallback(cb) {
|
|
ArrayPrototypePush(macrotaskCallbacks, cb);
|
|
}
|
|
|
|
function setNextTickCallback(cb) {
|
|
ArrayPrototypePush(nextTickCallbacks, cb);
|
|
}
|
|
|
|
// This function has variable number of arguments. The last argument describes
|
|
// if there's a "next tick" scheduled by the Node.js compat layer. Arguments
|
|
// before last are alternating integers and any values that describe the
|
|
// responses of async ops.
|
|
function eventLoopTick() {
|
|
// First respond to all pending ops.
|
|
for (let i = 0; i < arguments.length - 1; i += 2) {
|
|
const promiseId = arguments[i];
|
|
const res = arguments[i + 1];
|
|
const promise = getPromise(promiseId);
|
|
promise.resolve(res);
|
|
}
|
|
// Drain nextTick queue if there's a tick scheduled.
|
|
if (arguments[arguments.length - 1]) {
|
|
for (let i = 0; i < nextTickCallbacks.length; i++) {
|
|
nextTickCallbacks[i]();
|
|
}
|
|
} else {
|
|
ops.op_run_microtasks();
|
|
}
|
|
// Finally drain macrotask queue.
|
|
for (let i = 0; i < macrotaskCallbacks.length; i++) {
|
|
const cb = macrotaskCallbacks[i];
|
|
while (true) {
|
|
const res = cb();
|
|
ops.op_run_microtasks();
|
|
if (res === true) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function registerErrorClass(className, errorClass) {
|
|
registerErrorBuilder(className, (msg) => new errorClass(msg));
|
|
}
|
|
|
|
function registerErrorBuilder(className, errorBuilder) {
|
|
if (typeof errorMap[className] !== "undefined") {
|
|
throw new TypeError(`Error class for "${className}" already registered`);
|
|
}
|
|
errorMap[className] = errorBuilder;
|
|
}
|
|
|
|
function buildCustomError(className, message, code) {
|
|
let error;
|
|
try {
|
|
error = errorMap[className]?.(message);
|
|
} catch (e) {
|
|
throw new Error(
|
|
`Unsable to build custom error for "${className}"\n ${e.message}`,
|
|
);
|
|
}
|
|
// Strip buildCustomError() calls from stack trace
|
|
if (typeof error == "object") {
|
|
ErrorCaptureStackTrace(error, buildCustomError);
|
|
if (code) {
|
|
error.code = code;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
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;
|
|
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);
|
|
return PromiseReject(err);
|
|
}
|
|
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++;
|
|
try {
|
|
const maybeResult = asyncOps[name](id, arg0, arg1);
|
|
if (maybeResult !== undefined) {
|
|
movePromise(id);
|
|
return unwrapOpResultNewPromise(id, maybeResult, opAsync2);
|
|
}
|
|
} catch (err) {
|
|
movePromise(id);
|
|
if (!ReflectHas(asyncOps, name)) {
|
|
return PromiseReject(new TypeError(`${name} is not a registered op`));
|
|
}
|
|
ErrorCaptureStackTrace(err, opAsync2);
|
|
return PromiseReject(err);
|
|
}
|
|
let promise = PromisePrototypeThen(
|
|
setPromise(id),
|
|
unwrapOpError(eventLoopTick),
|
|
);
|
|
promise = handleOpCallTracing(name, id, promise);
|
|
promise[promiseIdSymbol] = id;
|
|
return promise;
|
|
}
|
|
|
|
function opAsync(name, ...args) {
|
|
const id = nextPromiseId++;
|
|
try {
|
|
const maybeResult = asyncOps[name](id, ...new SafeArrayIterator(args));
|
|
if (maybeResult !== undefined) {
|
|
movePromise(id);
|
|
return unwrapOpResultNewPromise(id, maybeResult, opAsync);
|
|
}
|
|
} catch (err) {
|
|
movePromise(id);
|
|
if (!ReflectHas(asyncOps, name)) {
|
|
return PromiseReject(new TypeError(`${name} is not a registered op`));
|
|
}
|
|
ErrorCaptureStackTrace(err, opAsync);
|
|
return PromiseReject(err);
|
|
}
|
|
let promise = PromisePrototypeThen(
|
|
setPromise(id),
|
|
unwrapOpError(eventLoopTick),
|
|
);
|
|
promise = handleOpCallTracing(name, id, promise);
|
|
promise[promiseIdSymbol] = id;
|
|
return promise;
|
|
}
|
|
|
|
function handleOpCallTracing(opName, promiseId, p) {
|
|
if (opCallTracingEnabled) {
|
|
const stack = StringPrototypeSlice(new Error().stack, 6);
|
|
MapPrototypeSet(opCallTraces, promiseId, { opName, stack });
|
|
return SafePromisePrototypeFinally(
|
|
p,
|
|
() => MapPrototypeDelete(opCallTraces, promiseId),
|
|
);
|
|
} else {
|
|
return p;
|
|
}
|
|
}
|
|
|
|
function refOp(promiseId) {
|
|
if (!hasPromise(promiseId)) {
|
|
return;
|
|
}
|
|
ops.op_ref_op(promiseId);
|
|
}
|
|
|
|
function unrefOp(promiseId) {
|
|
if (!hasPromise(promiseId)) {
|
|
return;
|
|
}
|
|
ops.op_unref_op(promiseId);
|
|
}
|
|
|
|
function resources() {
|
|
return ObjectFromEntries(ops.op_resources());
|
|
}
|
|
|
|
function metrics() {
|
|
const { 0: aggregate, 1: perOps } = ops.op_metrics();
|
|
aggregate.ops = ObjectFromEntries(ArrayPrototypeMap(
|
|
ops.op_op_names(),
|
|
(opName, opId) => [opName, perOps[opId]],
|
|
));
|
|
return aggregate;
|
|
}
|
|
|
|
let reportExceptionCallback = undefined;
|
|
|
|
// Used to report errors thrown from functions passed to `queueMicrotask()`.
|
|
// The callback will be passed the thrown error. For example, you can use this
|
|
// to dispatch an error event to the global scope.
|
|
// In other words, set the implementation for
|
|
// https://html.spec.whatwg.org/multipage/webappapis.html#report-the-exception
|
|
function setReportExceptionCallback(cb) {
|
|
if (typeof cb != "function") {
|
|
throw new TypeError("expected a function");
|
|
}
|
|
reportExceptionCallback = cb;
|
|
}
|
|
|
|
function queueMicrotask(cb) {
|
|
if (typeof cb != "function") {
|
|
throw new TypeError("expected a function");
|
|
}
|
|
return ops.op_queue_microtask(() => {
|
|
try {
|
|
cb();
|
|
} catch (error) {
|
|
if (reportExceptionCallback) {
|
|
reportExceptionCallback(error);
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Some "extensions" rely on "BadResource" and "Interrupted" errors in the
|
|
// JS code (eg. "deno_net") so they are provided in "Deno.core" but later
|
|
// reexported on "Deno.errors"
|
|
class BadResource extends Error {
|
|
constructor(msg) {
|
|
super(msg);
|
|
this.name = "BadResource";
|
|
}
|
|
}
|
|
const BadResourcePrototype = BadResource.prototype;
|
|
|
|
class Interrupted extends Error {
|
|
constructor(msg) {
|
|
super(msg);
|
|
this.name = "Interrupted";
|
|
}
|
|
}
|
|
const InterruptedPrototype = Interrupted.prototype;
|
|
|
|
const promiseHooks = [
|
|
[], // init
|
|
[], // before
|
|
[], // after
|
|
[], // resolve
|
|
];
|
|
|
|
function setPromiseHooks(init, before, after, resolve) {
|
|
const hooks = [init, before, after, resolve];
|
|
for (let i = 0; i < hooks.length; i++) {
|
|
const hook = hooks[i];
|
|
// Skip if no callback was provided for this hook type.
|
|
if (hook == null) {
|
|
continue;
|
|
}
|
|
// Verify that the type of `hook` is a function.
|
|
if (typeof hook !== "function") {
|
|
throw new TypeError(`Expected function at position ${i}`);
|
|
}
|
|
// Add the hook to the list.
|
|
ArrayPrototypePush(promiseHooks[i], hook);
|
|
}
|
|
|
|
const wrappedHooks = ArrayPrototypeMap(promiseHooks, (hooks) => {
|
|
switch (hooks.length) {
|
|
case 0:
|
|
return undefined;
|
|
case 1:
|
|
return hooks[0];
|
|
case 2:
|
|
return create2xHookWrapper(hooks[0], hooks[1]);
|
|
case 3:
|
|
return create3xHookWrapper(hooks[0], hooks[1], hooks[2]);
|
|
default:
|
|
return createHookListWrapper(hooks);
|
|
}
|
|
|
|
// The following functions are used to create wrapper functions that call
|
|
// all the hooks in a list of a certain length. The reason to use a
|
|
// function that creates a wrapper is to minimize the number of objects
|
|
// captured in the closure.
|
|
function create2xHookWrapper(hook1, hook2) {
|
|
return function (promise, parent) {
|
|
hook1(promise, parent);
|
|
hook2(promise, parent);
|
|
};
|
|
}
|
|
function create3xHookWrapper(hook1, hook2, hook3) {
|
|
return function (promise, parent) {
|
|
hook1(promise, parent);
|
|
hook2(promise, parent);
|
|
hook3(promise, parent);
|
|
};
|
|
}
|
|
function createHookListWrapper(hooks) {
|
|
return function (promise, parent) {
|
|
for (let i = 0; i < hooks.length; i++) {
|
|
const hook = hooks[i];
|
|
hook(promise, parent);
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
ops.op_set_promise_hooks(
|
|
wrappedHooks[0],
|
|
wrappedHooks[1],
|
|
wrappedHooks[2],
|
|
wrappedHooks[3],
|
|
);
|
|
}
|
|
|
|
// 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,
|
|
metrics,
|
|
registerErrorBuilder,
|
|
registerErrorClass,
|
|
buildCustomError,
|
|
eventLoopTick,
|
|
BadResource,
|
|
BadResourcePrototype,
|
|
Interrupted,
|
|
InterruptedPrototype,
|
|
enableOpCallTracing,
|
|
isOpCallTracingEnabled,
|
|
opCallTraces,
|
|
refOp,
|
|
unrefOp,
|
|
setReportExceptionCallback,
|
|
setPromiseHooks,
|
|
close,
|
|
tryClose,
|
|
read,
|
|
readAll,
|
|
write,
|
|
writeAll,
|
|
readSync,
|
|
writeSync,
|
|
shutdown,
|
|
print: (msg, isErr) => ops.op_print(msg, isErr),
|
|
setMacrotaskCallback,
|
|
setNextTickCallback,
|
|
runMicrotasks: () => ops.op_run_microtasks(),
|
|
hasTickScheduled: () => ops.op_has_tick_scheduled(),
|
|
setHasTickScheduled: (bool) => ops.op_set_has_tick_scheduled(bool),
|
|
evalContext: (
|
|
source,
|
|
specifier,
|
|
) => ops.op_eval_context(source, specifier),
|
|
createHostObject: () => ops.op_create_host_object(),
|
|
encode: (text) => ops.op_encode(text),
|
|
decode: (buffer) => ops.op_decode(buffer),
|
|
serialize: (
|
|
value,
|
|
options,
|
|
errorCallback,
|
|
) => ops.op_serialize(value, options, errorCallback),
|
|
deserialize: (buffer, options) => ops.op_deserialize(buffer, options),
|
|
getPromiseDetails: (promise) => ops.op_get_promise_details(promise),
|
|
getProxyDetails: (proxy) => ops.op_get_proxy_details(proxy),
|
|
isProxy: (value) => ops.op_is_proxy(value),
|
|
memoryUsage: () => ops.op_memory_usage(),
|
|
setWasmStreamingCallback: (fn) => ops.op_set_wasm_streaming_callback(fn),
|
|
abortWasmStreaming: (
|
|
rid,
|
|
error,
|
|
) => ops.op_abort_wasm_streaming(rid, error),
|
|
destructureError: (error) => ops.op_destructure_error(error),
|
|
opNames: () => ops.op_op_names(),
|
|
eventLoopHasMoreWork: () => ops.op_event_loop_has_more_work(),
|
|
setPromiseRejectCallback: (fn) => ops.op_set_promise_reject_callback(fn),
|
|
byteLength: (str) => ops.op_str_byte_length(str),
|
|
build,
|
|
setBuildInfo,
|
|
});
|
|
|
|
ObjectAssign(globalThis.__bootstrap, { core });
|
|
const internals = {};
|
|
ObjectAssign(globalThis.__bootstrap, { internals });
|
|
ObjectAssign(globalThis.Deno, { core });
|
|
|
|
// Direct bindings on `globalThis`
|
|
ObjectAssign(globalThis, { queueMicrotask });
|
|
setQueueMicrotask(queueMicrotask);
|
|
})(globalThis);
|