@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.
- package/dist/{chunk-2PWHYDNX.js → chunk-MVHCSH3E.js} +239 -122
- package/dist/chunk-MVHCSH3E.js.map +6 -0
- package/dist/cli/index.js +3 -3
- package/dist/compiler/diagnostic.d.ts +12 -3
- package/dist/compiler/diagnostic.d.ts.map +1 -1
- package/dist/helpers/utils.d.ts +1 -0
- package/dist/helpers/utils.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/subtle.js +1 -1
- package/dist/vm/error.d.ts.map +1 -1
- package/dist/vm/lib/_helpers.d.ts.map +1 -1
- package/dist/vm/lib/global/sequence/find.d.ts.map +1 -1
- package/dist/vm/lib/global/sequence/with.d.ts +2 -2
- package/dist/vm/lib/global/sequence/with.d.ts.map +1 -1
- package/dist/vm/operations.d.ts.map +1 -1
- package/dist/vm/types/boundary.d.ts +12 -0
- package/dist/vm/types/boundary.d.ts.map +1 -0
- package/dist/vm/types/checker.d.ts +1 -1
- package/dist/vm/types/checker.d.ts.map +1 -1
- package/dist/vm/types/context.d.ts +14 -6
- package/dist/vm/types/context.d.ts.map +1 -1
- package/dist/vm/types/extern.d.ts +7 -7
- package/dist/vm/types/extern.d.ts.map +1 -1
- package/dist/vm/types/function.d.ts +0 -4
- package/dist/vm/types/function.d.ts.map +1 -1
- package/dist/vm/types/index.d.ts +9 -10
- package/dist/vm/types/index.d.ts.map +1 -1
- package/dist/vm/types/module.d.ts +2 -0
- package/dist/vm/types/module.d.ts.map +1 -1
- package/dist/vm/types/wrapper.d.ts +2 -0
- package/dist/vm/types/wrapper.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/compiler/diagnostic.ts +14 -12
- package/src/helpers/utils.ts +1 -0
- package/src/vm/error.ts +15 -3
- package/src/vm/lib/_helpers.ts +4 -13
- package/src/vm/lib/_loader.ts +4 -4
- package/src/vm/lib/global/math.ts +1 -1
- package/src/vm/lib/global/sequence/find.ts +13 -11
- package/src/vm/lib/global/sequence/map-filter.ts +2 -2
- package/src/vm/lib/global/sequence/with.ts +111 -17
- package/src/vm/operations.ts +23 -24
- package/src/vm/types/boundary.ts +95 -0
- package/src/vm/types/checker.ts +8 -15
- package/src/vm/types/context.ts +31 -19
- package/src/vm/types/extern.ts +30 -57
- package/src/vm/types/function.ts +1 -37
- package/src/vm/types/index.ts +12 -24
- package/src/vm/types/module.ts +7 -0
- package/src/vm/types/wrapper.ts +7 -0
- 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
|
+
}
|
package/src/vm/types/checker.ts
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import { getPrototypeOf, isArray, values } from '../../helpers/utils.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
148
|
+
if (isVmWrapper(value)) return true;
|
|
156
149
|
return isVmConst(value, checkDeep);
|
|
157
150
|
case 'function':
|
|
158
151
|
return isVmFunction(value);
|
package/src/vm/types/context.ts
CHANGED
|
@@ -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 =
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
/** 检查是否为执行上下文 */
|
package/src/vm/types/extern.ts
CHANGED
|
@@ -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,
|
|
5
|
-
import { getPrototypeOf, hasOwn } from '../../helpers/utils.js';
|
|
6
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
106
|
-
return wrapToVmValue(ret, null);
|
|
71
|
+
ret = apply(value, caller, unwrappedArgs);
|
|
107
72
|
} catch (ex) {
|
|
108
|
-
throw VmError.from(`
|
|
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
|
|
122
|
-
return this.value === other.value && this.
|
|
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
|
+
}
|
package/src/vm/types/function.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
}
|
package/src/vm/types/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isArray } from '../../helpers/utils.js';
|
|
2
|
-
import { VmExtern } from './extern.js';
|
|
3
|
-
import
|
|
4
|
-
import { VmModule } from './module.js';
|
|
5
|
-
import {
|
|
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
|
|
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,
|
|
95
|
+
export { VmFunction, isVmFunction, VmExtern, isVmExtern, VmModule, isVmModule, isVmWrapper };
|
|
96
|
+
export { wrapToVmValue, unwrapFromVmValue, toVmFunctionProxy, fromVmFunctionProxy } from './boundary.js';
|
|
96
97
|
|
|
97
|
-
/** 检查值是否为 Mirascript
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
package/src/vm/types/module.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/vm/types/wrapper.ts
CHANGED
|
@@ -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
|
+
}
|