1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 08:09:08 -05:00

workers: proper TS libs, more spec-compliant APIs (#3812)

* split lib.deno_main.d.ts into:
  - lib.deno.shared_globals.d.ts
  - lib.deno.window.d.ts
  - lib.deno.worker.d.ts
* remove no longer used libs:
  - lib.deno_main.d.ts
  - lib.deno_worker.d.ts
* change module loading to use proper TS library for compilation
* align to Worker API spec:
  - Worker.terminate()
  - self.close()
  - self.name
This commit is contained in:
Bartek Iwańczuk 2020-01-29 18:54:23 +01:00 committed by GitHub
parent d14864c57c
commit 161adfc51b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 449 additions and 287 deletions

View file

@ -71,14 +71,21 @@ fn main() {
let snapshot_path = o.join("COMPILER_SNAPSHOT.bin"); let snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
let mut custom_libs: HashMap<String, PathBuf> = HashMap::new(); let mut custom_libs: HashMap<String, PathBuf> = HashMap::new();
custom_libs.insert( custom_libs.insert(
"lib.deno_main.d.ts".to_string(), "lib.deno.window.d.ts".to_string(),
c.join("js/lib.deno_main.d.ts"), c.join("js/lib.deno.window.d.ts"),
); );
custom_libs.insert( custom_libs.insert(
"lib.deno_worker.d.ts".to_string(), "lib.deno.worker.d.ts".to_string(),
c.join("js/lib.deno_worker.d.ts"), c.join("js/lib.deno.worker.d.ts"),
);
custom_libs.insert(
"lib.deno.shared_globals.d.ts".to_string(),
c.join("js/lib.deno.shared_globals.d.ts"),
);
custom_libs.insert(
"lib.deno.ns.d.ts".to_string(),
c.join("js/lib.deno.ns.d.ts"),
); );
custom_libs.insert("lib.deno.d.ts".to_string(), c.join("js/lib.deno.d.ts"));
let main_module_name = let main_module_name =
deno_typescript::compile_bundle(&bundle_path, root_names) deno_typescript::compile_bundle(&bundle_path, root_names)

View file

@ -13,6 +13,7 @@ pub use js::JsCompiler;
pub use json::JsonCompiler; pub use json::JsonCompiler;
pub use ts::runtime_compile_async; pub use ts::runtime_compile_async;
pub use ts::runtime_transpile_async; pub use ts::runtime_transpile_async;
pub use ts::TargetLib;
pub use ts::TsCompiler; pub use ts::TsCompiler;
pub use wasm::WasmCompiler; pub use wasm::WasmCompiler;

View file

@ -37,6 +37,12 @@ lazy_static! {
Regex::new(r#""checkJs"\s*?:\s*?true"#).unwrap(); Regex::new(r#""checkJs"\s*?:\s*?true"#).unwrap();
} }
#[derive(Clone, Copy)]
pub enum TargetLib {
Main,
Worker,
}
/// Struct which represents the state of the compiler /// Struct which represents the state of the compiler
/// configuration where the first is canonical name for the configuration file, /// configuration where the first is canonical name for the configuration file,
/// second is a vector of the bytes of the contents of the configuration file, /// second is a vector of the bytes of the contents of the configuration file,
@ -318,6 +324,7 @@ impl TsCompiler {
&self, &self,
global_state: ThreadSafeGlobalState, global_state: ThreadSafeGlobalState,
source_file: &SourceFile, source_file: &SourceFile,
target: TargetLib,
) -> Pin<Box<CompiledModuleFuture>> { ) -> Pin<Box<CompiledModuleFuture>> {
if self.has_compiled(&source_file.url) { if self.has_compiled(&source_file.url) {
return match self.get_compiled_module(&source_file.url) { return match self.get_compiled_module(&source_file.url) {
@ -360,7 +367,10 @@ impl TsCompiler {
&source_file.url &source_file.url
); );
let target = "main"; let target = match target {
TargetLib::Main => "main",
TargetLib::Worker => "worker",
};
let root_names = vec![module_url.to_string()]; let root_names = vec![module_url.to_string()];
let req_msg = req( let req_msg = req(
@ -710,7 +720,7 @@ mod tests {
let fut = async move { let fut = async move {
let result = mock_state let result = mock_state
.ts_compiler .ts_compiler
.compile_async(mock_state.clone(), &out) .compile_async(mock_state.clone(), &out, TargetLib::Main)
.await; .await;
assert!(result.is_ok()); assert!(result.is_ok());

View file

@ -2,6 +2,7 @@
use crate::compilers::CompiledModule; use crate::compilers::CompiledModule;
use crate::compilers::JsCompiler; use crate::compilers::JsCompiler;
use crate::compilers::JsonCompiler; use crate::compilers::JsonCompiler;
use crate::compilers::TargetLib;
use crate::compilers::TsCompiler; use crate::compilers::TsCompiler;
use crate::compilers::WasmCompiler; use crate::compilers::WasmCompiler;
use crate::deno_dir; use crate::deno_dir;
@ -122,6 +123,7 @@ impl ThreadSafeGlobalState {
&self, &self,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
maybe_referrer: Option<ModuleSpecifier>, maybe_referrer: Option<ModuleSpecifier>,
target_lib: TargetLib,
) -> impl Future<Output = Result<CompiledModule, ErrBox>> { ) -> impl Future<Output = Result<CompiledModule, ErrBox>> {
let state1 = self.clone(); let state1 = self.clone();
let state2 = self.clone(); let state2 = self.clone();
@ -141,11 +143,15 @@ impl ThreadSafeGlobalState {
msg::MediaType::TypeScript msg::MediaType::TypeScript
| msg::MediaType::TSX | msg::MediaType::TSX
| msg::MediaType::JSX => { | msg::MediaType::JSX => {
state1.ts_compiler.compile_async(state1.clone(), &out) state1
.ts_compiler
.compile_async(state1.clone(), &out, target_lib)
} }
msg::MediaType::JavaScript => { msg::MediaType::JavaScript => {
if state1.ts_compiler.compile_js { if state1.ts_compiler.compile_js {
state1.ts_compiler.compile_async(state1.clone(), &out) state1
.ts_compiler
.compile_async(state1.clone(), &out, target_lib)
} else { } else {
state1.js_compiler.compile_async(&out) state1.js_compiler.compile_async(&out)
} }

View file

@ -16,8 +16,10 @@ pub static COMPILER_SNAPSHOT_MAP: &[u8] =
pub static COMPILER_SNAPSHOT_DTS: &[u8] = pub static COMPILER_SNAPSHOT_DTS: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.d.ts")); include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.d.ts"));
pub static DENO_NS_LIB: &str = include_str!("js/lib.deno.d.ts"); pub static DENO_NS_LIB: &str = include_str!("js/lib.deno.ns.d.ts");
pub static DENO_MAIN_LIB: &str = include_str!("js/lib.deno_main.d.ts"); pub static SHARED_GLOBALS_LIB: &str =
include_str!("js/lib.deno.shared_globals.d.ts");
pub static WINDOW_LIB: &str = include_str!("js/lib.deno.window.d.ts");
#[test] #[test]
fn cli_snapshot() { fn cli_snapshot() {

View file

@ -304,7 +304,7 @@ async function tsCompilerOnMessage({
} }
// The compiler isolate exits after a single message. // The compiler isolate exits after a single message.
globalThis.workerClose(); globalThis.close();
} }
async function wasmCompilerOnMessage({ async function wasmCompilerOnMessage({
@ -332,7 +332,7 @@ async function wasmCompilerOnMessage({
util.log("<<< WASM compile end"); util.log("<<< WASM compile end");
// The compiler isolate exits after a single message. // The compiler isolate exits after a single message.
globalThis.workerClose(); globalThis.close();
} }
function bootstrapTsCompilerRuntime(): void { function bootstrapTsCompilerRuntime(): void {

View file

@ -15,17 +15,27 @@ const options = host.getCompilationSettings();
// This is a hacky way of adding our libs to the libs available in TypeScript() // This is a hacky way of adding our libs to the libs available in TypeScript()
// as these are internal APIs of TypeScript which maintain valid libs // as these are internal APIs of TypeScript which maintain valid libs
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
(ts as any).libs.push("deno_main", "deno_worker", "deno"); (ts as any).libs.push(
(ts as any).libMap.set("deno_main", "lib.deno_main.d.ts"); "deno_ns",
(ts as any).libMap.set("deno_worker", "lib.deno_worker.d.ts"); "deno_window",
(ts as any).libMap.set("deno", "lib.deno.d.ts"); "deno_worker",
"deno_shared_globals"
);
(ts as any).libMap.set("deno_ns", "lib.deno.ns.d.ts");
(ts as any).libMap.set("deno_window", "lib.deno.window.d.ts");
(ts as any).libMap.set("deno_worker", "lib.deno.worker.d.ts");
(ts as any).libMap.set("deno_shared_globals", "lib.deno.shared_globals.d.ts");
/* eslint-enable @typescript-eslint/no-explicit-any */ /* eslint-enable @typescript-eslint/no-explicit-any */
// this pre-populates the cache at snapshot time of our library files, so they // this pre-populates the cache at snapshot time of our library files, so they
// are available in the future when needed. // are available in the future when needed.
host.getSourceFile(`${ASSETS}/lib.deno_main.d.ts`, ts.ScriptTarget.ESNext); host.getSourceFile(`${ASSETS}/lib.deno.ns.d.ts`, ts.ScriptTarget.ESNext);
host.getSourceFile(`${ASSETS}/lib.deno_worker.d.ts`, ts.ScriptTarget.ESNext); host.getSourceFile(`${ASSETS}/lib.deno.window.d.ts`, ts.ScriptTarget.ESNext);
host.getSourceFile(`${ASSETS}/lib.deno.d.ts`, ts.ScriptTarget.ESNext); host.getSourceFile(`${ASSETS}/lib.deno.worker.d.ts`, ts.ScriptTarget.ESNext);
host.getSourceFile(
`${ASSETS}/lib.deno.shared_globals.d.ts`,
ts.ScriptTarget.ESNext
);
/** /**
* This function spins up TS compiler and loads all available libraries * This function spins up TS compiler and loads all available libraries

View file

@ -233,9 +233,9 @@ export class Host implements ts.CompilerHost {
switch (this._target) { switch (this._target) {
case CompilerHostTarget.Main: case CompilerHostTarget.Main:
case CompilerHostTarget.Runtime: case CompilerHostTarget.Runtime:
return `${ASSETS}/lib.deno_main.d.ts`; return `${ASSETS}/lib.deno.window.d.ts`;
case CompilerHostTarget.Worker: case CompilerHostTarget.Worker:
return `${ASSETS}/lib.deno_worker.d.ts`; return `${ASSETS}/lib.deno.worker.d.ts`;
} }
} }

View file

@ -132,7 +132,7 @@ declare global {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
var onmessage: ((e: { data: any }) => Promise<void> | void) | undefined; var onmessage: ((e: { data: any }) => Promise<void> | void) | undefined;
// Called in compiler // Called in compiler
var workerClose: () => void; var close: () => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
var postMessage: (msg: any) => void; var postMessage: (msg: any) => void;
// Assigned to `self` global - compiler // Assigned to `self` global - compiler

View file

@ -3,23 +3,23 @@
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */
/// <reference no-default-lib="true" /> /// <reference no-default-lib="true" />
/// <reference lib="deno" /> /// <reference lib="deno_ns" />
/// <reference lib="esnext" /> /// <reference lib="esnext" />
declare interface Window { // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
window: Window & typeof globalThis;
declare interface WindowOrWorkerGlobalScope {
// methods
atob: typeof __textEncoding.atob; atob: typeof __textEncoding.atob;
btoa: typeof __textEncoding.btoa; btoa: typeof __textEncoding.btoa;
fetch: typeof __fetch.fetch;
clearTimeout: typeof __timers.clearTimeout;
clearInterval: typeof __timers.clearInterval; clearInterval: typeof __timers.clearInterval;
console: __console.Console; clearTimeout: typeof __timers.clearTimeout;
setTimeout: typeof __timers.setTimeout; fetch: typeof __fetch.fetch;
queueMicrotask: (task: () => void) => void;
setInterval: typeof __timers.setInterval; setInterval: typeof __timers.setInterval;
location: __domTypes.Location; setTimeout: typeof __timers.setTimeout;
onload: Function | undefined; // properties
onunload: Function | undefined; console: __console.Console;
crypto: Crypto;
Blob: typeof __blob.DenoBlob; Blob: typeof __blob.DenoBlob;
File: __domTypes.DomFileConstructor; File: __domTypes.DomFileConstructor;
CustomEvent: typeof __customEvent.CustomEvent; CustomEvent: typeof __customEvent.CustomEvent;
@ -34,11 +34,9 @@ declare interface Window {
Request: __domTypes.RequestConstructor; Request: __domTypes.RequestConstructor;
Response: typeof __fetch.Response; Response: typeof __fetch.Response;
performance: __performanceUtil.Performance; performance: __performanceUtil.Performance;
onmessage: (e: { data: any }) => void;
onerror: undefined | typeof onerror;
workerClose: typeof __workerMain.workerClose;
postMessage: typeof __workerMain.postMessage;
Worker: typeof __workers.WorkerImpl; Worker: typeof __workers.WorkerImpl;
location: __domTypes.Location;
addEventListener: ( addEventListener: (
type: string, type: string,
callback: (event: __domTypes.Event) => void | null, callback: (event: __domTypes.Event) => void | null,
@ -50,23 +48,17 @@ declare interface Window {
callback: (event: __domTypes.Event) => void | null, callback: (event: __domTypes.Event) => void | null,
options?: boolean | __domTypes.EventListenerOptions | undefined options?: boolean | __domTypes.EventListenerOptions | undefined
) => void; ) => void;
queueMicrotask: (task: () => void) => void;
Deno: typeof Deno;
} }
declare const window: Window & typeof globalThis;
declare const atob: typeof __textEncoding.atob; declare const atob: typeof __textEncoding.atob;
declare const btoa: typeof __textEncoding.btoa; declare const btoa: typeof __textEncoding.btoa;
declare const fetch: typeof __fetch.fetch;
declare const clearTimeout: typeof __timers.clearTimeout;
declare const clearInterval: typeof __timers.clearInterval; declare const clearInterval: typeof __timers.clearInterval;
declare const console: __console.Console; declare const clearTimeout: typeof __timers.clearTimeout;
declare const setTimeout: typeof __timers.setTimeout; declare const fetch: typeof __fetch.fetch;
declare const setInterval: typeof __timers.setInterval; declare const setInterval: typeof __timers.setInterval;
declare const location: __domTypes.Location; declare const setTimeout: typeof __timers.setTimeout;
declare const onload: Function | undefined;
declare const onunload: Function | undefined; declare const console: __console.Console;
declare const crypto: Crypto;
declare const Blob: typeof __blob.DenoBlob; declare const Blob: typeof __blob.DenoBlob;
declare const File: __domTypes.DomFileConstructor; declare const File: __domTypes.DomFileConstructor;
declare const CustomEventInit: typeof __customEvent.CustomEventInit; declare const CustomEventInit: typeof __customEvent.CustomEventInit;
@ -78,25 +70,15 @@ declare const EventTarget: typeof __eventTarget.EventTarget;
declare const URL: typeof __url.URL; declare const URL: typeof __url.URL;
declare const URLSearchParams: typeof __urlSearchParams.URLSearchParams; declare const URLSearchParams: typeof __urlSearchParams.URLSearchParams;
declare const Headers: __domTypes.HeadersConstructor; declare const Headers: __domTypes.HeadersConstructor;
declare const location: __domTypes.Location;
declare const FormData: __domTypes.FormDataConstructor; declare const FormData: __domTypes.FormDataConstructor;
declare const TextEncoder: typeof __textEncoding.TextEncoder; declare const TextEncoder: typeof __textEncoding.TextEncoder;
declare const TextDecoder: typeof __textEncoding.TextDecoder; declare const TextDecoder: typeof __textEncoding.TextDecoder;
declare const Request: __domTypes.RequestConstructor; declare const Request: __domTypes.RequestConstructor;
declare const Response: typeof __fetch.Response; declare const Response: typeof __fetch.Response;
declare const performance: __performanceUtil.Performance; declare const performance: __performanceUtil.Performance;
declare let onmessage: ((e: { data: any }) => Promise<void> | void) | undefined;
declare let onerror:
| ((
msg: string,
source: string,
lineno: number,
colno: number,
e: Event
) => boolean | void)
| undefined;
declare const workerClose: typeof __workerMain.workerClose;
declare const postMessage: typeof __workerMain.postMessage;
declare const Worker: typeof __workers.WorkerImpl; declare const Worker: typeof __workers.WorkerImpl;
declare const addEventListener: ( declare const addEventListener: (
type: string, type: string,
callback: (event: __domTypes.Event) => void | null, callback: (event: __domTypes.Event) => void | null,
@ -133,198 +115,6 @@ declare interface ImportMeta {
main: boolean; main: boolean;
} }
declare interface Crypto {
readonly subtle: null;
getRandomValues<
T extends
| Int8Array
| Int16Array
| Int32Array
| Uint8Array
| Uint16Array
| Uint32Array
| Uint8ClampedArray
| Float32Array
| Float64Array
| DataView
| null
>(
array: T
): T;
}
// This follows the WebIDL at: https://webassembly.github.io/spec/js-api/
// and: https://webassembly.github.io/spec/web-api/
declare namespace WebAssembly {
interface WebAssemblyInstantiatedSource {
module: Module;
instance: Instance;
}
/** Compiles a `WebAssembly.Module` from WebAssembly binary code. This
* function is useful if it is necessary to a compile a module before it can
* be instantiated (otherwise, the `WebAssembly.instantiate()` function
* should be used). */
function compile(bufferSource: __domTypes.BufferSource): Promise<Module>;
/** Compiles a `WebAssembly.Module` directly from a streamed underlying
* source. This function is useful if it is necessary to a compile a module
* before it can be instantiated (otherwise, the
* `WebAssembly.instantiateStreaming()` function should be used). */
function compileStreaming(
source: Promise<__domTypes.Response>
): Promise<Module>;
/** Takes the WebAssembly binary code, in the form of a typed array or
* `ArrayBuffer`, and performs both compilation and instantiation in one step.
* The returned `Promise` resolves to both a compiled `WebAssembly.Module` and
* its first `WebAssembly.Instance`. */
function instantiate(
bufferSource: __domTypes.BufferSource,
importObject?: object
): Promise<WebAssemblyInstantiatedSource>;
/** Takes an already-compiled `WebAssembly.Module` and returns a `Promise`
* that resolves to an `Instance` of that `Module`. This overload is useful if
* the `Module` has already been compiled. */
function instantiate(
module: Module,
importObject?: object
): Promise<Instance>;
/** Compiles and instantiates a WebAssembly module directly from a streamed
* underlying source. This is the most efficient, optimized way to load wasm
* code. */
function instantiateStreaming(
source: Promise<__domTypes.Response>,
importObject?: object
): Promise<WebAssemblyInstantiatedSource>;
/** Validates a given typed array of WebAssembly binary code, returning
* whether the bytes form a valid wasm module (`true`) or not (`false`). */
function validate(bufferSource: __domTypes.BufferSource): boolean;
type ImportExportKind = "function" | "table" | "memory" | "global";
interface ModuleExportDescriptor {
name: string;
kind: ImportExportKind;
}
interface ModuleImportDescriptor {
module: string;
name: string;
kind: ImportExportKind;
}
class Module {
constructor(bufferSource: __domTypes.BufferSource);
/** Given a `Module` and string, returns a copy of the contents of all
* custom sections in the module with the given string name. */
static customSections(
moduleObject: Module,
sectionName: string
): ArrayBuffer;
/** Given a `Module`, returns an array containing descriptions of all the
* declared exports. */
static exports(moduleObject: Module): ModuleExportDescriptor[];
/** Given a `Module`, returns an array containing descriptions of all the
* declared imports. */
static imports(moduleObject: Module): ModuleImportDescriptor[];
}
class Instance<T extends object = { [key: string]: any }> {
constructor(module: Module, importObject?: object);
/** An object containing as its members all the functions exported from the
* WebAssembly module instance, to allow them to be accessed and used by
* JavaScript. */
readonly exports: T;
}
interface MemoryDescriptor {
initial: number;
maximum?: number;
}
class Memory {
constructor(descriptor: MemoryDescriptor);
/** An accessor property that returns the buffer contained in the memory. */
readonly buffer: ArrayBuffer;
/** Increases the size of the memory instance by a specified number of
* WebAssembly pages (each one is 64KB in size). */
grow(delta: number): number;
}
type TableKind = "anyfunc";
interface TableDescriptor {
element: TableKind;
initial: number;
maximum?: number;
}
class Table {
constructor(descriptor: TableDescriptor);
/** Returns the length of the table, i.e. the number of elements. */
readonly length: number;
/** Accessor function — gets the element stored at a given index. */
get(index: number): (...args: any[]) => any;
/** Increases the size of the Table instance by a specified number of
* elements. */
grow(delta: number): number;
/** Sets an element stored at a given index to a given value. */
set(index: number, value: (...args: any[]) => any): void;
}
type ValueType = "i32" | "i64" | "f32" | "f64";
interface GlobalDescriptor {
value: ValueType;
mutable?: boolean;
}
/** Represents a global variable instance, accessible from both JavaScript and
* importable/exportable across one or more `WebAssembly.Module` instances.
* This allows dynamic linking of multiple modules. */
class Global {
constructor(descriptor: GlobalDescriptor, value?: any);
/** Old-style method that returns the value contained inside the global
* variable. */
valueOf(): any;
/** The value contained inside the global variable this can be used to
* directly set and get the global's value. */
value: any;
}
/** Indicates an error during WebAssembly decoding or validation */
class CompileError extends Error {
constructor(message: string, fileName?: string, lineNumber?: string);
}
/** Indicates an error during module instantiation (besides traps from the
* start function). */
class LinkError extends Error {
constructor(message: string, fileName?: string, lineNumber?: string);
}
/** Is thrown whenever WebAssembly specifies a trap. */
class RuntimeError extends Error {
constructor(message: string, fileName?: string, lineNumber?: string);
}
}
declare namespace __domTypes { declare namespace __domTypes {
// @url js/dom_types.d.ts // @url js/dom_types.d.ts
@ -1519,13 +1309,6 @@ declare namespace __url {
}; };
} }
declare namespace __workerMain {
export let onmessage: (e: { data: any }) => void;
export function postMessage(data: any): void;
export function getMessage(): Promise<any>;
export function workerClose(): void;
}
declare namespace __workers { declare namespace __workers {
// @url js/workers.d.ts // @url js/workers.d.ts
export interface Worker { export interface Worker {
@ -1533,9 +1316,11 @@ declare namespace __workers {
onmessage?: (e: { data: any }) => void; onmessage?: (e: { data: any }) => void;
onmessageerror?: () => void; onmessageerror?: () => void;
postMessage(data: any): void; postMessage(data: any): void;
terminate(): void;
} }
export interface WorkerOptions { export interface WorkerOptions {
type?: "classic" | "module"; type?: "classic" | "module";
name?: string;
} }
export class WorkerImpl implements Worker { export class WorkerImpl implements Worker {
private readonly id; private readonly id;
@ -1546,6 +1331,7 @@ declare namespace __workers {
onmessageerror?: () => void; onmessageerror?: () => void;
constructor(specifier: string, options?: WorkerOptions); constructor(specifier: string, options?: WorkerOptions);
postMessage(data: any): void; postMessage(data: any): void;
terminate(): void;
private run; private run;
} }
} }

214
cli/js/lib.deno.window.d.ts vendored Normal file
View file

@ -0,0 +1,214 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */
/// <reference no-default-lib="true" />
/// <reference lib="deno_ns" />
/// <reference lib="deno_shared_globals" />
/// <reference lib="esnext" />
declare interface Window extends WindowOrWorkerGlobalScope {
window: Window & WindowOrWorkerGlobalScope & typeof globalThis;
onload: Function | undefined;
onunload: Function | undefined;
crypto: Crypto;
Deno: typeof Deno;
}
declare const window: Window & WindowOrWorkerGlobalScope & typeof globalThis;
declare const onload: Function | undefined;
declare const onunload: Function | undefined;
declare const crypto: Crypto;
declare interface Crypto {
readonly subtle: null;
getRandomValues<
T extends
| Int8Array
| Int16Array
| Int32Array
| Uint8Array
| Uint16Array
| Uint32Array
| Uint8ClampedArray
| Float32Array
| Float64Array
| DataView
| null
>(
array: T
): T;
}
// This follows the WebIDL at: https://webassembly.github.io/spec/js-api/
// and: https://webassembly.github.io/spec/web-api/
declare namespace WebAssembly {
interface WebAssemblyInstantiatedSource {
module: Module;
instance: Instance;
}
/** Compiles a `WebAssembly.Module` from WebAssembly binary code. This
* function is useful if it is necessary to a compile a module before it can
* be instantiated (otherwise, the `WebAssembly.instantiate()` function
* should be used). */
function compile(bufferSource: __domTypes.BufferSource): Promise<Module>;
/** Compiles a `WebAssembly.Module` directly from a streamed underlying
* source. This function is useful if it is necessary to a compile a module
* before it can be instantiated (otherwise, the
* `WebAssembly.instantiateStreaming()` function should be used). */
function compileStreaming(
source: Promise<__domTypes.Response>
): Promise<Module>;
/** Takes the WebAssembly binary code, in the form of a typed array or
* `ArrayBuffer`, and performs both compilation and instantiation in one step.
* The returned `Promise` resolves to both a compiled `WebAssembly.Module` and
* its first `WebAssembly.Instance`. */
function instantiate(
bufferSource: __domTypes.BufferSource,
importObject?: object
): Promise<WebAssemblyInstantiatedSource>;
/** Takes an already-compiled `WebAssembly.Module` and returns a `Promise`
* that resolves to an `Instance` of that `Module`. This overload is useful if
* the `Module` has already been compiled. */
function instantiate(
module: Module,
importObject?: object
): Promise<Instance>;
/** Compiles and instantiates a WebAssembly module directly from a streamed
* underlying source. This is the most efficient, optimized way to load wasm
* code. */
function instantiateStreaming(
source: Promise<__domTypes.Response>,
importObject?: object
): Promise<WebAssemblyInstantiatedSource>;
/** Validates a given typed array of WebAssembly binary code, returning
* whether the bytes form a valid wasm module (`true`) or not (`false`). */
function validate(bufferSource: __domTypes.BufferSource): boolean;
type ImportExportKind = "function" | "table" | "memory" | "global";
interface ModuleExportDescriptor {
name: string;
kind: ImportExportKind;
}
interface ModuleImportDescriptor {
module: string;
name: string;
kind: ImportExportKind;
}
class Module {
constructor(bufferSource: __domTypes.BufferSource);
/** Given a `Module` and string, returns a copy of the contents of all
* custom sections in the module with the given string name. */
static customSections(
moduleObject: Module,
sectionName: string
): ArrayBuffer;
/** Given a `Module`, returns an array containing descriptions of all the
* declared exports. */
static exports(moduleObject: Module): ModuleExportDescriptor[];
/** Given a `Module`, returns an array containing descriptions of all the
* declared imports. */
static imports(moduleObject: Module): ModuleImportDescriptor[];
}
class Instance<T extends object = { [key: string]: any }> {
constructor(module: Module, importObject?: object);
/** An object containing as its members all the functions exported from the
* WebAssembly module instance, to allow them to be accessed and used by
* JavaScript. */
readonly exports: T;
}
interface MemoryDescriptor {
initial: number;
maximum?: number;
}
class Memory {
constructor(descriptor: MemoryDescriptor);
/** An accessor property that returns the buffer contained in the memory. */
readonly buffer: ArrayBuffer;
/** Increases the size of the memory instance by a specified number of
* WebAssembly pages (each one is 64KB in size). */
grow(delta: number): number;
}
type TableKind = "anyfunc";
interface TableDescriptor {
element: TableKind;
initial: number;
maximum?: number;
}
class Table {
constructor(descriptor: TableDescriptor);
/** Returns the length of the table, i.e. the number of elements. */
readonly length: number;
/** Accessor function — gets the element stored at a given index. */
get(index: number): (...args: any[]) => any;
/** Increases the size of the Table instance by a specified number of
* elements. */
grow(delta: number): number;
/** Sets an element stored at a given index to a given value. */
set(index: number, value: (...args: any[]) => any): void;
}
type ValueType = "i32" | "i64" | "f32" | "f64";
interface GlobalDescriptor {
value: ValueType;
mutable?: boolean;
}
/** Represents a global variable instance, accessible from both JavaScript and
* importable/exportable across one or more `WebAssembly.Module` instances.
* This allows dynamic linking of multiple modules. */
class Global {
constructor(descriptor: GlobalDescriptor, value?: any);
/** Old-style method that returns the value contained inside the global
* variable. */
valueOf(): any;
/** The value contained inside the global variable this can be used to
* directly set and get the global's value. */
value: any;
}
/** Indicates an error during WebAssembly decoding or validation */
class CompileError extends Error {
constructor(message: string, fileName?: string, lineNumber?: string);
}
/** Indicates an error during module instantiation (besides traps from the
* start function). */
class LinkError extends Error {
constructor(message: string, fileName?: string, lineNumber?: string);
}
/** Is thrown whenever WebAssembly specifies a trap. */
class RuntimeError extends Error {
constructor(message: string, fileName?: string, lineNumber?: string);
}
}
/* eslint-enable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */

45
cli/js/lib.deno.worker.d.ts vendored Normal file
View file

@ -0,0 +1,45 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */
/// <reference no-default-lib="true" />
/// <reference lib="deno_shared_globals" />
/// <reference lib="esnext" />
declare interface DedicatedWorkerGlobalScope extends WindowOrWorkerGlobalScope {
self: DedicatedWorkerGlobalScope &
WindowOrWorkerGlobalScope &
typeof globalThis;
onmessage: (e: { data: any }) => void;
onerror: undefined | typeof onerror;
name: typeof __workerMain.name;
close: typeof __workerMain.close;
postMessage: typeof __workerMain.postMessage;
}
declare const self: DedicatedWorkerGlobalScope &
WindowOrWorkerGlobalScope &
typeof globalThis;
declare let onmessage: ((e: { data: any }) => Promise<void> | void) | undefined;
declare let onerror:
| ((
msg: string,
source: string,
lineno: number,
colno: number,
e: Event
) => boolean | void)
| undefined;
declare const close: typeof __workerMain.close;
declare const name: typeof __workerMain.name;
declare const postMessage: typeof __workerMain.postMessage;
declare namespace __workerMain {
export let onmessage: (e: { data: any }) => void;
export function postMessage(data: any): void;
export function getMessage(): Promise<any>;
export function close(): void;
export const name: string;
}
/* eslint-enable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */

View file

@ -1,5 +0,0 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
/// <reference no-default-lib="true" />
/// <reference lib="deno" />
/// <reference lib="webworker" />

View file

@ -54,7 +54,7 @@ export async function getMessage(): Promise<any> {
let isClosing = false; let isClosing = false;
let hasBootstrapped = false; let hasBootstrapped = false;
export function workerClose(): void { export function close(): void {
isClosing = true; isClosing = true;
} }
@ -102,7 +102,7 @@ export const workerRuntimeGlobalProperties = {
self: readOnly(globalThis), self: readOnly(globalThis),
onmessage: writable(onmessage), onmessage: writable(onmessage),
onerror: writable(onerror), onerror: writable(onerror),
workerClose: nonEnumerable(workerClose), close: nonEnumerable(close),
postMessage: writable(postMessage) postMessage: writable(postMessage)
}; };
@ -122,5 +122,6 @@ export function bootstrapWorkerRuntime(name: string): void {
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeProperties); Object.defineProperties(globalThis, windowOrWorkerGlobalScopeProperties);
Object.defineProperties(globalThis, workerRuntimeGlobalProperties); Object.defineProperties(globalThis, workerRuntimeGlobalProperties);
Object.defineProperties(globalThis, eventTargetProperties); Object.defineProperties(globalThis, eventTargetProperties);
Object.defineProperties(globalThis, { name: readOnly(name) });
runtime.start(false, name); runtime.start(false, name);
} }

View file

@ -27,12 +27,14 @@ function decodeMessage(dataIntArray: Uint8Array): any {
function createWorker( function createWorker(
specifier: string, specifier: string,
hasSourceCode: boolean, hasSourceCode: boolean,
sourceCode: Uint8Array sourceCode: Uint8Array,
name?: string
): { id: number; loaded: boolean } { ): { id: number; loaded: boolean } {
return sendSync(dispatch.OP_CREATE_WORKER, { return sendSync(dispatch.OP_CREATE_WORKER, {
specifier, specifier,
hasSourceCode, hasSourceCode,
sourceCode: new TextDecoder().decode(sourceCode) sourceCode: new TextDecoder().decode(sourceCode),
name
}); });
} }
@ -72,10 +74,12 @@ export interface Worker {
onmessage?: (e: { data: any }) => void; onmessage?: (e: { data: any }) => void;
onmessageerror?: () => void; onmessageerror?: () => void;
postMessage(data: any): void; postMessage(data: any): void;
terminate(): void;
} }
export interface WorkerOptions { export interface WorkerOptions {
type?: "classic" | "module"; type?: "classic" | "module";
name?: string;
} }
export class WorkerImpl extends EventTarget implements Worker { export class WorkerImpl extends EventTarget implements Worker {
@ -121,7 +125,12 @@ export class WorkerImpl extends EventTarget implements Worker {
} }
*/ */
const { id, loaded } = createWorker(specifier, hasSourceCode, sourceCode); const { id, loaded } = createWorker(
specifier,
hasSourceCode,
sourceCode,
options?.name
);
this.id = id; this.id = id;
this.ready = loaded; this.ready = loaded;
this.poll(); this.poll();
@ -196,6 +205,10 @@ export class WorkerImpl extends EventTarget implements Worker {
hostPostMessage(this.id, data); hostPostMessage(this.id, data);
} }
terminate(): void {
throw new Error("Not yet implemented");
}
private async run(): Promise<void> { private async run(): Promise<void> {
while (!this.isClosing) { while (!this.isClosing) {
const data = await hostGetMessage(this.id); const data = await hostGetMessage(this.id);

View file

@ -54,6 +54,7 @@ pub mod version;
mod web_worker; mod web_worker;
pub mod worker; pub mod worker;
use crate::compilers::TargetLib;
use crate::deno_error::js_check; use crate::deno_error::js_check;
use crate::deno_error::{print_err_and_exit, print_msg_and_exit}; use crate::deno_error::{print_err_and_exit, print_msg_and_exit};
use crate::global_state::ThreadSafeGlobalState; use crate::global_state::ThreadSafeGlobalState;
@ -146,7 +147,12 @@ fn create_worker_and_state(
} }
fn types_command() { fn types_command() {
println!("{}\n{}", crate::js::DENO_NS_LIB, crate::js::DENO_MAIN_LIB); println!(
"{}\n{}\n{}",
crate::js::DENO_NS_LIB,
crate::js::SHARED_GLOBALS_LIB,
crate::js::WINDOW_LIB
);
} }
fn print_cache_info(worker: MainWorker) { fn print_cache_info(worker: MainWorker) {
@ -198,7 +204,7 @@ async fn print_file_info(
let maybe_compiled = global_state_ let maybe_compiled = global_state_
.clone() .clone()
.fetch_compiled_module(&module_specifier, None) .fetch_compiled_module(&module_specifier, None, TargetLib::Main)
.await; .await;
if let Err(e) = maybe_compiled { if let Err(e) = maybe_compiled {
debug!("compiler error exiting!"); debug!("compiler error exiting!");

View file

@ -4,7 +4,6 @@ use crate::deno_error::bad_resource;
use crate::deno_error::js_check; use crate::deno_error::js_check;
use crate::deno_error::DenoError; use crate::deno_error::DenoError;
use crate::deno_error::ErrorKind; use crate::deno_error::ErrorKind;
use crate::deno_error::GetErrorKind;
use crate::fmt_errors::JSError; use crate::fmt_errors::JSError;
use crate::ops::json_op; use crate::ops::json_op;
use crate::startup_data; use crate::startup_data;
@ -60,6 +59,7 @@ pub fn init(i: &mut Isolate, s: &ThreadSafeState) {
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct CreateWorkerArgs { struct CreateWorkerArgs {
name: Option<String>,
specifier: String, specifier: String,
has_source_code: bool, has_source_code: bool,
source_code: String, source_code: String,
@ -89,23 +89,28 @@ fn op_create_worker(
} }
let (int, ext) = ThreadSafeState::create_channels(); let (int, ext) = ThreadSafeState::create_channels();
let child_state = ThreadSafeState::new( let child_state = ThreadSafeState::new_for_worker(
state.global_state.clone(), state.global_state.clone(),
Some(parent_state.permissions.clone()), // by default share with parent Some(parent_state.permissions.clone()), // by default share with parent
Some(module_specifier.clone()), Some(module_specifier.clone()),
int, int,
)?; )?;
let worker_name = if let Some(name) = args.name {
name
} else {
// TODO(bartlomieju): change it to something more descriptive
format!("USER-WORKER-{}", specifier)
};
// TODO: add a new option to make child worker not sharing permissions // TODO: add a new option to make child worker not sharing permissions
// with parent (aka .clone(), requests from child won't reflect in parent) // with parent (aka .clone(), requests from child won't reflect in parent)
// TODO(bartlomieju): get it from "name" argument when creating worker
let name = format!("USER-WORKER-{}", specifier);
let mut worker = WebWorker::new( let mut worker = WebWorker::new(
name.to_string(), worker_name.to_string(),
startup_data::deno_isolate_init(), startup_data::deno_isolate_init(),
child_state, child_state,
ext, ext,
); );
let script = format!("bootstrapWorkerRuntime(\"{}\")", name); let script = format!("bootstrapWorkerRuntime(\"{}\")", worker_name);
js_check(worker.execute(&script)); js_check(worker.execute(&script));
js_check(worker.execute("runWorkerMessageLoop()")); js_check(worker.execute("runWorkerMessageLoop()"));
@ -158,6 +163,8 @@ impl Future for WorkerPollFuture {
} }
fn serialize_worker_result(result: Result<(), ErrBox>) -> Value { fn serialize_worker_result(result: Result<(), ErrBox>) -> Value {
use crate::deno_error::GetErrorKind;
if let Err(error) = result { if let Err(error) = result {
match error.kind() { match error.kind() {
ErrorKind::JSError => { ErrorKind::JSError => {

View file

@ -1,4 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::compilers::TargetLib;
use crate::deno_error::permission_denied; use crate::deno_error::permission_denied;
use crate::global_state::ThreadSafeGlobalState; use crate::global_state::ThreadSafeGlobalState;
use crate::global_timer::GlobalTimer; use crate::global_timer::GlobalTimer;
@ -59,6 +60,7 @@ pub struct State {
pub start_time: Instant, pub start_time: Instant,
pub seeded_rng: Option<Mutex<StdRng>>, pub seeded_rng: Option<Mutex<StdRng>>,
pub resource_table: Mutex<ResourceTable>, pub resource_table: Mutex<ResourceTable>,
pub target_lib: TargetLib,
} }
impl Clone for ThreadSafeState { impl Clone for ThreadSafeState {
@ -200,7 +202,7 @@ impl Loader for ThreadSafeState {
let module_url_specified = module_specifier.to_string(); let module_url_specified = module_specifier.to_string();
let fut = self let fut = self
.global_state .global_state
.fetch_compiled_module(module_specifier, maybe_referrer) .fetch_compiled_module(module_specifier, maybe_referrer, self.target_lib)
.map_ok(|compiled_module| deno_core::SourceCodeInfo { .map_ok(|compiled_module| deno_core::SourceCodeInfo {
// Real module name, might be different from initial specifier // Real module name, might be different from initial specifier
// due to redirections. // due to redirections.
@ -228,9 +230,9 @@ impl ThreadSafeState {
(internal_channels, external_channels) (internal_channels, external_channels)
} }
/// If `shared_permission` is None then permissions from globa state are used.
pub fn new( pub fn new(
global_state: ThreadSafeGlobalState, global_state: ThreadSafeGlobalState,
// If Some(perm), use perm. Else copy from global_state.
shared_permissions: Option<Arc<Mutex<DenoPermissions>>>, shared_permissions: Option<Arc<Mutex<DenoPermissions>>>,
main_module: Option<ModuleSpecifier>, main_module: Option<ModuleSpecifier>,
internal_channels: WorkerChannels, internal_channels: WorkerChannels,
@ -267,6 +269,46 @@ impl ThreadSafeState {
seeded_rng, seeded_rng,
resource_table: Mutex::new(ResourceTable::default()), resource_table: Mutex::new(ResourceTable::default()),
target_lib: TargetLib::Main,
};
Ok(ThreadSafeState(Arc::new(state)))
}
/// If `shared_permission` is None then permissions from globa state are used.
pub fn new_for_worker(
global_state: ThreadSafeGlobalState,
shared_permissions: Option<Arc<Mutex<DenoPermissions>>>,
main_module: Option<ModuleSpecifier>,
internal_channels: WorkerChannels,
) -> Result<Self, ErrBox> {
let seeded_rng = match global_state.flags.seed {
Some(seed) => Some(Mutex::new(StdRng::seed_from_u64(seed))),
None => None,
};
let permissions = if let Some(perm) = shared_permissions {
perm
} else {
Arc::new(Mutex::new(global_state.permissions.clone()))
};
let state = State {
global_state,
main_module,
permissions,
import_map: None,
worker_channels: internal_channels,
metrics: Metrics::default(),
global_timer: Mutex::new(GlobalTimer::new()),
workers: Mutex::new(HashMap::new()),
loading_workers: Mutex::new(HashMap::new()),
next_worker_id: AtomicUsize::new(0),
start_time: Instant::now(),
seeded_rng,
resource_table: Mutex::new(ResourceTable::default()),
target_lib: TargetLib::Worker,
}; };
Ok(ThreadSafeState(Arc::new(state))) Ok(ThreadSafeState(Arc::new(state)))

View file

@ -1,5 +1,11 @@
const jsWorker = new Worker("./subdir/test_worker.js", { type: "module" }); const jsWorker = new Worker("./subdir/test_worker.js", {
const tsWorker = new Worker("./subdir/test_worker.ts", { type: "module" }); type: "module",
name: "jsWorker"
});
const tsWorker = new Worker("./subdir/test_worker.ts", {
type: "module",
name: "tsWorker"
});
tsWorker.onmessage = (e): void => { tsWorker.onmessage = (e): void => {
console.log("Received ts: " + e.data); console.log("Received ts: " + e.data);

View file

@ -15,7 +15,7 @@ onmessage = function(e): void {
break; break;
case 3: // Close case 3: // Close
postMessage({ cmdId: 3 }); postMessage({ cmdId: 3 });
workerClose(); close();
break; break;
} }
}; };

View file

@ -1,5 +1,10 @@
let thrown = false; let thrown = false;
// TODO(bartlomieju): add test for throwing in web worker
if (self.name !== "jsWorker") {
throw Error(`Bad worker name: ${self.name}, expected jsWorker`);
}
onmessage = function(e) { onmessage = function(e) {
console.log(e.data); console.log(e.data);
@ -10,7 +15,7 @@ onmessage = function(e) {
postMessage(e.data); postMessage(e.data);
workerClose(); close();
}; };
onerror = function() { onerror = function() {

View file

@ -1,7 +1,11 @@
if (self.name !== "tsWorker") {
throw Error(`Invalid worker name: ${self.name}, expected tsWorker`);
}
onmessage = function(e): void { onmessage = function(e): void {
console.log(e.data); console.log(e.data);
postMessage(e.data); postMessage(e.data);
workerClose(); close();
}; };

View file

@ -5,10 +5,12 @@ declare namespace Deno {
[WILDCARD] [WILDCARD]
} }
[WILDCARD] [WILDCARD]
declare interface Window { declare interface WindowOrWorkerGlobalScope {
[WILDCARD]
declare interface Window extends WindowOrWorkerGlobalScope {
[WILDCARD] [WILDCARD]
Deno: typeof Deno; Deno: typeof Deno;
} }
declare const window: Window & typeof globalThis; declare const window: Window & WindowOrWorkerGlobalScope & typeof globalThis;
[WILDCARD] [WILDCARD]