UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

162 lines (134 loc) 3.98 kB
import { assert } from "../assert.js"; import { Cache } from "../cache/Cache.js"; import { combine_hash } from "../collection/array/combine_hash.js"; import { computeHashArray } from "../collection/array/computeHashArray.js"; import { isArrayEqualStrict } from "../collection/array/isArrayEqualStrict.js"; import { invokeObjectEquals } from "../model/object/invokeObjectEquals.js"; import { invokeObjectHash } from "../model/object/invokeObjectHash.js"; import { computeStringHash } from "../primitives/strings/computeStringHash.js"; import { string_compute_byte_size } from "../primitives/strings/string_compute_byte_size.js"; /** * Hash is an integer value, so this is an invalid value for a computed hash * @type {number} */ const HASH_NOT_SET = 0.1; class FunctionDefinition { /** * * @param {string} [name] * @param {string} body * @param {string[]} [args] */ constructor({ name, body, args = [] }) { assert.isString(body, 'body'); /** * * @type {string} */ this.name = name; /** *` * @type {string[]} */ this.args = args; /** * * @type {string} */ this.body = body; /** * * @type {number} * @private */ this.__hash = HASH_NOT_SET; } updateHash() { this.__hash = combine_hash( computeStringHash(this.name), computeStringHash(this.body), computeHashArray(this.args, computeStringHash) ); } /** * * @return {number} */ computeByteSize() { let result = string_compute_byte_size(this.body); if (this.name !== undefined) { result += string_compute_byte_size(this.name); } const n = this.args.length; for (let i = 0; i < n; i++) { const arg = this.args[i]; result += string_compute_byte_size(arg); } return result; } /** * * @param {FunctionDefinition} other * @returns {boolean} */ equals(other) { return this.name === other.name && this.body === other.body && isArrayEqualStrict(this.args, other.args) ; } /** * @returns {number} */ hash() { if (this.__hash === HASH_NOT_SET) { this.updateHash(); } return this.__hash; } } /** * Optimization structure, caches functions internally to avoid creating multiple identical functions */ export class FunctionCompiler { constructor() { /** * * @type {Cache<FunctionDefinition, Function>} * @private */ this.cache = new Cache({ maxWeight: 10383360, keyWeigher(k) { return k.computeByteSize(); }, keyHashFunction: invokeObjectHash, keyEqualityFunction: invokeObjectEquals }); } /** * * @param {string} code * @param {string[]} [args] * @param {string} [name] */ compile({ code, args = [], name }) { assert.isString(code, 'code'); assert.isArray(args, 'args'); const fd = new FunctionDefinition({ body: code, args, name }); const existing = this.cache.get(fd); if (existing === null) { const f = new Function(args.join(","), code); if (name !== undefined) { //give function a name Object.defineProperty(f, "name", { value: name }); } //cache this.cache.put(fd, f); return f; } else { return existing; } } } FunctionCompiler.INSTANCE = new FunctionCompiler();