@mirascript/mirascript 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/{chunk-2PWHYDNX.js → chunk-MVHCSH3E.js} +239 -122
  2. package/dist/chunk-MVHCSH3E.js.map +6 -0
  3. package/dist/cli/index.js +3 -3
  4. package/dist/compiler/diagnostic.d.ts +12 -3
  5. package/dist/compiler/diagnostic.d.ts.map +1 -1
  6. package/dist/helpers/utils.d.ts +1 -0
  7. package/dist/helpers/utils.d.ts.map +1 -1
  8. package/dist/index.js +9 -1
  9. package/dist/subtle.js +1 -1
  10. package/dist/vm/error.d.ts.map +1 -1
  11. package/dist/vm/lib/_helpers.d.ts.map +1 -1
  12. package/dist/vm/lib/global/sequence/find.d.ts.map +1 -1
  13. package/dist/vm/lib/global/sequence/with.d.ts +2 -2
  14. package/dist/vm/lib/global/sequence/with.d.ts.map +1 -1
  15. package/dist/vm/operations.d.ts.map +1 -1
  16. package/dist/vm/types/boundary.d.ts +12 -0
  17. package/dist/vm/types/boundary.d.ts.map +1 -0
  18. package/dist/vm/types/checker.d.ts +1 -1
  19. package/dist/vm/types/checker.d.ts.map +1 -1
  20. package/dist/vm/types/context.d.ts +14 -6
  21. package/dist/vm/types/context.d.ts.map +1 -1
  22. package/dist/vm/types/extern.d.ts +7 -7
  23. package/dist/vm/types/extern.d.ts.map +1 -1
  24. package/dist/vm/types/function.d.ts +0 -4
  25. package/dist/vm/types/function.d.ts.map +1 -1
  26. package/dist/vm/types/index.d.ts +9 -10
  27. package/dist/vm/types/index.d.ts.map +1 -1
  28. package/dist/vm/types/module.d.ts +2 -0
  29. package/dist/vm/types/module.d.ts.map +1 -1
  30. package/dist/vm/types/wrapper.d.ts +2 -0
  31. package/dist/vm/types/wrapper.d.ts.map +1 -1
  32. package/package.json +4 -4
  33. package/src/compiler/diagnostic.ts +14 -12
  34. package/src/helpers/utils.ts +1 -0
  35. package/src/vm/error.ts +15 -3
  36. package/src/vm/lib/_helpers.ts +4 -13
  37. package/src/vm/lib/_loader.ts +4 -4
  38. package/src/vm/lib/global/math.ts +1 -1
  39. package/src/vm/lib/global/sequence/find.ts +13 -11
  40. package/src/vm/lib/global/sequence/map-filter.ts +2 -2
  41. package/src/vm/lib/global/sequence/with.ts +111 -17
  42. package/src/vm/operations.ts +23 -24
  43. package/src/vm/types/boundary.ts +95 -0
  44. package/src/vm/types/checker.ts +8 -15
  45. package/src/vm/types/context.ts +31 -19
  46. package/src/vm/types/extern.ts +30 -57
  47. package/src/vm/types/function.ts +1 -37
  48. package/src/vm/types/index.ts +12 -24
  49. package/src/vm/types/module.ts +7 -0
  50. package/src/vm/types/wrapper.ts +7 -0
  51. package/dist/chunk-2PWHYDNX.js.map +0 -6
