From e67826f6bd836f811fd3ffb38706fca4fa145028 Mon Sep 17 00:00:00 2001 From: Ian Thomas Date: Thu, 9 Jan 2025 15:38:55 +0000 Subject: [PATCH] Display wasm module cache status in cockle-config (#92) * Display wasm module cache status in cockle-config * Linting --- src/builtin/cockle_config_command.ts | 23 +++++++++++++++++++ src/command_registry.ts | 16 +++++++++++-- src/commands/wasm_command_module.ts | 11 +++++---- src/commands/wasm_command_runner.ts | 8 +++---- src/context.ts | 4 +++- src/shell_impl.ts | 18 ++++++++------- src/wasm_module_cache.ts | 15 ++++++++++++ src/{wasm_loader.ts => wasm_module_loader.ts} | 10 ++++---- 8 files changed, 81 insertions(+), 24 deletions(-) create mode 100644 src/wasm_module_cache.ts rename src/{wasm_loader.ts => wasm_module_loader.ts} (73%) diff --git a/src/builtin/cockle_config_command.ts b/src/builtin/cockle_config_command.ts index fd813d1..e2f34ed 100644 --- a/src/builtin/cockle_config_command.ts +++ b/src/builtin/cockle_config_command.ts @@ -15,10 +15,33 @@ export class CockleConfigCommand extends BuiltinCommand { stdout.write(`cockle ${COCKLE_VERSION}\n`); this._writePackageConfig(context); + this._writeModuleConfig(context); return ExitCode.SUCCESS; } + private _writeModuleConfig(context: Context) { + const { commandRegistry, stdout, wasmModuleCache } = context; + + const allModules = commandRegistry.allModules(); + + const lines = [['module', 'package', 'cached']]; + for (const module of allModules) { + lines.push([module.name, module.packageName, wasmModuleCache.has(module.name) ? 'yes' : '']); + } + + let colorMap: Map | null = null; + if (stdout.supportsAnsiEscapes()) { + colorMap = new Map(); + colorMap.set(1, ansi.styleBrightBlue); + colorMap.set(2, ansi.styleBrightPurple); + } + + for (const line of toTable(lines, 1, false, colorMap)) { + stdout.write(line + '\n'); + } + } + private _writePackageConfig(context: Context) { const { commandRegistry, stdout } = context; diff --git a/src/command_registry.ts b/src/command_registry.ts index c8197f0..b5e4f11 100644 --- a/src/command_registry.ts +++ b/src/command_registry.ts @@ -1,13 +1,25 @@ import { ICommandRunner } from './commands/command_runner'; +import { WasmCommandModule } from './commands/wasm_command_module'; import { WasmCommandPackage } from './commands/wasm_command_package'; import * as AllBuiltinCommands from './builtin'; -import { WasmLoader } from './wasm_loader'; export class CommandRegistry { - constructor(wasmLoader: WasmLoader) { + constructor() { this.registerBuiltinCommands(AllBuiltinCommands); } + /** + * Return sequence of all wasm modules ordered by module name. + */ + allModules(): WasmCommandModule[] { + const modules: WasmCommandModule[] = []; + for (const pkg of this.wasmPackageMap.values()) { + modules.push(...pkg.modules); + } + modules.sort((a, b) => (a.name < b.name ? -1 : 1)); + return modules; + } + get(name: string): ICommandRunner | null { return this._map.get(name) ?? null; } diff --git a/src/commands/wasm_command_module.ts b/src/commands/wasm_command_module.ts index 2d8de5c..8560597 100644 --- a/src/commands/wasm_command_module.ts +++ b/src/commands/wasm_command_module.ts @@ -1,5 +1,5 @@ import { WasmCommandRunner } from './wasm_command_runner'; -import { WasmLoader } from '../wasm_loader'; +import { WasmModuleLoader } from '../wasm_module_loader'; /** * A moduleName.wasm file with its associated moduleName.js wrapper. @@ -7,11 +7,12 @@ import { WasmLoader } from '../wasm_loader'; */ export class WasmCommandModule extends WasmCommandRunner { constructor( - wasmLoader: WasmLoader, - private readonly name: string, - private readonly commands: string[] + wasmModuleLoader: WasmModuleLoader, + readonly name: string, + private readonly commands: string[], + readonly packageName: string ) { - super(wasmLoader); + super(wasmModuleLoader); } moduleName(): string { diff --git a/src/commands/wasm_command_runner.ts b/src/commands/wasm_command_runner.ts index a3a2ea9..fe2c70a 100644 --- a/src/commands/wasm_command_runner.ts +++ b/src/commands/wasm_command_runner.ts @@ -2,10 +2,10 @@ import { ICommandRunner } from './command_runner'; import { Context } from '../context'; import { ExitCode } from '../exit_code'; import { ITermios } from '../termios'; -import { WasmLoader } from '../wasm_loader'; +import { WasmModuleLoader } from '../wasm_module_loader'; export abstract class WasmCommandRunner implements ICommandRunner { - constructor(readonly wasmLoader: WasmLoader) {} + constructor(readonly wasmModuleLoader: WasmModuleLoader) {} abstract moduleName(): string; @@ -13,10 +13,10 @@ export abstract class WasmCommandRunner implements ICommandRunner { async run(cmdName: string, context: Context): Promise { const { args, bufferedIO, fileSystem, mountpoint, stdin, stdout, stderr } = context; - const { wasmBaseUrl } = this.wasmLoader; + const { wasmBaseUrl } = this.wasmModuleLoader; const start = Date.now(); - const wasmModule = this.wasmLoader.getModule(this.moduleName()); + const wasmModule = this.wasmModuleLoader.getModule(this.moduleName()); let _getCharBuffer: number[] = []; diff --git a/src/context.ts b/src/context.ts index b4a7181..899f900 100644 --- a/src/context.ts +++ b/src/context.ts @@ -6,6 +6,7 @@ import { Environment } from './environment'; import { History } from './history'; import { IFileSystem } from './file_system'; import { IInput, IOutput } from './io'; +import { WasmModuleCache } from './wasm_module_cache'; /** * Context used to run commands. @@ -23,7 +24,8 @@ export class Context { readonly stdin: IInput, readonly stdout: IOutput, readonly stderr: IOutput, - readonly bufferedIO: WorkerBufferedIO + readonly bufferedIO: WorkerBufferedIO, + readonly wasmModuleCache: WasmModuleCache ) {} flush(): void { diff --git a/src/shell_impl.ts b/src/shell_impl.ts index 4f7c182..6a7faa4 100644 --- a/src/shell_impl.ts +++ b/src/shell_impl.ts @@ -13,7 +13,7 @@ import { History } from './history'; import { FileInput, FileOutput, IInput, IOutput, Pipe, TerminalInput, TerminalOutput } from './io'; import { CommandNode, PipeNode, parse } from './parse'; import { longestStartsWith, toColumns } from './utils'; -import { WasmLoader } from './wasm_loader'; +import { WasmModuleLoader } from './wasm_module_loader'; import { WasmCommandModule } from './commands/wasm_command_module'; import { WasmCommandPackage } from './commands/wasm_command_package'; @@ -23,8 +23,8 @@ import { WasmCommandPackage } from './commands/wasm_command_package'; export class ShellImpl implements IShellWorker { constructor(readonly options: IShellImpl.IOptions) { this._environment = new Environment(options.color); - this._wasmLoader = new WasmLoader(options.wasmBaseUrl); - this._commandRegistry = new CommandRegistry(this._wasmLoader); + this._wasmModuleLoader = new WasmModuleLoader(options.wasmBaseUrl); + this._commandRegistry = new CommandRegistry(); } get aliases(): Aliases { @@ -266,7 +266,7 @@ export class ShellImpl implements IShellWorker { private async _initFilesystem(): Promise { const { wasmBaseUrl } = this.options; - const fsModule = this._wasmLoader.getModule('fs'); + const fsModule = this._wasmModuleLoader.getModule('fs'); const module = await fsModule({ locateFile: (path: string) => wasmBaseUrl + path }); @@ -326,9 +326,10 @@ export class ShellImpl implements IShellWorker { const commandModules = pkgConfig.modules.map( (moduleConfig: any) => new WasmCommandModule( - this._wasmLoader, + this._wasmModuleLoader, moduleConfig.name, - moduleConfig.commands ? moduleConfig.commands.split(',') : [] + moduleConfig.commands ? moduleConfig.commands.split(',') : [], + pkgConfig.package ) ); const commandPackage = new WasmCommandPackage( @@ -460,7 +461,8 @@ export class ShellImpl implements IShellWorker { input, output, error, - this.options.bufferedIO + this.options.bufferedIO, + this._wasmModuleLoader.cache ); const exitCode = await runner.run(name, context); @@ -562,7 +564,7 @@ export class ShellImpl implements IShellWorker { private _commandRegistry: CommandRegistry; private _environment: Environment; private _history = new History(); - private _wasmLoader: WasmLoader; + private _wasmModuleLoader: WasmModuleLoader; private _fileSystem?: IFileSystem; private _driveFS?: DriveFS; diff --git a/src/wasm_module_cache.ts b/src/wasm_module_cache.ts new file mode 100644 index 0000000..9c8d404 --- /dev/null +++ b/src/wasm_module_cache.ts @@ -0,0 +1,15 @@ +export class WasmModuleCache { + get(moduleName: string): any { + return this._cache.get(moduleName) ?? undefined; + } + + has(moduleName: string): boolean { + return this._cache.has(moduleName); + } + + set(moduleName: string, module: any): void { + this._cache.set(moduleName, module); + } + + private _cache = new Map(); +} diff --git a/src/wasm_loader.ts b/src/wasm_module_loader.ts similarity index 73% rename from src/wasm_loader.ts rename to src/wasm_module_loader.ts index 70c57ad..9fb3bc9 100644 --- a/src/wasm_loader.ts +++ b/src/wasm_module_loader.ts @@ -1,22 +1,24 @@ +import { WasmModuleCache } from './wasm_module_cache'; + /** * Loader of WASM modules. Once loaded, a module is cached so that it is faster to subsequently. * Must be run in a WebWorker. */ -export class WasmLoader { +export class WasmModuleLoader { constructor(readonly wasmBaseUrl: string) {} public getModule(name: string): any { - let module = this._cache.get(name); + let module = this.cache.get(name); if (module === undefined) { // Maybe should use @jupyterlab/coreutils.URLExt to combine URL components. const url = this.wasmBaseUrl + name + '.js'; console.log('Importing JS/WASM from ' + url); importScripts(url); module = (self as any).Module; - this._cache.set(name, module); + this.cache.set(name, module); } return module; } - private _cache = new Map(); + cache = new WasmModuleCache(); }