From ccab310a396003a1a9ff0b90d68077d77ac09c73 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 5 Apr 2026 13:49:03 +0200 Subject: [PATCH 1/2] Add an optional parameter in `buildPostScriptJsFunction` to force use of the `PSStackBasedInterpreter` code This way the test-only function `buildPostScriptProgramFunction` can be removed. --- src/core/postscript/js_evaluator.js | 22 +++++++++------------- test/unit/postscript_spec.js | 12 +++++------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/core/postscript/js_evaluator.js b/src/core/postscript/js_evaluator.js index ecef299cd..9d71c5380 100644 --- a/src/core/postscript/js_evaluator.js +++ b/src/core/postscript/js_evaluator.js @@ -795,9 +795,15 @@ class PSStackBasedInterpreter { * @param {number[]} range – flat [min0,max0, …] * @returns {Function} – `(src, srcOffset, dest, destOffset) => void` */ -function buildPostScriptJsFunction(source, domain, range) { +function buildPostScriptJsFunction( + source, + domain, + range, + forceInterpreter = false +) { const program = parsePostScriptFunction(source); - const ir = new PsJsCompiler(domain, range).compile(program); + const ir = + !forceInterpreter && new PsJsCompiler(domain, range).compile(program); if (ir) { return (src, srcOffset, dest, destOffset) => { PsJsCompiler.execute(ir, src, srcOffset, dest, destOffset); @@ -807,14 +813,4 @@ function buildPostScriptJsFunction(source, domain, range) { return PSStackBasedInterpreter.build(program, domain, range); } -/** - * @param {import("./ast.js").PsProgram} program - * @param {number[]} domain – flat [min0,max0, …] - * @param {number[]} range – flat [min0,max0, …] - * @returns {Function} – `(src, srcOffset, dest, destOffset) => void` - */ -function buildPostScriptProgramFunction(program, domain, range) { - return PSStackBasedInterpreter.build(program, domain, range); -} - -export { buildPostScriptJsFunction, buildPostScriptProgramFunction }; +export { buildPostScriptJsFunction }; diff --git a/test/unit/postscript_spec.js b/test/unit/postscript_spec.js index 5ad7d69e9..cdfdcc0b5 100644 --- a/test/unit/postscript_spec.js +++ b/test/unit/postscript_spec.js @@ -13,10 +13,6 @@ * limitations under the License. */ -import { - buildPostScriptJsFunction, - buildPostScriptProgramFunction, -} from "../../src/core/postscript/js_evaluator.js"; import { buildPostScriptWasmFunction, compilePostScriptToWasm, @@ -39,6 +35,7 @@ import { PsTernaryNode, PsUnaryNode, } from "../../src/core/postscript/ast.js"; +import { buildPostScriptJsFunction } from "../../src/core/postscript/js_evaluator.js"; // Precision argument for toBeCloseTo() in trigonometric tests. const TRIGONOMETRY_EPS = 1e-10; @@ -209,10 +206,11 @@ describe("PostScript Type 4 lexer, parser, and Wasm compiler", function () { // direct program interpreter otherwise. const jsFn = buildPostScriptJsFunction(src, domain, range); // Direct interpreter: always available, never uses PSStackToTree. - const interpFn = buildPostScriptProgramFunction( - parsePostScriptFunction(src), + const interpFn = buildPostScriptJsFunction( + src, domain, - range + range, + /* forceInterpreter = */ true ); if (!wasmFn) { From 9f9be2c619e13a53bcd42f03f472ffa4fd8e8e3e Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 5 Apr 2026 14:00:47 +0200 Subject: [PATCH 2/2] Use `this` more in static methods in the `src/core/postscript/` folder Inside of static methods it's possible to reference static fields with `this`, rather than having to spell out the full class name. --- src/core/postscript/ast.js | 8 +++---- src/core/postscript/js_evaluator.js | 4 ++-- src/core/postscript/lexer.js | 4 ++-- src/core/postscript/wasm_compiler.js | 34 +++++++++++++++------------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/core/postscript/ast.js b/src/core/postscript/ast.js index 9bff1d4db..6ade320cb 100644 --- a/src/core/postscript/ast.js +++ b/src/core/postscript/ast.js @@ -544,7 +544,7 @@ class PSStackToTree { static #init() { // Binary operator ids — used by _evalOp. - PSStackToTree.#binaryOps = new Set([ + this.#binaryOps = new Set([ TOKEN.add, TOKEN.sub, TOKEN.mul, @@ -565,7 +565,7 @@ class PSStackToTree { TOKEN.bitshift, ]); // Unary operator ids. - PSStackToTree.#unaryOps = new Set([ + this.#unaryOps = new Set([ TOKEN.abs, TOKEN.neg, TOKEN.ceiling, @@ -583,7 +583,7 @@ class PSStackToTree { ]); // Unary operators where f(f(x)) = f(x) — applying them twice is the same // as applying them once. - PSStackToTree.#idempotentUnary = new Set([ + this.#idempotentUnary = new Set([ TOKEN.abs, TOKEN.ceiling, TOKEN.cvi, @@ -594,7 +594,7 @@ class PSStackToTree { ]); // Maps each comparison operator to its logical negation. // Used to simplify not(comparison) → negated-comparison. - PSStackToTree.#negatedComparison = new Map([ + this.#negatedComparison = new Map([ [TOKEN.eq, TOKEN.ne], [TOKEN.ne, TOKEN.eq], [TOKEN.lt, TOKEN.ge], diff --git a/src/core/postscript/js_evaluator.js b/src/core/postscript/js_evaluator.js index 9d71c5380..c6e825402 100644 --- a/src/core/postscript/js_evaluator.js +++ b/src/core/postscript/js_evaluator.js @@ -327,8 +327,8 @@ class PsJsCompiler { let ip = 0, sp = 0; const n = ir.length; - const stack = PsJsCompiler.#stack; - const tmp = PsJsCompiler.#tmp; + const stack = this.#stack; + const tmp = this.#tmp; while (ip < n) { switch (ir[ip++] | 0) { diff --git a/src/core/postscript/lexer.js b/src/core/postscript/lexer.js index 7361e2396..8ac271dfe 100644 --- a/src/core/postscript/lexer.js +++ b/src/core/postscript/lexer.js @@ -120,8 +120,8 @@ class Lexer { operatorSingletons[name] = token; } } - Lexer.#singletons = singletons; - Lexer.#operatorSingletons = operatorSingletons; + this.#singletons = singletons; + this.#operatorSingletons = operatorSingletons; } constructor(data) { diff --git a/src/core/postscript/wasm_compiler.js b/src/core/postscript/wasm_compiler.js index b68d0a217..1b552c3b8 100644 --- a/src/core/postscript/wasm_compiler.js +++ b/src/core/postscript/wasm_compiler.js @@ -170,7 +170,7 @@ class PsWasmCompiler { static #init() { // TOKEN comparison ids → Wasm f64 comparison opcodes (leave i32 on stack). - PsWasmCompiler.#comparisonToOp = new Map([ + this.#comparisonToOp = new Map([ [TOKEN.eq, OP.f64_eq], [TOKEN.ne, OP.f64_ne], [TOKEN.lt, OP.f64_lt], @@ -179,18 +179,20 @@ class PsWasmCompiler { [TOKEN.ge, OP.f64_ge], ]); // Index of each import function by name. - PsWasmCompiler.#importIdx = Object.create(null); + this.#importIdx = Object.create(null); for (let i = 0; i < MATH_IMPORTS.length; i++) { - PsWasmCompiler.#importIdx[MATH_IMPORTS[i][0]] = i; + this.#importIdx[MATH_IMPORTS[i][0]] = i; } - PsWasmCompiler.#degToRad = Math.PI / 180; - PsWasmCompiler.#radToDeg = 180 / Math.PI; + this.#degToRad = Math.PI / 180; + this.#radToDeg = 180 / Math.PI; // Import type entries are identical on every compilation — compute once. - PsWasmCompiler.#importTypeEntries = MATH_IMPORTS.map( - ([, , , params, results]) => [FUNC_TYPE, ...vec(params), ...vec(results)] - ); + this.#importTypeEntries = MATH_IMPORTS.map(([, , , params, results]) => [ + FUNC_TYPE, + ...vec(params), + ...vec(results), + ]); // Static Wasm sections that never change between compilations. - PsWasmCompiler.#importSection = new Uint8Array( + this.#importSection = new Uint8Array( section( SECTION.import, vec( @@ -204,16 +206,16 @@ class PsWasmCompiler { ) ); // One function, type index 0. - PsWasmCompiler.#functionSection = new Uint8Array( + this.#functionSection = new Uint8Array( section(SECTION.function, vec([[0]])) ); // Min 1 page (64 KiB), no max. // https://webassembly.github.io/spec/core/binary/types.html#binary-limits - PsWasmCompiler.#memorySection = new Uint8Array( + this.#memorySection = new Uint8Array( section(SECTION.memory, vec([[0x00, 0x01]])) ); // Export "fn" (func index = nImports) and "mem" (memory) for the wrapper. - PsWasmCompiler.#exportSection = new Uint8Array( + this.#exportSection = new Uint8Array( section( SECTION.export, vec([ @@ -228,7 +230,7 @@ class PsWasmCompiler { ); // Wasm binary magic + version (constant). // https://webassembly.github.io/spec/core/binary/modules.html#binary-magic - PsWasmCompiler.#wasmMagicVersion = new Uint8Array([ + this.#wasmMagicVersion = new Uint8Array([ 0x00, 0x61, 0x73, @@ -239,9 +241,9 @@ class PsWasmCompiler { 0x00, // version 1 ]); const f64Buf = new ArrayBuffer(8); - PsWasmCompiler.#f64View = new DataView(f64Buf); - PsWasmCompiler.#f64Arr = new Uint8Array(f64Buf); - PsWasmCompiler.#initialized = true; + this.#f64View = new DataView(f64Buf); + this.#f64Arr = new Uint8Array(f64Buf); + this.#initialized = true; } constructor(domain, range) {