@@ -0,0 +1,95 @@
1
+ import { isVmFunction, type VmFunctionLike, type VmFunction } from './function.js';
2
+ import { isVmExtern, VmExtern } from './extern.js';
3
+ import { isVmWrapper } from './wrapper.js';
4
+ import type { VmAny, VmConst, VmModule, VmPrimitive, VmValue } from './index.js';
5
+ import { $Call } from '../operations.js';
6
+ import { defineProperty, apply } from '../../helpers/utils.js';
7
+
8
+ const kProxy = Symbol.for('mirascript.vm.function.proxy');
9
+
10
+ /** 创建 Mirascript 函数在宿主语言运行的代理 */
11
+ export function toVmFunctionProxy<T extends VmFunctionLike>(fn: VmFunction<T>): T {
12
+ if (!isVmFunction(fn)) return fn;
13
+
14
+ const cached = (fn as unknown as { [kProxy]?: T })[kProxy];
15
+ if (cached != null) return cached;
16
+
17
+ const proxy = (...args: unknown[]) => {
18
+ const ret = $Call(
19
+ fn,
20
+ // 作为函数参数传入的值一定丢失了它的 this
21
+ args.map((v) => wrapToVmValue(v, null)),
22
+ );
23
+ return unwrapFromVmValue(ret);
24
+ };
25
+ defineProperty(fn, kProxy, { value: proxy });
26
+ defineProperty(proxy, kProxy, { value: fn });
27
+ defineProperty(proxy, 'name', {
28
+ value: fn.name,
29
+ configurable: true,
30
+ });
31
+ return proxy as T;
32
+ }
33
+
34
+ /** 解开 Mirascript 函数在宿主语言运行的代理 */
35
+ export function fromVmFunctionProxy<T extends VmFunctionLike>(fn: T): VmFunction<T> | undefined {
36
+ if (isVmFunction(fn)) return fn;
37
+
38
+ const original = (fn as unknown as { [kProxy]?: VmFunction<T> })[kProxy];
39
+ if (original && isVmFunction(original)) return original;
40
+
41
+ return undefined;
42
+ }
43
+
44
+ /** 将宿主语言的值包装为 Mirascript 类型 */
45
+ export function wrapToVmValue(
46
+ value: unknown,
47
+ thisArg: VmExtern | null = null,
48
+ assumeVmValue?: (obj: object) => obj is Exclude<VmConst, VmPrimitive>,
49
+ ): VmValue {
50
+ if (value == null) return null;
51
+ switch (typeof value) {
52
+ case 'function': {
53
+ const unwrapped = fromVmFunctionProxy(value as VmFunctionLike);
54
+ if (unwrapped) return unwrapped;
55
+ return new VmExtern(value as () => never, thisArg);
56
+ }
57
+ case 'object': {
58
+ if (isVmWrapper(value)) return value as VmModule | VmExtern;
59
+ if (value instanceof Date) return value.valueOf();
60
+ if (assumeVmValue?.(value)) return value;
61
+ // Only functions preserve thisArg
62
+ return new VmExtern(value);
63
+ }
64
+ case 'string':
65
+ case 'number':
66
+ case 'boolean':
67
+ return value;
68
+ case 'bigint':
69
+ return Number(value);
70
+ case 'symbol':
71
+ case 'undefined':
72
+ default:
73
+ return null;
74
+ }
75
+ }
76
+
77
+ /** 取消宿主语言的值的 Mirascript 包装 */
78
+ export function unwrapFromVmValue(value: VmAny): unknown {
79
+ if (typeof value == 'function') {
80
+ return toVmFunctionProxy(value);
81
+ }
82
+ if (value == null || typeof value != 'object') return value;
83
+ if (!isVmExtern(value)) return value;
84
+
85
+ if (value.thisArg == null || typeof value.value != 'function') {
86
+ return value.value;
87
+ }
88
+ const f = value as VmExtern<(...args: unknown[]) => unknown>;
89
+ const caller = f.thisArg!.value;
90
+ return new Proxy(f.value, {
91
+ apply(target, thisArg, args): unknown {
92
+ return apply(target, caller, args);
93
+ },
94
+ });
95
+ }
@@ -1,15 +1,8 @@
1
1
  import { getPrototypeOf, isArray, values } from '../../helpers/utils.js';
2
- import {
3
- isVmFunction,
4
- VmModule,
5
- type VmAny,
6
- type VmArray,
7
- type VmConst,
8
- type VmImmutable,
9
- type VmRecord,
10
- type VmValue,
11
- } from './index.js';
12
- import { VmWrapper } from './wrapper.js';
2
+ import type { VmAny, VmArray, VmConst, VmImmutable, VmRecord, VmValue } from './index.js';
3
+ import { isVmWrapper } from './wrapper.js';
4
+ import { isVmModule } from './module.js';
5
+ import { isVmFunction } from './function.js';
13
6
 
14
7
  const MAX_DEPTH = 32;
15
8
  /**
@@ -59,7 +52,7 @@ function isVmConstInner(value: unknown, depth: number): value is VmConst {
59
52
  return true;
60
53
  case 'object':
61
54
  if (value == null) return true;
62
- if (value instanceof VmWrapper) return false;
55
+ if (isVmWrapper(value)) return false;
63
56
  if (isArray(value)) {
64
57
  return isVmArrayDeep(value, depth);
65
58
  } else {
@@ -92,7 +85,7 @@ export function isVmConst(value: unknown, checkDeep = false): value is VmConst {
92
85
  return true;
93
86
  case 'object':
94
87
  if (value == null) return true;
95
- if (value instanceof VmWrapper) return false;
88
+ if (isVmWrapper(value)) return false;
96
89
  if (!checkDeep) {
97
90
  if (isArray(value)) {
98
91
  return isVmArrayDeep(value, 0);
@@ -122,7 +115,7 @@ export function isVmImmutable(value: unknown, checkDeep: boolean): value is VmIm
122
115
  * 检查是否为 Mirascript 不可变值
123
116
  */
124
117
  export function isVmImmutable(value: unknown, checkDeep = false): value is VmImmutable {
125
- return value instanceof VmModule || isVmFunction(value) || isVmConst(value, checkDeep);
118
+ return isVmModule(value) || isVmFunction(value) || isVmConst(value, checkDeep);
126
119
  }
127
120
  /**
128
121
  * 检查是否为 Mirascript 合法值
@@ -152,7 +145,7 @@ export function isVmAny(value: unknown, checkDeep: boolean): value is VmAny {
152
145
  return true;
153
146
  case 'object':
154
147
  if (value == null) return true;
155
- if (value instanceof VmWrapper) return true;
148
+ if (isVmWrapper(value)) return true;
156
149
  return isVmConst(value, checkDeep);
157
150
  case 'function':
158
151
  return isVmFunction(value);
@@ -7,7 +7,7 @@ import {
7
7
  isVmAny,
8
8
  type VmFunctionLike,
9
9
  } from './index.js';
10
- import { entries, keys } from '../../helpers/utils.js';
10
+ import { create, entries, keys } from '../../helpers/utils.js';
11
11
  import type * as global from '../lib/global/index.js';
12
12
 
13
13
  /** 全局导入的标准库 */
@@ -29,6 +29,8 @@ export interface VmContext {
29
29
  readonly [kVmContext]: true;
30
30
  /** 枚举所有 key,仅在 LSP 中使用 */
31
31
  keys(): Iterable<string>;
32
+ /** 描述值,返回 MarkDown 文本,仅在 LSP 中使用 */
33
+ describe?(key: string): string | undefined;
32
34
  /** 获取指定 key 的值 `global[key]` */
33
35
  get(key: string): VmValue;
34
36
  /** 查找指定 key 是否存在 `key in global` */
@@ -36,7 +38,7 @@ export interface VmContext {
36
38
  }
37
39
  /** MiraScript 执行上下文 */
38
40
  export type VmContextRecord = Record<string, VmValue | undefined>;
39
- export const VmSharedContext = { __proto__: null } as object as VmSharedContext;
41
+ export const VmSharedContext = create(null) as VmSharedContext;
40
42
 
41
43
  /** 定义在所有 MiraScript 执行上下文中共享的全局变量 */
42
44
  export function defineVmContextValue(
@@ -91,7 +93,11 @@ class ValueVmContext implements VmContext {
91
93
  has(key: string): boolean {
92
94
  return key in this.env;
93
95
  }
94
- constructor(private readonly env: VmContextRecord & { __proto__: VmSharedContext }) {}
96
+ constructor(
97
+ private readonly env: VmContextRecord,
98
+ /** @inheritdoc */
99
+ readonly describe?: (key: string) => string | undefined,
100
+ ) {}
95
101
  }
96
102
 
97
103
  /** 以工厂函数为后备的实现 */
@@ -115,31 +121,37 @@ class FactoryVmContext implements VmContext {
115
121
  constructor(
116
122
  private readonly getter: (key: string) => VmValue | undefined,
117
123
  private readonly enumerator?: () => Iterable<string>,
124
+ /** @inheritdoc */
125
+ readonly describe?: (key: string) => string | undefined,
118
126
  ) {}
119
127
  }
120
128
 
129
+ /** 以值为后备的实现 */
130
+ type CreateVmContextWithValues = readonly [
131
+ vmValues?: VmContextRecord | null | undefined,
132
+ externValues?: Record<string, unknown> | null | undefined,
133
+ describer?: ((key: string) => string | undefined) | null | undefined,
134
+ ];
135
+ /** 以工厂函数为后备的实现 */
136
+ type CreateVmContextWithFactory = readonly [
137
+ getter: (key: string) => VmValue | undefined,
138
+ enumerator?: (() => Iterable<string>) | null | undefined,
139
+ describer?: ((key: string) => string | undefined) | null | undefined,
140
+ ];
141
+
121
142
  /** 创建用于执行脚本的执行上下文 */
122
- export function createVmContext(
123
- ...args:
124
- | readonly [
125
- vmValues?: VmContextRecord | null | undefined,
126
- externValues?: Record<string, unknown> | null | undefined,
127
- ]
128
- | readonly [
129
- getter: (key: string) => VmValue | undefined,
130
- enumerator?: (() => Iterable<string>) | null | undefined,
131
- ]
132
- ): VmContext {
143
+ export function createVmContext(...args: CreateVmContextWithValues | CreateVmContextWithFactory): VmContext {
133
144
  if (args[0] == null && args[1] == null) {
134
145
  return { ...DefaultVmContext };
135
146
  }
136
147
 
137
148
  if (typeof args[0] == 'function') {
138
- return new FactoryVmContext(args[0], args[1] as (() => Iterable<string>) | undefined);
149
+ const [getter, enumerator, describer] = args as CreateVmContextWithFactory;
150
+ return new FactoryVmContext(getter, enumerator ?? undefined, describer ?? undefined);
139
151
  }
140
152
 
141
- const [vmValues, externValues] = args;
142
- const env = { __proto__: VmSharedContext } as { __proto__: VmSharedContext } & VmContextRecord;
153
+ const [vmValues, externValues, describer] = args as CreateVmContextWithValues;
154
+ const env = create(VmSharedContext) as VmContextRecord;
143
155
  if (vmValues) {
144
156
  for (const [key, value] of entries(vmValues)) {
145
157
  if (!isVmAny(value, false)) continue;
@@ -147,11 +159,11 @@ export function createVmContext(
147
159
  }
148
160
  }
149
161
  if (externValues) {
150
- for (const [key, value] of entries(externValues as Record<string, unknown>)) {
162
+ for (const [key, value] of entries(externValues)) {
151
163
  env[key] = value == null ? null : wrapToVmValue(value, null);
152
164
  }
153
165
  }
154
- return new ValueVmContext(env);
166
+ return new ValueVmContext(env, describer ?? undefined);
155
167
  }
156
168
 
157
169
  /** 检查是否为执行上下文 */
@@ -1,62 +1,19 @@
1
1
  import { VmError } from '../error.js';
2
- import { fromVmFunctionProxy, toVmFunctionProxy, type VmFunctionLike } from './function.js';
3
2
  import { VmWrapper } from './wrapper.js';
4
- import type { TypeName, VmAny, VmModule, VmValue } from './index.js';
5
- import { getPrototypeOf, hasOwn } from '../../helpers/utils.js';
6
- const { apply } = Reflect;
7
-
8
- /** 包装为 Mirascript 类型 */
9
- export function wrapToVmValue(value: unknown, caller: VmExtern | null): VmValue {
10
- if (value == null) return null;
11
- switch (typeof value) {
12
- case 'function': {
13
- const unwrapped = fromVmFunctionProxy(value as VmFunctionLike);
14
- if (unwrapped) return unwrapped;
15
- return new VmExtern(value, caller);
16
- }
17
- case 'object':
18
- if (value instanceof VmWrapper) return value as VmModule | VmExtern;
19
- return new VmExtern(value, null);
20
- case 'string':
21
- case 'number':
22
- case 'boolean':
23
- return value;
24
- case 'bigint':
25
- return Number(value);
26
- case 'symbol':
27
- case 'undefined':
28
- default:
29
- return null;
30
- }
31
- }
32
-
33
- /** 取消 Mirascript 类型包装 */
34
- export function unwrapFromVmValue(value: VmAny): unknown {
35
- if (typeof value == 'function') {
36
- return toVmFunctionProxy(value);
37
- }
38
- if (value == null || typeof value != 'object') return value;
39
- if (!(value instanceof VmExtern)) return value;
40
- if (value.caller == null || typeof value.value != 'function') {
41
- return value.value;
42
- }
43
- const caller = value.caller.value;
44
- const func = value.value as (...args: unknown[]) => unknown;
45
- return new Proxy(func, {
46
- apply(target, thisArg, args): unknown {
47
- return apply(target, caller, args);
48
- },
49
- });
50
- }
3
+ import type { TypeName, VmAny, VmConst, VmPrimitive, VmValue } from './index.js';
4
+ import { getPrototypeOf, hasOwn, apply } from '../../helpers/utils.js';
5
+ import { unwrapFromVmValue, wrapToVmValue } from './boundary.js';
51
6
 
52
7
  const ObjectPrototype = Object.prototype;
53
8
  // eslint-disable-next-line @typescript-eslint/unbound-method
54
9
  const ObjectToString = ObjectPrototype.toString;
10
+ // eslint-disable-next-line @typescript-eslint/unbound-method
11
+ const FunctionToString = Function.prototype.toString;
55
12
  /** 包装 Mirascript `extern` 类型的对象 */
56
13
  export class VmExtern<const T extends object = object> extends VmWrapper<T> {
57
14
  constructor(
58
15
  value: T,
59
- readonly caller: VmExtern | null = null,
16
+ readonly thisArg: T extends (...args: readonly never[]) => unknown ? VmExtern | null : null = null,
60
17
  ) {
61
18
  super(value);
62
19
  }
@@ -79,6 +36,11 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
79
36
  return true;
80
37
  }
81
38
 
39
+ /** 决定是否对属性进行包装 */
40
+ protected assumeVmValue(value: object, key: keyof T | undefined): value is Exclude<VmConst, VmPrimitive> {
41
+ return false;
42
+ }
43
+
82
44
  /** @inheritdoc */
83
45
  override has(key: string): boolean {
84
46
  return this.access(key, true);
@@ -87,7 +49,7 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
87
49
  override get(key: string): VmAny {
88
50
  if (!this.has(key)) return undefined;
89
51
  const prop = (this.value as Record<string, unknown>)[key];
90
- return wrapToVmValue(prop, this);
52
+ return wrapToVmValue(prop, this, (v) => this.assumeVmValue(v, key as keyof T));
91
53
  }
92
54
  /** Set a property on the object */
93
55
  set(key: string, value: VmValue): boolean {
@@ -99,14 +61,18 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
99
61
  /** Call extern value */
100
62
  call(args: readonly VmValue[]): VmAny {
101
63
  const { value } = this;
102
- const caller = this.caller?.value ?? null;
64
+ if (typeof value != 'function') {
65
+ throw VmError.from(`Not a callable extern`, null, null);
66
+ }
67
+ const caller = this.thisArg?.value ?? null;
103
68
  const unwrappedArgs = args.map(unwrapFromVmValue);
69
+ let ret: unknown;
104
70
  try {
105
- const ret: unknown = apply(value as (...args: unknown[]) => unknown, caller, unwrappedArgs);
106
- return wrapToVmValue(ret, null);
71
+ ret = apply(value, caller, unwrappedArgs);
107
72
  } catch (ex) {
108
- throw VmError.from(`Not a callable extern`, ex, null);
73
+ throw VmError.from(`Callable extern`, ex, null);
109
74
  }
75
+ return wrapToVmValue(ret, null, (obj) => this.assumeVmValue(obj, undefined));
110
76
  }
111
77
  /** @inheritdoc */
112
78
  override keys(): string[] {
@@ -118,14 +84,14 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
118
84
  }
119
85
  /** @inheritdoc */
120
86
  override same(other: VmAny): boolean {
121
- if (!(other instanceof VmExtern)) return false;
122
- return this.value === other.value && this.caller === other.caller;
87
+ if (!isVmExtern(other)) return false;
88
+ return this.value === other.value && this.thisArg === other.thisArg;
123
89
  }
124
90
  /** @inheritdoc */
125
91
  override toString(): string {
126
92
  // eslint-disable-next-line @typescript-eslint/unbound-method
127
93
  const { toString } = this.value;
128
- if (typeof toString != 'function' || toString === ObjectToString) {
94
+ if (typeof toString != 'function' || toString === ObjectToString || toString === FunctionToString) {
129
95
  return super.toString();
130
96
  }
131
97
  try {
@@ -164,3 +130,10 @@ export class VmExtern<const T extends object = object> extends VmWrapper<T> {
164
130
  return tag;
165
131
  }
166
132
  }
133
+
134
+ const kVmExtern = Symbol.for('mirascript.vm.extern');
135
+ Object.defineProperty(VmExtern.prototype, kVmExtern, { value: true });
136
+ /** 检查值是否为 Mirascript 外部值 */
137
+ export function isVmExtern<T extends object>(value: unknown): value is VmExtern<T> {
138
+ return value != null && typeof value == 'object' && kVmExtern in value;
139
+ }
@@ -1,12 +1,10 @@
1
1
  import type { Writable } from 'type-fest';
2
2
  import { defineProperty } from '../../helpers/utils.js';
3
3
  import { CpEnter, CpExit } from '../helpers.js';
4
- import { $Call } from '../operations.js';
5
4
  import type { VmAny, VmValue } from './index.js';
6
- import { wrapToVmValue, unwrapFromVmValue } from './extern.js';
5
+ import { fromVmFunctionProxy } from './boundary.js';
7
6
 
8
7
  const kVmFunction = Symbol.for('mirascript.vm.function');
9
- const kProxy = Symbol.for('mirascript.vm.function.proxy');
10
8
 
11
9
  /**
12
10
  * Mirascript 函数签名
@@ -101,37 +99,3 @@ export function VmFunction<T extends VmFunctionLike>(fn: T, option: VmFunctionOp
101
99
  });
102
100
  return fn as VmFunction<T>;
103
101
  }
104
-
105
- /** 创建 Mirascript 函数在宿主语言运行的代理 */
106
- export function toVmFunctionProxy<T extends VmFunctionLike>(fn: VmFunction<T>): T {
107
- if (!isVmFunction(fn)) return fn;
108
-
109
- const cached = (fn as unknown as { [kProxy]?: T })[kProxy];
110
- if (cached) return cached;
111
-
112
- const proxy = (...args: unknown[]) => {
113
- const ret = $Call(
114
- fn,
115
- // 作为函数参数传入的值一定丢失了它的 this
116
- args.map((v) => wrapToVmValue(v, null)),
117
- );
118
- return unwrapFromVmValue(ret);
119
- };
120
- defineProperty(fn, kProxy, { value: proxy });
121
- defineProperty(proxy, kProxy, { value: fn });
122
- defineProperty(proxy, 'name', {
123
- value: fn.name,
124
- configurable: true,
125
- });
126
- return proxy as T;
127
- }
128
-
129
- /** 解开 Mirascript 函数在宿主语言运行的代理 */
130
- export function fromVmFunctionProxy<T extends VmFunctionLike>(fn: T): VmFunction<T> | undefined {
131
- if (isVmFunction(fn)) return fn;
132
-
133
- const original = (fn as unknown as { [kProxy]?: VmFunction<T> })[kProxy];
134
- if (original && isVmFunction(original)) return original;
135
-
136
- return undefined;
137
- }
@@ -1,8 +1,8 @@
1
1
  import { isArray } from '../../helpers/utils.js';
2
- import { VmExtern } from './extern.js';
3
- import type { VmFunction } from './function.js';
4
- import { VmModule } from './module.js';
5
- import { VmWrapper } from './wrapper.js';
2
+ import { isVmExtern, VmExtern } from './extern.js';
3
+ import { isVmFunction, VmFunction } from './function.js';
4
+ import { VmModule, isVmModule } from './module.js';
5
+ import { isVmWrapper } from './wrapper.js';
6
6
 
7
7
  /** Mirascript 原始值 */
8
8
  export type VmPrimitive = null | string | number | boolean;
@@ -74,7 +74,7 @@ export function isVmArray(value: VmAny): value is VmArray {
74
74
  */
75
75
  export function isVmRecord(value: VmAny): value is VmRecord {
76
76
  if (value == null || typeof value !== 'object') return false;
77
- if (value instanceof VmWrapper) return false;
77
+ if (isVmWrapper(value)) return false;
78
78
  if (isVmArray(value)) return false;
79
79
  value satisfies VmRecord;
80
80
  return true;
@@ -92,28 +92,16 @@ export function isVmPrimitive(value: unknown): value is VmPrimitive {
92
92
  return false;
93
93
  }
94
94
 
95
- export { VmExtern, wrapToVmValue, unwrapFromVmValue } from './extern.js';
95
+ export { VmFunction, isVmFunction, VmExtern, isVmExtern, VmModule, isVmModule, isVmWrapper };
96
+ export { wrapToVmValue, unwrapFromVmValue, toVmFunctionProxy, fromVmFunctionProxy } from './boundary.js';
96
97
 
97
- /** 检查值是否为 Mirascript 外部值 */
98
- export function isVmExtern(value: unknown): value is VmExtern {
99
- return value instanceof VmExtern;
98
+ /** 检查值是否为 Mirascript 可调用值 */
99
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
100
+ export function isVmCallable(value: unknown): value is VmFunction | VmExtern<Function> {
101
+ return isVmFunction(value) || (isVmExtern(value) && typeof value.value == 'function');
100
102
  }
101
103
 
102
- export {
103
- VmFunction,
104
- isVmFunction,
105
- getVmFunctionInfo,
106
- type VmFunctionInfo,
107
- type VmFunctionLike,
108
- type VmFunctionOption,
109
- } from './function.js';
110
-
111
- export { VmModule } from './module.js';
112
-
113
- /** 检查值是否为 Mirascript 模块 */
114
- export function isVmModule(value: unknown): value is VmModule {
115
- return value instanceof VmModule;
116
- }
104
+ export { getVmFunctionInfo, type VmFunctionInfo, type VmFunctionLike, type VmFunctionOption } from './function.js';
117
105
 
118
106
  export { type VmContext, type VmSharedContext, isVmContext, defineVmContextValue, createVmContext } from './context.js';
119
107
 
@@ -38,3 +38,10 @@ export class VmModule<const T extends Record<string, VmValue> = Record<string, V
38
38
  return this.name;
39
39
  }
40
40
  }
41
+
42
+ const kVmModule = Symbol.for('mirascript.vm.module');
43
+ Object.defineProperty(VmModule.prototype, kVmModule, { value: true });
44
+ /** 检查值是否为 Mirascript 模块 */
45
+ export function isVmModule<T extends Record<string, VmValue>>(value: unknown): value is VmModule<T> {
46
+ return value != null && typeof value == 'object' && kVmModule in value;
47
+ }
@@ -26,3 +26,10 @@ export abstract class VmWrapper<T extends object> {
26
26
  return `<${this.type} ${this.describe}>`;
27
27
  }
28
28
  }
29
+
30
+ const kVmWrapper = Symbol.for('mirascript.vm.wrapper');
31
+ Object.defineProperty(VmWrapper.prototype, kVmWrapper, { value: true });
32
+ /** 检查值是否为 MiraScript 包装器 */
33
+ export function isVmWrapper<T extends object>(value: unknown): value is VmWrapper<T> {
34
+ return value != null && typeof value == 'object' && kVmWrapper in value;
35
+ }