mirror of
https://github.com/denoland/deno.git
synced 2025-08-04 10:59:13 +00:00
refactor: Use ES modules for internal runtime code (#17648)
This PR refactors all internal js files (except core) to be written as ES modules. `__bootstrap`has been mostly replaced with static imports in form in `internal:[path to file from repo root]`. To specify if files are ESM, an `esm` method has been added to `Extension`, similar to the `js` method. A new ModuleLoader called `InternalModuleLoader` has been added to enable the loading of internal specifiers, which is used in all situations except when a snapshot is only loaded, and not a new one is created from it. --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
parent
65500f36e8
commit
b4aa153097
123 changed files with 41574 additions and 41713 deletions
|
@ -1,254 +1,253 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const {
|
||||
Error,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
StringPrototypeStartsWith,
|
||||
String,
|
||||
SymbolIterator,
|
||||
SymbolToStringTag,
|
||||
} = window.__bootstrap.primordials;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { URL } = window.__bootstrap.url;
|
||||
const { getLocationHref } = window.__bootstrap.location;
|
||||
const { serializePermissions } = window.__bootstrap.permissions;
|
||||
const { log } = window.__bootstrap.util;
|
||||
const { ErrorEvent, MessageEvent, defineEventHandler } =
|
||||
window.__bootstrap.event;
|
||||
const { EventTarget } = window.__bootstrap.eventTarget;
|
||||
const {
|
||||
deserializeJsMessageData,
|
||||
serializeJsMessageData,
|
||||
MessagePortPrototype,
|
||||
} = window.__bootstrap.messagePort;
|
||||
const core = globalThis.Deno.core;
|
||||
const ops = core.ops;
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
Error,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
StringPrototypeStartsWith,
|
||||
String,
|
||||
SymbolIterator,
|
||||
SymbolToStringTag,
|
||||
} = primordials;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import { URL } from "internal:ext/url/00_url.js";
|
||||
import { getLocationHref } from "internal:ext/web/12_location.js";
|
||||
import { serializePermissions } from "internal:runtime/js/10_permissions.js";
|
||||
import { log } from "internal:runtime/js/06_util.js";
|
||||
import {
|
||||
defineEventHandler,
|
||||
ErrorEvent,
|
||||
EventTarget,
|
||||
MessageEvent,
|
||||
} from "internal:ext/web/02_event.js";
|
||||
import {
|
||||
deserializeJsMessageData,
|
||||
MessagePortPrototype,
|
||||
serializeJsMessageData,
|
||||
} from "internal:ext/web/13_message_port.js";
|
||||
|
||||
function createWorker(
|
||||
specifier,
|
||||
function createWorker(
|
||||
specifier,
|
||||
hasSourceCode,
|
||||
sourceCode,
|
||||
permissions,
|
||||
name,
|
||||
workerType,
|
||||
) {
|
||||
return ops.op_create_worker({
|
||||
hasSourceCode,
|
||||
sourceCode,
|
||||
permissions,
|
||||
name,
|
||||
permissions: serializePermissions(permissions),
|
||||
sourceCode,
|
||||
specifier,
|
||||
workerType,
|
||||
) {
|
||||
return ops.op_create_worker({
|
||||
hasSourceCode,
|
||||
});
|
||||
}
|
||||
|
||||
function hostTerminateWorker(id) {
|
||||
ops.op_host_terminate_worker(id);
|
||||
}
|
||||
|
||||
function hostPostMessage(id, data) {
|
||||
ops.op_host_post_message(id, data);
|
||||
}
|
||||
|
||||
function hostRecvCtrl(id) {
|
||||
return core.opAsync("op_host_recv_ctrl", id);
|
||||
}
|
||||
|
||||
function hostRecvMessage(id) {
|
||||
return core.opAsync("op_host_recv_message", id);
|
||||
}
|
||||
|
||||
class Worker extends EventTarget {
|
||||
#id = 0;
|
||||
#name = "";
|
||||
|
||||
// "RUNNING" | "CLOSED" | "TERMINATED"
|
||||
// "TERMINATED" means that any controls or messages received will be
|
||||
// discarded. "CLOSED" means that we have received a control
|
||||
// indicating that the worker is no longer running, but there might
|
||||
// still be messages left to receive.
|
||||
#status = "RUNNING";
|
||||
|
||||
constructor(specifier, options = {}) {
|
||||
super();
|
||||
specifier = String(specifier);
|
||||
const {
|
||||
deno,
|
||||
name,
|
||||
permissions: serializePermissions(permissions),
|
||||
sourceCode,
|
||||
type = "classic",
|
||||
} = options;
|
||||
|
||||
const workerType = webidl.converters["WorkerType"](type);
|
||||
|
||||
if (
|
||||
StringPrototypeStartsWith(specifier, "./") ||
|
||||
StringPrototypeStartsWith(specifier, "../") ||
|
||||
StringPrototypeStartsWith(specifier, "/") || workerType === "classic"
|
||||
) {
|
||||
const baseUrl = getLocationHref();
|
||||
if (baseUrl != null) {
|
||||
specifier = new URL(specifier, baseUrl).href;
|
||||
}
|
||||
}
|
||||
|
||||
this.#name = name;
|
||||
let hasSourceCode, sourceCode;
|
||||
if (workerType === "classic") {
|
||||
hasSourceCode = true;
|
||||
sourceCode = `importScripts("#");`;
|
||||
} else {
|
||||
hasSourceCode = false;
|
||||
sourceCode = "";
|
||||
}
|
||||
|
||||
const id = createWorker(
|
||||
specifier,
|
||||
hasSourceCode,
|
||||
sourceCode,
|
||||
deno?.permissions,
|
||||
name,
|
||||
workerType,
|
||||
);
|
||||
this.#id = id;
|
||||
this.#pollControl();
|
||||
this.#pollMessages();
|
||||
}
|
||||
|
||||
#handleError(e) {
|
||||
const event = new ErrorEvent("error", {
|
||||
cancelable: true,
|
||||
message: e.message,
|
||||
lineno: e.lineNumber ? e.lineNumber : undefined,
|
||||
colno: e.columnNumber ? e.columnNumber : undefined,
|
||||
filename: e.fileName,
|
||||
error: null,
|
||||
});
|
||||
}
|
||||
|
||||
function hostTerminateWorker(id) {
|
||||
ops.op_host_terminate_worker(id);
|
||||
}
|
||||
|
||||
function hostPostMessage(id, data) {
|
||||
ops.op_host_post_message(id, data);
|
||||
}
|
||||
|
||||
function hostRecvCtrl(id) {
|
||||
return core.opAsync("op_host_recv_ctrl", id);
|
||||
}
|
||||
|
||||
function hostRecvMessage(id) {
|
||||
return core.opAsync("op_host_recv_message", id);
|
||||
}
|
||||
|
||||
class Worker extends EventTarget {
|
||||
#id = 0;
|
||||
#name = "";
|
||||
|
||||
// "RUNNING" | "CLOSED" | "TERMINATED"
|
||||
// "TERMINATED" means that any controls or messages received will be
|
||||
// discarded. "CLOSED" means that we have received a control
|
||||
// indicating that the worker is no longer running, but there might
|
||||
// still be messages left to receive.
|
||||
#status = "RUNNING";
|
||||
|
||||
constructor(specifier, options = {}) {
|
||||
super();
|
||||
specifier = String(specifier);
|
||||
const {
|
||||
deno,
|
||||
name,
|
||||
type = "classic",
|
||||
} = options;
|
||||
|
||||
const workerType = webidl.converters["WorkerType"](type);
|
||||
|
||||
if (
|
||||
StringPrototypeStartsWith(specifier, "./") ||
|
||||
StringPrototypeStartsWith(specifier, "../") ||
|
||||
StringPrototypeStartsWith(specifier, "/") || workerType === "classic"
|
||||
) {
|
||||
const baseUrl = getLocationHref();
|
||||
if (baseUrl != null) {
|
||||
specifier = new URL(specifier, baseUrl).href;
|
||||
}
|
||||
}
|
||||
|
||||
this.#name = name;
|
||||
let hasSourceCode, sourceCode;
|
||||
if (workerType === "classic") {
|
||||
hasSourceCode = true;
|
||||
sourceCode = `importScripts("#");`;
|
||||
} else {
|
||||
hasSourceCode = false;
|
||||
sourceCode = "";
|
||||
}
|
||||
|
||||
const id = createWorker(
|
||||
specifier,
|
||||
hasSourceCode,
|
||||
sourceCode,
|
||||
deno?.permissions,
|
||||
name,
|
||||
workerType,
|
||||
);
|
||||
this.#id = id;
|
||||
this.#pollControl();
|
||||
this.#pollMessages();
|
||||
this.dispatchEvent(event);
|
||||
// Don't bubble error event to window for loader errors (`!e.fileName`).
|
||||
// TODO(nayeemrmn): It's not correct to use `e.fileName` to detect user
|
||||
// errors. It won't be there for non-awaited async ops for example.
|
||||
if (e.fileName && !event.defaultPrevented) {
|
||||
globalThis.dispatchEvent(event);
|
||||
}
|
||||
|
||||
#handleError(e) {
|
||||
const event = new ErrorEvent("error", {
|
||||
cancelable: true,
|
||||
message: e.message,
|
||||
lineno: e.lineNumber ? e.lineNumber : undefined,
|
||||
colno: e.columnNumber ? e.columnNumber : undefined,
|
||||
filename: e.fileName,
|
||||
error: null,
|
||||
});
|
||||
return event.defaultPrevented;
|
||||
}
|
||||
|
||||
this.dispatchEvent(event);
|
||||
// Don't bubble error event to window for loader errors (`!e.fileName`).
|
||||
// TODO(nayeemrmn): It's not correct to use `e.fileName` to detect user
|
||||
// errors. It won't be there for non-awaited async ops for example.
|
||||
if (e.fileName && !event.defaultPrevented) {
|
||||
window.dispatchEvent(event);
|
||||
#pollControl = async () => {
|
||||
while (this.#status === "RUNNING") {
|
||||
const { 0: type, 1: data } = await hostRecvCtrl(this.#id);
|
||||
|
||||
// If terminate was called then we ignore all messages
|
||||
if (this.#status === "TERMINATED") {
|
||||
return;
|
||||
}
|
||||
|
||||
return event.defaultPrevented;
|
||||
switch (type) {
|
||||
case 1: { // TerminalError
|
||||
this.#status = "CLOSED";
|
||||
} /* falls through */
|
||||
case 2: { // Error
|
||||
if (!this.#handleError(data)) {
|
||||
throw new Error("Unhandled error in child worker.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: { // Close
|
||||
log(`Host got "close" message from worker: ${this.#name}`);
|
||||
this.#status = "CLOSED";
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown worker event: "${type}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#pollControl = async () => {
|
||||
while (this.#status === "RUNNING") {
|
||||
const { 0: type, 1: data } = await hostRecvCtrl(this.#id);
|
||||
|
||||
// If terminate was called then we ignore all messages
|
||||
if (this.#status === "TERMINATED") {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 1: { // TerminalError
|
||||
this.#status = "CLOSED";
|
||||
} /* falls through */
|
||||
case 2: { // Error
|
||||
if (!this.#handleError(data)) {
|
||||
throw new Error("Unhandled error in child worker.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: { // Close
|
||||
log(`Host got "close" message from worker: ${this.#name}`);
|
||||
this.#status = "CLOSED";
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown worker event: "${type}"`);
|
||||
}
|
||||
}
|
||||
#pollMessages = async () => {
|
||||
while (this.#status !== "TERMINATED") {
|
||||
const data = await hostRecvMessage(this.#id);
|
||||
if (this.#status === "TERMINATED" || data === null) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
#pollMessages = async () => {
|
||||
while (this.#status !== "TERMINATED") {
|
||||
const data = await hostRecvMessage(this.#id);
|
||||
if (this.#status === "TERMINATED" || data === null) {
|
||||
return;
|
||||
}
|
||||
let message, transferables;
|
||||
try {
|
||||
const v = deserializeJsMessageData(data);
|
||||
message = v[0];
|
||||
transferables = v[1];
|
||||
} catch (err) {
|
||||
const event = new MessageEvent("messageerror", {
|
||||
cancelable: false,
|
||||
data: err,
|
||||
});
|
||||
this.dispatchEvent(event);
|
||||
return;
|
||||
}
|
||||
const event = new MessageEvent("message", {
|
||||
let message, transferables;
|
||||
try {
|
||||
const v = deserializeJsMessageData(data);
|
||||
message = v[0];
|
||||
transferables = v[1];
|
||||
} catch (err) {
|
||||
const event = new MessageEvent("messageerror", {
|
||||
cancelable: false,
|
||||
data: message,
|
||||
ports: transferables.filter((t) =>
|
||||
ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t)
|
||||
),
|
||||
data: err,
|
||||
});
|
||||
this.dispatchEvent(event);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
postMessage(message, transferOrOptions = {}) {
|
||||
const prefix = "Failed to execute 'postMessage' on 'MessagePort'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
message = webidl.converters.any(message);
|
||||
let options;
|
||||
if (
|
||||
webidl.type(transferOrOptions) === "Object" &&
|
||||
transferOrOptions !== undefined &&
|
||||
transferOrOptions[SymbolIterator] !== undefined
|
||||
) {
|
||||
const transfer = webidl.converters["sequence<object>"](
|
||||
transferOrOptions,
|
||||
{ prefix, context: "Argument 2" },
|
||||
);
|
||||
options = { transfer };
|
||||
} else {
|
||||
options = webidl.converters.StructuredSerializeOptions(
|
||||
transferOrOptions,
|
||||
{
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
},
|
||||
);
|
||||
}
|
||||
const { transfer } = options;
|
||||
const data = serializeJsMessageData(message, transfer);
|
||||
if (this.#status === "RUNNING") {
|
||||
hostPostMessage(this.#id, data);
|
||||
}
|
||||
const event = new MessageEvent("message", {
|
||||
cancelable: false,
|
||||
data: message,
|
||||
ports: transferables.filter((t) =>
|
||||
ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t)
|
||||
),
|
||||
});
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
terminate() {
|
||||
if (this.#status !== "TERMINATED") {
|
||||
this.#status = "TERMINATED";
|
||||
hostTerminateWorker(this.#id);
|
||||
}
|
||||
postMessage(message, transferOrOptions = {}) {
|
||||
const prefix = "Failed to execute 'postMessage' on 'MessagePort'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
message = webidl.converters.any(message);
|
||||
let options;
|
||||
if (
|
||||
webidl.type(transferOrOptions) === "Object" &&
|
||||
transferOrOptions !== undefined &&
|
||||
transferOrOptions[SymbolIterator] !== undefined
|
||||
) {
|
||||
const transfer = webidl.converters["sequence<object>"](
|
||||
transferOrOptions,
|
||||
{ prefix, context: "Argument 2" },
|
||||
);
|
||||
options = { transfer };
|
||||
} else {
|
||||
options = webidl.converters.StructuredSerializeOptions(
|
||||
transferOrOptions,
|
||||
{
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
},
|
||||
);
|
||||
}
|
||||
const { transfer } = options;
|
||||
const data = serializeJsMessageData(message, transfer);
|
||||
if (this.#status === "RUNNING") {
|
||||
hostPostMessage(this.#id, data);
|
||||
}
|
||||
|
||||
[SymbolToStringTag] = "Worker";
|
||||
}
|
||||
|
||||
defineEventHandler(Worker.prototype, "error");
|
||||
defineEventHandler(Worker.prototype, "message");
|
||||
defineEventHandler(Worker.prototype, "messageerror");
|
||||
terminate() {
|
||||
if (this.#status !== "TERMINATED") {
|
||||
this.#status = "TERMINATED";
|
||||
hostTerminateWorker(this.#id);
|
||||
}
|
||||
}
|
||||
|
||||
webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [
|
||||
"classic",
|
||||
"module",
|
||||
]);
|
||||
[SymbolToStringTag] = "Worker";
|
||||
}
|
||||
|
||||
window.__bootstrap.worker = {
|
||||
Worker,
|
||||
};
|
||||
})(this);
|
||||
defineEventHandler(Worker.prototype, "error");
|
||||
defineEventHandler(Worker.prototype, "message");
|
||||
defineEventHandler(Worker.prototype, "messageerror");
|
||||
|
||||
webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [
|
||||
"classic",
|
||||
"module",
|
||||
]);
|
||||
|
||||
export { Worker };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue