diff options
author | Leo Kettmeir <crowlkats@toaxl.com> | 2023-02-07 20:22:46 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-07 20:22:46 +0100 |
commit | b4aa1530970f7b9cc4e6f2f27e077852c4e178d3 (patch) | |
tree | 3d008912affe8550692183bd2697a386db5e3c79 /runtime/js/11_workers.js | |
parent | 65500f36e870b4ada3996b06aa287e30177d21a3 (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.js | 441 |
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 }; |