import type { Constructor } from 'type-fest'; import { createVmContext, type VmAny, type VmContext, type VmValue, isVmWrapper, isVmFunction, } from '@mirascript/mirascript'; import { type CompiledExpression, type Expression, type ExpressionOrValue, ExpressionTag, isExpression, } from './expression.js'; import { evaluateEval } from './eval.js'; import { parse } from './parser.js'; import { TypeInfo } from './type.js'; import type { Evaluator } from './main.js'; const { hasOwn, keys } = Object; const { isArray } = Array; const RAW = Symbol.for('@private/expression:raw'); const specialTypes = new Set>([Date, Promise, RegExp, ArrayBuffer]); if (typeof SharedArrayBuffer != 'undefined') { specialTypes.add(SharedArrayBuffer); } /** 是否需要创建代理对象 */ function isSpecialObject(value: object): boolean { return ( isVmWrapper(value) || isVmFunction(value) || ArrayBuffer.isView(value) || specialTypes.has(value.constructor as Constructor) ); } /** 创建代理对象 */ function createProxyIfNeeded( value: ExpressionOrValue | undefined, scope: Scope, path: readonly PropertyKey[], propName: PropertyKey, ): unknown { if (value == null || (typeof value != 'object' && typeof value != 'function')) { return value; } if (isExpression(value)) { return scope.eval(value, [...path, propName]); } if (isSpecialObject(value)) { return value; } if (isArray(value)) { return new Proxy(value, new ArrayScopeProxyHandler(scope, [...path, propName])); } else { return new Proxy(value, new RecordScopeProxyHandler(scope, [...path, propName])); } } /** 函数的执行环境 */ class ArrayScopeProxyHandler implements ProxyHandler { constructor( readonly scope: Scope, readonly path: readonly PropertyKey[], ) {} /** * @inheritdoc */ get(target: object, p: string | symbol): unknown { if (p === RAW) { return target; } const value = Reflect.get(target, p) as ExpressionOrValue; if (typeof p == 'symbol' || String(Number(p)) !== p) { return value; } return createProxyIfNeeded(value, this.scope, this.path, p); } } /** 函数的执行环境 */ class RecordScopeProxyHandler implements ProxyHandler { constructor( readonly scope: Scope, readonly path: readonly PropertyKey[], ) {} /** * @inheritdoc */ get(target: object, p: string | symbol): unknown { if (p === RAW) { return target; } const value = Reflect.get(target, p) as ExpressionOrValue; if (typeof p == 'symbol' || !hasOwn(target, p)) { return value; } return createProxyIfNeeded(value, this.scope, this.path, p); } } /** 最大求值计数 */ const MAX_RECURSION = 500; /** 函数的执行环境 */ export class Scope { constructor( /** 生成环境的工厂函数 */ readonly scope: | ((key: string) => ExpressionOrValue | undefined) | Record | null = null, /** 求值失败是否抛异常 */ readonly throws = true, /** 执行环境名称 */ readonly name = '', ) {} /** 枚举环境中的值 */ enumerator: (() => Iterable) | null | undefined = null; /** 描述环境中的值 */ describer: ((key: string) => string | undefined) | null | undefined = null; /** 执行器上下文 */ protected evaluator: Evaluator | null = null; /** 求值计数 */ protected evalCounter = 0; /** 重置执行器 */ reset(evaluator: Evaluator | null): () => void { const oldEvaluator = this.evaluator; const oldCounter = this.evalCounter; this.evaluator = evaluator; this.evalCounter = 0; return () => { this.evaluator = oldEvaluator; this.evalCounter = oldCounter; }; } /** 函数的执行环境,惰性求值 */ protected _proxy: VmContext | null = null; /** 函数的执行环境,惰性求值 */ get proxy(): VmContext { if (this._proxy != null) return this._proxy; let getter: (key: string) => VmAny; let enumerate: () => Iterable; let describe: ((key: string) => string | undefined) | undefined; const { scope, enumerator, describer } = this; if (scope == null) { getter = (key) => this.evaluator?.imported.get(key); } else if (typeof scope == 'function') { getter = (key) => { const v = scope(key); if (v === undefined) return this.evaluator?.imported.get(key); return createProxyIfNeeded(v, this, [], key) as VmValue; }; } else { getter = (key) => { if (!hasOwn(scope, key)) return this.evaluator?.imported.get(key); const v = scope[key]; return createProxyIfNeeded(v, this, [], key) as VmValue; }; } if (typeof enumerator == 'function') { enumerate = () => { const keys = enumerator(); const imported = this.evaluator?.imported.keys() ?? []; return [...keys, ...imported]; }; } else { enumerate = () => this.evaluator?.imported.keys() ?? []; } if (typeof describer == 'function') { describe = (key) => { const d = describer(key); if (d) return d; return undefined; }; } else { describe = () => undefined; } const proxy = createVmContext(getter, enumerate, describe); this._proxy = proxy; return proxy; } /** 检查环境中是否有值,不包含 evaluator 和 default context 中的值 */ has(key: string): boolean { const { scope } = this; if (scope == null) return false; let v; if (typeof scope == 'function') { v = scope(key); } else if (hasOwn(scope, key)) { v = scope[key]; } return v !== undefined; } /** 获取环境中的值,不包含 evaluator 和 default context 中的值 */ get(key: string): V | undefined { const { scope } = this; if (scope == null) return undefined; let v; if (typeof scope == 'function') { v = scope(key); } else if (hasOwn(scope, key)) { v = scope[key]; } if (!isExpression(v)) return v satisfies VmValue | undefined as V | undefined; return this.eval(v, [key]) satisfies VmValue as V; } /** 表达式递归求值 */ eval(expression: Expression | CompiledExpression, path: readonly PropertyKey[]): VmValue { if (this.evalCounter >= MAX_RECURSION) { this.evalCounter = 0; throw new Error(`Execution recursion exceeds limit`); } this.evalCounter++; try { let result: VmValue | null = null; if (typeof expression == 'function') { result = expression(this, this.evaluator!); } else { const { evaluator } = this; const exp = parse(evaluator, expression.source, this.throws, false); if (exp.func != null) { result = evaluateEval(evaluator, exp, this); } else if (exp.error != null) { throw exp.error; } } const tag = expression[ExpressionTag]; if (!tag) return result; return TypeInfo.to(result, tag); } catch (ex) { const msg = (ex as Error).message || String(ex); throw new Error(`${msg}\n In ${this.name}: @ ${path.join('/')}`, { cause: ex }); } } } /** 移除 PROXY */ function unwrapCore(value: T): T | undefined { if (value == null || (typeof value != 'object' && typeof value != 'function')) { return undefined; } if (isSpecialObject(value)) { return undefined; } // 是否有 proxy const raw = (value as T & { [RAW]?: T })[RAW]; // 不论是否被代理,均须递归处理子项 const obj = raw ?? value; // 只处理 own enumerable 属性 for (const key of keys(obj) as Array>) { const unwrapped = unwrapCore(obj[key]); if (unwrapped !== undefined) { obj[key] = unwrapped; } } // 返回被代理的对象,如没有 proxy 则返回 undefined return raw; } /** 移除 PROXY */ export function unwrap(value: T): T { return unwrapCore(value) ?? value; }