summaryrefslogtreecommitdiff
path: root/runtime/js/11_workers.js
diff options
context:
space:
mode:
authorLeo Kettmeir <crowlkats@toaxl.com>2023-02-07 20:22:46 +0100
committerGitHub <noreply@github.com>2023-02-07 20:22:46 +0100
commitb4aa1530970f7b9cc4e6f2f27e077852c4e178d3 (patch)
tree3d008912affe8550692183bd2697a386db5e3c79 /runtime/js/11_workers.js
parent65500f36e870b4ada3996b06aa287e30177d21a3 (diff)
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>
Diffstat (limited to 'runtime/js/11_workers.js')
-rw-r--r--runtime/js/11_workers.js441
1 files changed, 220 insertions, 221 deletions
diff --git a/runtime/js/11_workers.js b/runtime/js/11_workers.js
index 85c01e1a9..56ceb92f3 100644
--- a/runtime/js/11_workers.js
+++ b/runtime/js/11_workers.js
@@ -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;
-
- function createWorker(
- specifier,
+
+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,
+ 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();
}
- function hostTerminateWorker(id) {
- ops.op_host_terminate_worker(id);
- }
-
- function hostPostMessage(id, data) {
- ops.op_host_post_message(id, data);
- }
+ #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 hostRecvCtrl(id) {
- return core.opAsync("op_host_recv_ctrl", id);
- }
+ 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);
+ }
- function hostRecvMessage(id) {
- return core.opAsync("op_host_recv_message", id);
+ return event.defaultPrevented;
}
- 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;
- }
- }
+ #pollControl = async () => {
+ while (this.#status === "RUNNING") {
+ const { 0: type, 1: data } = await hostRecvCtrl(this.#id);
- this.#name = name;
- let hasSourceCode, sourceCode;
- if (workerType === "classic") {
- hasSourceCode = true;
- sourceCode = `importScripts("#");`;
- } else {
- hasSourceCode = false;
- sourceCode = "";
+ // If terminate was called then we ignore all messages
+ if (this.#status === "TERMINATED") {
+ return;
}
- 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,
- });
-
- 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);
- }
-
- return event.defaultPrevented;
- }
-
- #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}"`);
+ 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;
}
- }
- };
-
- #pollMessages = async () => {
- while (this.#status !== "TERMINATED") {
- const data = await hostRecvMessage(this.#id);
- if (this.#status === "TERMINATED" || data === null) {
+ case 3: { // Close
+ log(`Host got "close" message from worker: ${this.#name}`);
+ this.#status = "CLOSED";
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;
+ default: {
+ throw new Error(`Unknown worker event: "${type}"`);
}
- const event = new MessageEvent("message", {
+ }
+ }
+ };
+
+ #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: 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";
+ terminate() {
+ if (this.#status !== "TERMINATED") {
+ this.#status = "TERMINATED";
+ hostTerminateWorker(this.#id);
+ }
}
- defineEventHandler(Worker.prototype, "error");
- defineEventHandler(Worker.prototype, "message");
- defineEventHandler(Worker.prototype, "messageerror");
+ [SymbolToStringTag] = "Worker";
+}
- webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [
- "classic",
- "module",
- ]);
+defineEventHandler(Worker.prototype, "error");
+defineEventHandler(Worker.prototype, "message");
+defineEventHandler(Worker.prototype, "messageerror");
- window.__bootstrap.worker = {
- Worker,
- };
-})(this);
+webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [
+ "classic",
+ "module",
+]);
+
+export { Worker };