@mirascript/mirascript 0.1.19 → 0.1.21
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-5AEWM4W6.js → chunk-4T5YE7LC.js} +241 -177
- package/dist/chunk-4T5YE7LC.js.map +6 -0
- package/dist/chunk-RNLB52WP.js +1 -0
- package/dist/chunk-RY7PYMXX.js +204 -0
- package/dist/chunk-RY7PYMXX.js.map +6 -0
- package/dist/{chunk-BFSB6SQK.js → chunk-XNGYORPT.js} +94 -192
- package/dist/chunk-XNGYORPT.js.map +6 -0
- package/dist/cli/index.js +6 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/compiler/compile-fast.d.ts.map +1 -1
- package/dist/compiler/create-script.d.ts +4 -4
- package/dist/compiler/create-script.d.ts.map +1 -1
- package/dist/compiler/emit/constants.d.ts +1 -2
- package/dist/compiler/emit/constants.d.ts.map +1 -1
- package/dist/compiler/emit/consts.d.ts +2 -2
- package/dist/compiler/emit/consts.d.ts.map +1 -1
- package/dist/compiler/emit/index.d.ts +2 -3
- package/dist/compiler/emit/index.d.ts.map +1 -1
- package/dist/compiler/emit/sourcemap.d.ts +1 -2
- package/dist/compiler/emit/sourcemap.d.ts.map +1 -1
- package/dist/compiler/emit-script.d.ts +10 -0
- package/dist/compiler/emit-script.d.ts.map +1 -0
- package/dist/compiler/generate-bytecode.d.ts +4 -2
- package/dist/compiler/generate-bytecode.d.ts.map +1 -1
- package/dist/compiler/index.d.ts +1 -6
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/keywords.d.ts +4 -0
- package/dist/compiler/keywords.d.ts.map +1 -0
- package/dist/compiler/load-module.d.ts +2 -0
- package/dist/compiler/load-module.d.ts.map +1 -0
- package/dist/compiler/worker.js +1 -1
- package/dist/helpers/constants.d.ts +2 -0
- package/dist/helpers/constants.d.ts.map +1 -1
- package/dist/helpers/serialize.d.ts.map +1 -1
- package/dist/helpers/utils.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -11
- package/dist/subtle.d.ts +5 -3
- package/dist/subtle.d.ts.map +1 -1
- package/dist/subtle.js +6 -5
- package/dist/vm/checkpoint.d.ts +1 -1
- package/dist/vm/checkpoint.d.ts.map +1 -1
- package/dist/vm/helpers.d.ts +6 -1
- package/dist/vm/helpers.d.ts.map +1 -1
- package/dist/vm/lib/global/json.d.ts.map +1 -1
- package/dist/vm/lib/helpers.d.ts +1 -1
- package/dist/vm/lib/helpers.d.ts.map +1 -1
- package/dist/vm/types/context.d.ts +7 -16
- package/dist/vm/types/context.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/compiler/compile-fast.ts +79 -14
- package/src/compiler/create-script.ts +8 -8
- package/src/compiler/emit/constants.ts +1 -2
- package/src/compiler/emit/consts.ts +166 -29
- package/src/compiler/emit/index.ts +40 -32
- package/src/compiler/emit/sourcemap.ts +21 -76
- package/src/compiler/emit-script.ts +30 -0
- package/src/compiler/generate-bytecode.ts +5 -8
- package/src/compiler/index.ts +16 -35
- package/src/compiler/keywords.ts +9 -0
- package/src/compiler/load-module.ts +3 -0
- package/src/helpers/constants.ts +2 -0
- package/src/helpers/serialize.ts +1 -4
- package/src/helpers/utils.ts +2 -7
- package/src/index.ts +2 -0
- package/src/subtle.ts +5 -9
- package/src/vm/checkpoint.ts +42 -17
- package/src/vm/helpers.ts +12 -5
- package/src/vm/lib/global/json.ts +2 -0
- package/src/vm/lib/helpers.ts +2 -1
- package/src/vm/types/context.ts +42 -29
- package/src/vm/types/wrapper.ts +3 -3
- package/dist/chunk-5AEWM4W6.js.map +0 -6
- package/dist/chunk-BFSB6SQK.js.map +0 -6
- package/dist/chunk-RIT53WVY.js +0 -1
- package/dist/chunk-RLWIIOH5.js +0 -12
- package/dist/chunk-RLWIIOH5.js.map +0 -6
- package/dist/compiler/emit/globals.d.ts +0 -17
- package/dist/compiler/emit/globals.d.ts.map +0 -1
- package/src/compiler/emit/globals.ts +0 -39
- /package/dist/{chunk-RIT53WVY.js.map → chunk-RNLB52WP.js.map} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { IRange } from '../diagnostic.js';
|
|
2
2
|
import type { ScriptInput, TranspileOptions } from '../types.js';
|
|
3
3
|
import { SourceMapGenerator } from 'source-map-js';
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
4
|
+
import { SCRIPT_PREFIX } from './constants.js';
|
|
5
|
+
import { toJsLiteral } from './consts.js';
|
|
6
6
|
|
|
7
7
|
const ORIGIN = `mira://MiraScript/`;
|
|
8
8
|
const { SOURCE_URL, SOURCE_MAPPING_URL } = ((source, mapping, url) => {
|
|
@@ -13,10 +13,11 @@ const { SOURCE_URL, SOURCE_MAPPING_URL } = ((source, mapping, url) => {
|
|
|
13
13
|
SOURCE_MAPPING_URL: prefix.concat(source, mapping, url),
|
|
14
14
|
};
|
|
15
15
|
})(`source`, `Mapping`, `URL`);
|
|
16
|
-
//
|
|
16
|
+
// 前 3 行固定为:
|
|
17
17
|
// (function anonymous($Add,$Aeq, ...
|
|
18
18
|
// ) {
|
|
19
|
-
|
|
19
|
+
// 'use strict';
|
|
20
|
+
const SOURCE_OFFSET = 4;
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Node.js Buffer 类型的简易声明,@mirascript/playground 调试环境下会直接加载此文件
|
|
@@ -33,79 +34,15 @@ const toDataUrl: (json: string) => string =
|
|
|
33
34
|
? (s) => `data:application/json;base64,${Buffer.from(s, 'utf8').toString('base64')}`
|
|
34
35
|
: (s) => `data:application/json;charset=utf-8,${encodeURIComponent(s)}`;
|
|
35
36
|
|
|
36
|
-
/** 添加全局变量的源映射 */
|
|
37
|
-
function addGlobalMappings(globalLine: string, fileName: string, map: SourceMapGenerator, globals: GlobalMap) {
|
|
38
|
-
let globalFile = `global;\n`;
|
|
39
|
-
map.addMapping({
|
|
40
|
-
generated: {
|
|
41
|
-
line: 3,
|
|
42
|
-
column: globalLine.indexOf(`global = `),
|
|
43
|
-
},
|
|
44
|
-
original: {
|
|
45
|
-
line: 1,
|
|
46
|
-
column: 0,
|
|
47
|
-
},
|
|
48
|
-
source: fileName,
|
|
49
|
-
name: 'global',
|
|
50
|
-
});
|
|
51
|
-
map.addMapping({
|
|
52
|
-
generated: {
|
|
53
|
-
line: 3,
|
|
54
|
-
column: SCRIPT_PREFIX.length,
|
|
55
|
-
},
|
|
56
|
-
original: {
|
|
57
|
-
line: 1,
|
|
58
|
-
column: 7,
|
|
59
|
-
},
|
|
60
|
-
source: fileName,
|
|
61
|
-
name: '',
|
|
62
|
-
});
|
|
63
|
-
let i = 1;
|
|
64
|
-
let pos = globalLine.indexOf(GLOBAL_HINT, SCRIPT_PREFIX.length) + GLOBAL_HINT.length;
|
|
65
|
-
for (const p of globals.values()) {
|
|
66
|
-
i++;
|
|
67
|
-
if (pos < 0) break;
|
|
68
|
-
const { v, n } = p;
|
|
69
|
-
pos = globalLine.indexOf(v, pos);
|
|
70
|
-
if (pos < 0) break;
|
|
71
|
-
map.addMapping({
|
|
72
|
-
generated: {
|
|
73
|
-
line: 3,
|
|
74
|
-
column: pos,
|
|
75
|
-
},
|
|
76
|
-
original: {
|
|
77
|
-
line: i,
|
|
78
|
-
column: 0,
|
|
79
|
-
},
|
|
80
|
-
source: fileName,
|
|
81
|
-
name: n,
|
|
82
|
-
});
|
|
83
|
-
globalFile += `${n};\n`;
|
|
84
|
-
}
|
|
85
|
-
map.addMapping({
|
|
86
|
-
generated: {
|
|
87
|
-
line: 3,
|
|
88
|
-
column: pos,
|
|
89
|
-
},
|
|
90
|
-
original: {
|
|
91
|
-
line: i,
|
|
92
|
-
column: 0,
|
|
93
|
-
},
|
|
94
|
-
source: fileName,
|
|
95
|
-
name: '',
|
|
96
|
-
});
|
|
97
|
-
map.setSourceContent(fileName, globalFile);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
37
|
let sourceId = 1;
|
|
101
38
|
/** 创建源映射 */
|
|
102
39
|
export function createSourceMap(
|
|
103
40
|
source: ScriptInput | undefined,
|
|
104
41
|
sourcemaps: readonly IRange[],
|
|
105
|
-
codeLines:
|
|
106
|
-
|
|
42
|
+
codeLines: string[],
|
|
43
|
+
functions: readonly number[],
|
|
107
44
|
options: TranspileOptions,
|
|
108
|
-
):
|
|
45
|
+
): void {
|
|
109
46
|
let fileName = (options.fileName ?? '').trim();
|
|
110
47
|
const hasSchema = /^\w+:/.test(fileName);
|
|
111
48
|
if (!hasSchema) {
|
|
@@ -154,15 +91,23 @@ export function createSourceMap(
|
|
|
154
91
|
source: fileName,
|
|
155
92
|
});
|
|
156
93
|
}
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
|
|
94
|
+
let fnNames = `'use strict';`;
|
|
95
|
+
if (typeof source === 'string') {
|
|
96
|
+
const lines = source.split(/\r?\n/);
|
|
97
|
+
for (let i = 0; i < functions.length; i++) {
|
|
98
|
+
const index = functions[i]!;
|
|
99
|
+
const mapping = sourcemaps[index];
|
|
100
|
+
const line = mapping ? lines[mapping.startLineNumber - 1] : undefined;
|
|
101
|
+
const fnName = mapping && line ? line.slice(mapping.startColumn - 1, mapping.endColumn - 1) : '';
|
|
102
|
+
fnNames += `var fnName_${i} = ${fnName ? toJsLiteral(fnName) : 'null'};`;
|
|
103
|
+
}
|
|
160
104
|
}
|
|
161
105
|
const sourceURL = hasSchema ? fileName : `${ORIGIN}${fileName}`;
|
|
162
106
|
const dataUrl = toDataUrl(map.toString());
|
|
163
|
-
|
|
107
|
+
codeLines.unshift(fnNames);
|
|
108
|
+
codeLines.push(
|
|
164
109
|
// Prevent source map from being recognized as of this file
|
|
165
110
|
`${SOURCE_URL}=${sourceURL}.js`,
|
|
166
111
|
`${SOURCE_MAPPING_URL}=${dataUrl}`,
|
|
167
|
-
|
|
112
|
+
);
|
|
168
113
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ScriptInput, TranspileOptions } from './types.js';
|
|
2
|
+
import type { VmBytecodeResult } from './generate-bytecode.js';
|
|
3
|
+
import { emit } from './emit/index.js';
|
|
4
|
+
import { createScript, type VmScript } from './create-script.js';
|
|
5
|
+
import { DiagnosticCode, formatDiagnostics, parseDiagnostics } from './diagnostic.js';
|
|
6
|
+
|
|
7
|
+
/** 报告编译错误 */
|
|
8
|
+
export function reportDiagnostic(source: ScriptInput, diagnostics: Uint32Array, fileName: string | undefined): never {
|
|
9
|
+
const parsed = parseDiagnostics(source, diagnostics);
|
|
10
|
+
const messages = formatDiagnostics(parsed.errors, source, fileName);
|
|
11
|
+
throw new Error(`Failed to compile:\n${messages.join('\n')}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 生成 MiraScript 对应的 JavaScript 代码
|
|
16
|
+
*/
|
|
17
|
+
export function emitScript(
|
|
18
|
+
source: ScriptInput,
|
|
19
|
+
[code, diagnostics]: VmBytecodeResult,
|
|
20
|
+
options: TranspileOptions,
|
|
21
|
+
): VmScript {
|
|
22
|
+
if (!code) {
|
|
23
|
+
reportDiagnostic(source, diagnostics, options.fileName);
|
|
24
|
+
}
|
|
25
|
+
const sourcemaps = options.sourceMap
|
|
26
|
+
? parseDiagnostics(source, diagnostics, (c) => c === DiagnosticCode.SourceMap).sourcemaps
|
|
27
|
+
: [];
|
|
28
|
+
const target = emit(source, code, sourcemaps, options);
|
|
29
|
+
return createScript(source, options.input_mode ?? 'Script', target);
|
|
30
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { getModule, loadModule } from '@mirascript/bindings';
|
|
2
2
|
import type { CompileOptions, ScriptInput } from './types.js';
|
|
3
3
|
|
|
4
|
+
/** MiraScript 字节码 */
|
|
5
|
+
export type VmBytecodeResult = [code: Uint8Array | undefined, diagnostics: Uint32Array];
|
|
6
|
+
|
|
4
7
|
/**
|
|
5
8
|
* 生成 MiraScript 字节码
|
|
6
9
|
*/
|
|
7
|
-
export function generateBytecodeSync(
|
|
8
|
-
script: ScriptInput,
|
|
9
|
-
options: CompileOptions,
|
|
10
|
-
): [Uint8Array | undefined, Uint32Array] {
|
|
10
|
+
export function generateBytecodeSync(script: ScriptInput, options: CompileOptions): VmBytecodeResult {
|
|
11
11
|
const module = getModule();
|
|
12
12
|
const result = module.compileSync(script, options);
|
|
13
13
|
return [result.chunk, result.diagnostics];
|
|
@@ -16,10 +16,7 @@ export function generateBytecodeSync(
|
|
|
16
16
|
/**
|
|
17
17
|
* 生成 MiraScript 字节码
|
|
18
18
|
*/
|
|
19
|
-
export async function generateBytecode(
|
|
20
|
-
script: ScriptInput,
|
|
21
|
-
options: CompileOptions,
|
|
22
|
-
): Promise<[Uint8Array | undefined, Uint32Array]> {
|
|
19
|
+
export async function generateBytecode(script: ScriptInput, options: CompileOptions): Promise<VmBytecodeResult> {
|
|
23
20
|
if (options == null) {
|
|
24
21
|
throw new TypeError('options must be provided');
|
|
25
22
|
}
|
package/src/compiler/index.ts
CHANGED
|
@@ -1,54 +1,34 @@
|
|
|
1
|
-
import
|
|
1
|
+
import './load-module.js';
|
|
2
2
|
import type { ScriptInput, TranspileOptions } from './types.js';
|
|
3
|
-
import { emit } from './emit/index.js';
|
|
4
3
|
import { createScript, type VmScript } from './create-script.js';
|
|
5
4
|
import { compileFast } from './compile-fast.js';
|
|
6
|
-
import { DiagnosticCode, formatDiagnostics, parseDiagnostics } from './diagnostic.js';
|
|
7
5
|
import { generateBytecode, generateBytecodeSync } from './generate-bytecode.js';
|
|
8
6
|
import { compileWorker } from './worker-manager.js';
|
|
9
|
-
|
|
7
|
+
import { emitScript, reportDiagnostic } from './emit-script.js';
|
|
10
8
|
|
|
11
|
-
export { generateBytecode, generateBytecodeSync };
|
|
12
9
|
export * from './types.js';
|
|
13
10
|
export type { VmScript };
|
|
14
11
|
|
|
15
12
|
// 目前编译速度约 2000kB/s
|
|
16
13
|
const WORKER_MIN_LEN = typeof Worker != 'function' ? Number.MAX_VALUE : 1024;
|
|
17
14
|
|
|
18
|
-
/**
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 生成 MiraScript 对应的 JavaScript 代码
|
|
27
|
-
*/
|
|
28
|
-
export function emitScript(
|
|
29
|
-
source: ScriptInput,
|
|
30
|
-
[code, diagnostics]: [Uint8Array | undefined, Uint32Array],
|
|
31
|
-
options: TranspileOptions,
|
|
32
|
-
): VmScript {
|
|
33
|
-
if (!code) {
|
|
34
|
-
reportDiagnostic(source, diagnostics, options.fileName);
|
|
15
|
+
/** 设置 options */
|
|
16
|
+
function getOptions(options: TranspileOptions | undefined): TranspileOptions {
|
|
17
|
+
options ??= {};
|
|
18
|
+
if (options.sourceMap) {
|
|
19
|
+
options.diagnostic_sourcemap = true;
|
|
20
|
+
// https://tc39.es/ecma426/#sec-terms-and-definitions-colun
|
|
21
|
+
options.diagnostic_position_encoding ??= 'Utf16';
|
|
35
22
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
: [];
|
|
39
|
-
const target = emit(source, code, sourcemaps, options);
|
|
40
|
-
return createScript(source, target);
|
|
23
|
+
options.input_mode ??= 'Script';
|
|
24
|
+
return options;
|
|
41
25
|
}
|
|
42
26
|
|
|
43
27
|
/**
|
|
44
28
|
* 生成 MiraScript 对应的 JavaScript 代码
|
|
45
29
|
*/
|
|
46
|
-
export async function compile(this: void, source: ScriptInput, options
|
|
47
|
-
|
|
48
|
-
options.diagnostic_sourcemap = true;
|
|
49
|
-
// https://tc39.es/ecma426/#sec-terms-and-definitions-colun
|
|
50
|
-
options.diagnostic_position_encoding ??= 'Utf16';
|
|
51
|
-
}
|
|
30
|
+
export async function compile(this: void, source: ScriptInput, options?: TranspileOptions): Promise<VmScript> {
|
|
31
|
+
options = getOptions(options);
|
|
52
32
|
if (typeof source == 'string') {
|
|
53
33
|
const result = compileFast(source, options);
|
|
54
34
|
if (result) return result;
|
|
@@ -61,12 +41,13 @@ export async function compile(this: void, source: ScriptInput, options: Transpil
|
|
|
61
41
|
if (target == null) {
|
|
62
42
|
reportDiagnostic(source, diagnostics, options.fileName);
|
|
63
43
|
}
|
|
64
|
-
return createScript(source, target);
|
|
44
|
+
return createScript(source, options.input_mode ?? 'Script', target);
|
|
65
45
|
}
|
|
66
46
|
/**
|
|
67
47
|
* 生成 MiraScript 对应的 JavaScript 代码
|
|
68
48
|
*/
|
|
69
|
-
export function compileSync(this: void, source: ScriptInput, options
|
|
49
|
+
export function compileSync(this: void, source: ScriptInput, options?: TranspileOptions): VmScript {
|
|
50
|
+
options = getOptions(options);
|
|
70
51
|
if (typeof source == 'string') {
|
|
71
52
|
const result = compileFast(source, options);
|
|
72
53
|
if (result) return result;
|
package/src/helpers/constants.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export const REG_IDENTIFIER = /(?:_+|@+|\$+|\p{XID_Start})\p{XID_Continue}*/u;
|
|
2
2
|
export const REG_ORDINAL =
|
|
3
3
|
/(?:214748364[0-7]|21474836[0-3]\d|2147483[0-5]\d{2}|214748[0-2]\d{3}|21474[0-7]\d{4}|2147[0-3]\d{5}|214[0-6]\d{6}|21[0-3]\d{7}|20\d{8}|1\d{9}|[1-9]\d{0,8}|0)/;
|
|
4
|
+
export const REG_IDENTIFIER_FULL = new RegExp(`^${REG_IDENTIFIER.source}$`, REG_IDENTIFIER.flags);
|
|
5
|
+
export const REG_ORDINAL_FULL = new RegExp(`^${REG_ORDINAL.source}$`, REG_ORDINAL.flags);
|
|
4
6
|
export const REG_WHITESPACE = /[ \t\v\f\r\n]/u;
|
|
5
7
|
export const REG_HEX = /0[xX][a-fA-F0-9_]*[a-fA-F0-9]/;
|
|
6
8
|
export const REG_OCT = /0[oO][0-7_]*[0-7]/;
|
package/src/helpers/serialize.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { VmArray, VmExtern, VmFunction, VmModule, VmAny, VmRecord } from '../vm/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { REG_IDENTIFIER_FULL, REG_ORDINAL_FULL } from './constants.js';
|
|
3
3
|
import { entries, hasOwn, isFinite, isNaN } from '../helpers/utils.js';
|
|
4
4
|
import {
|
|
5
5
|
getVmFunctionInfo,
|
|
@@ -12,9 +12,6 @@ import {
|
|
|
12
12
|
} from './types.js';
|
|
13
13
|
import type { VmWrapper } from '../vm/types/wrapper.js';
|
|
14
14
|
|
|
15
|
-
const REG_IDENTIFIER_FULL = new RegExp(`^${REG_IDENTIFIER.source}$`, REG_IDENTIFIER.flags);
|
|
16
|
-
const REG_ORDINAL_FULL = new RegExp(`^${REG_ORDINAL.source}$`, REG_ORDINAL.flags);
|
|
17
|
-
|
|
18
15
|
/** 序列化设置 */
|
|
19
16
|
export interface SerializeOptions {
|
|
20
17
|
/** 最大递归深度,超过该深度的值将被序列化为 `nil`,默认值为 128 */
|
package/src/helpers/utils.ts
CHANGED
|
@@ -6,12 +6,7 @@ export const { apply } = Reflect;
|
|
|
6
6
|
/**
|
|
7
7
|
* Determines whether an object has an enumerable property with the specified name.
|
|
8
8
|
*/
|
|
9
|
-
export const hasOwnEnumerable = Function.call.bind
|
|
10
|
-
object['propertyIsEnumerable'],
|
|
11
|
-
[],
|
|
12
|
-
[o: object, v: PropertyKey],
|
|
13
|
-
boolean
|
|
14
|
-
>(
|
|
9
|
+
export const hasOwnEnumerable = Function.call.bind(
|
|
15
10
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
16
11
|
Object.prototype.propertyIsEnumerable,
|
|
17
|
-
);
|
|
12
|
+
) as (o: object, v: PropertyKey) => boolean;
|
package/src/index.ts
CHANGED
package/src/subtle.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import './compiler/load-module.js';
|
|
2
2
|
|
|
3
|
+
export { keywords } from './compiler/keywords.js';
|
|
3
4
|
export * as constants from './helpers/constants.js';
|
|
4
5
|
export * as convert from './helpers/convert/index.js';
|
|
5
6
|
export { DefaultVmContext } from './vm/types/context.js';
|
|
6
7
|
export * as operations from './vm/operations.js';
|
|
8
|
+
export * as helpers from './vm/helpers.js';
|
|
7
9
|
export {
|
|
8
10
|
display as serializeForDisplay,
|
|
9
11
|
serialize,
|
|
@@ -18,11 +20,5 @@ export {
|
|
|
18
20
|
} from './helpers/serialize.js';
|
|
19
21
|
export { lib } from './vm/lib/index.js';
|
|
20
22
|
export * from './compiler/diagnostic.js';
|
|
21
|
-
export {
|
|
22
|
-
|
|
23
|
-
/** 所有 MiraScript 关键字 */
|
|
24
|
-
export let keywords: () => readonly string[] = () => {
|
|
25
|
-
const kw = Object.freeze(getModule().keywords());
|
|
26
|
-
keywords = () => kw;
|
|
27
|
-
return kw;
|
|
28
|
-
};
|
|
23
|
+
export { emitScript } from './compiler/emit-script.js';
|
|
24
|
+
export { generateBytecode, generateBytecodeSync, type VmBytecodeResult } from './compiler/generate-bytecode.js';
|
package/src/vm/checkpoint.ts
CHANGED
|
@@ -1,32 +1,44 @@
|
|
|
1
|
+
/* eslint-disable unicorn/prefer-math-trunc */
|
|
1
2
|
import { isNaN } from '../helpers/utils.js';
|
|
2
3
|
// 不使用 performance.now(),精度够即可,且性能开销更小
|
|
3
4
|
const { now } = Date;
|
|
4
|
-
const TIME_ORIGIN = now() - 1000 * 3600 *
|
|
5
|
-
const timestamp = () => now() - TIME_ORIGIN;
|
|
5
|
+
const TIME_ORIGIN = now() - 1000 * 3600 * 12; // 防止系统时间被调整到过去时出问题
|
|
6
|
+
const timestamp = () => (now() - TIME_ORIGIN) | 0;
|
|
6
7
|
|
|
8
|
+
const CP_DEFAULT_INTERVAL = 100; // 每 100 次调用检查一次
|
|
7
9
|
const MAX_DEPTH = 128;
|
|
8
10
|
const CP_UNSET = -1;
|
|
9
11
|
/** Default timeout in milliseconds */
|
|
10
12
|
const CP_DEFAULT_TIMEOUT = 100;
|
|
11
13
|
|
|
12
14
|
let cpDepth = 0;
|
|
13
|
-
let cp = CP_UNSET;
|
|
14
|
-
let cpTimeout = CP_DEFAULT_TIMEOUT;
|
|
15
|
+
let cp = CP_UNSET | 0;
|
|
16
|
+
let cpTimeout = CP_DEFAULT_TIMEOUT | 0;
|
|
17
|
+
let cpInterval = CP_DEFAULT_INTERVAL | 0;
|
|
18
|
+
let cpCounter = 0;
|
|
15
19
|
/** 检查点 */
|
|
16
20
|
export function Cp(): void {
|
|
17
|
-
if (cp === CP_UNSET) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
if ((cp | 0) === (CP_UNSET | 0)) {
|
|
22
|
+
cpCounter = 0;
|
|
23
|
+
cp = timestamp() | 0;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// 不是每次都查时间
|
|
27
|
+
if ((cpCounter = (cpCounter + 1) | 0) % (cpInterval | 0) !== 0) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
cpCounter = 0;
|
|
31
|
+
if ((timestamp() | 0) - (cp | 0) >= (cpTimeout | 0)) {
|
|
32
|
+
throw new RangeError('Execution timed out');
|
|
21
33
|
}
|
|
22
34
|
}
|
|
23
35
|
/** 检查点 */
|
|
24
36
|
export function CpEnter(): void {
|
|
25
|
-
cpDepth
|
|
26
|
-
if (cpDepth <= 1) {
|
|
27
|
-
cp = timestamp();
|
|
37
|
+
cpDepth = (cpDepth | 0) + 1;
|
|
38
|
+
if ((cpDepth | 0) <= 1) {
|
|
39
|
+
cp = timestamp() | 0;
|
|
28
40
|
cpDepth = 1;
|
|
29
|
-
} else if (cpDepth > MAX_DEPTH) {
|
|
41
|
+
} else if ((cpDepth | 0) > MAX_DEPTH) {
|
|
30
42
|
throw new RangeError('Maximum call depth exceeded');
|
|
31
43
|
} else {
|
|
32
44
|
Cp();
|
|
@@ -34,18 +46,31 @@ export function CpEnter(): void {
|
|
|
34
46
|
}
|
|
35
47
|
/** 检查点 */
|
|
36
48
|
export function CpExit(): void {
|
|
37
|
-
cpDepth
|
|
38
|
-
if (cpDepth < 1) {
|
|
39
|
-
cp = CP_UNSET;
|
|
49
|
+
cpDepth = (cpDepth | 0) - 1;
|
|
50
|
+
if ((cpDepth | 0) < 1) {
|
|
51
|
+
cp = CP_UNSET | 0;
|
|
40
52
|
cpDepth = 0;
|
|
41
53
|
} else {
|
|
42
54
|
Cp();
|
|
43
55
|
}
|
|
44
56
|
}
|
|
57
|
+
|
|
58
|
+
const INT_MAX = 0x7fff_ffff;
|
|
45
59
|
/** 设置检查点超时时间 */
|
|
46
|
-
export function configCheckpoint(
|
|
60
|
+
export function configCheckpoint(
|
|
61
|
+
timeout: number = CP_DEFAULT_TIMEOUT,
|
|
62
|
+
checkInterval: number = CP_DEFAULT_INTERVAL,
|
|
63
|
+
): void {
|
|
47
64
|
if (typeof timeout !== 'number' || timeout <= 0 || isNaN(timeout)) {
|
|
48
65
|
throw new RangeError('Invalid timeout value');
|
|
66
|
+
} else if (timeout > INT_MAX) {
|
|
67
|
+
timeout = INT_MAX;
|
|
68
|
+
}
|
|
69
|
+
if (typeof checkInterval !== 'number' || checkInterval <= 0 || isNaN(checkInterval)) {
|
|
70
|
+
throw new RangeError('Invalid check interval value');
|
|
71
|
+
} else if (checkInterval > INT_MAX) {
|
|
72
|
+
checkInterval = INT_MAX;
|
|
49
73
|
}
|
|
50
|
-
cpTimeout =
|
|
74
|
+
cpTimeout = timeout | 0;
|
|
75
|
+
cpInterval = checkInterval | 0;
|
|
51
76
|
}
|
package/src/vm/helpers.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineProperty, isFinite } from '../helpers/utils.js';
|
|
1
|
+
import { create, defineProperty, isFinite } from '../helpers/utils.js';
|
|
2
2
|
import { VM_ARRAY_MAX_LENGTH, VM_FUNCTION_ANONYMOUS_NAME } from '../helpers/constants.js';
|
|
3
3
|
import { isVmConst } from '../helpers/types.js';
|
|
4
4
|
import { $AssertInit, $ToNumber } from './operations.js';
|
|
@@ -9,6 +9,7 @@ import { VmFunction } from './types/function.js';
|
|
|
9
9
|
|
|
10
10
|
export { Cp, CpEnter, CpExit } from './checkpoint.js';
|
|
11
11
|
|
|
12
|
+
/** 过滤剩余参数数组 */
|
|
12
13
|
export const Vargs = (varags: VmAny[]): VmArray => {
|
|
13
14
|
for (let i = 0, l = varags.length; i < l; i++) {
|
|
14
15
|
const el = varags[i];
|
|
@@ -18,23 +19,29 @@ export const Vargs = (varags: VmAny[]): VmArray => {
|
|
|
18
19
|
}
|
|
19
20
|
return varags as VmArray;
|
|
20
21
|
};
|
|
22
|
+
|
|
23
|
+
/** 构造 record | array 元素 */
|
|
21
24
|
export const Element = (value: VmAny): VmConst => {
|
|
22
25
|
$AssertInit(value);
|
|
23
26
|
if (!isVmConst(value)) return null;
|
|
24
27
|
return value;
|
|
25
28
|
};
|
|
26
29
|
|
|
30
|
+
const EMPTY = create(null);
|
|
31
|
+
/** 构造 record 可选元素 */
|
|
27
32
|
export const ElementOpt = (key: string, value: VmAny): VmConst => {
|
|
28
33
|
$AssertInit(value);
|
|
29
|
-
if (value == null || !isVmConst(value)) return
|
|
30
|
-
return { [key]: value };
|
|
34
|
+
if (value == null || !isVmConst(value)) return EMPTY;
|
|
35
|
+
return { __proto__: null, [key]: value };
|
|
31
36
|
};
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
/** 构造函数和函数表达式 */
|
|
39
|
+
export const Function = (name: string | null | undefined, fn: VmFunctionLike): VmFunction => {
|
|
40
|
+
defineProperty(fn, 'name', { value: name || VM_FUNCTION_ANONYMOUS_NAME });
|
|
35
41
|
return VmFunction(fn, { isLib: false, injectCp: false });
|
|
36
42
|
};
|
|
37
43
|
|
|
44
|
+
/** 读取闭包上值 */
|
|
38
45
|
export const Upvalue = (value: VmAny): VmValue => {
|
|
39
46
|
$AssertInit(value);
|
|
40
47
|
return value;
|
|
@@ -21,6 +21,7 @@ export const to_json = VmLib(
|
|
|
21
21
|
paramsType: { data: 'any' },
|
|
22
22
|
returnsType: 'string',
|
|
23
23
|
examples: ['to_json([1, 2, 3]) // "[1,2,3]"'],
|
|
24
|
+
injectCp: true,
|
|
24
25
|
},
|
|
25
26
|
);
|
|
26
27
|
|
|
@@ -40,5 +41,6 @@ export const from_json = VmLib(
|
|
|
40
41
|
paramsType: { json: 'string', fallback: 'any' },
|
|
41
42
|
returnsType: 'any',
|
|
42
43
|
examples: [`from_json('{"a":1}') // (a: 1)`],
|
|
44
|
+
injectCp: true,
|
|
43
45
|
},
|
|
44
46
|
);
|
package/src/vm/lib/helpers.ts
CHANGED
|
@@ -274,7 +274,7 @@ export function map(
|
|
|
274
274
|
/** 库函数选项 */
|
|
275
275
|
export type VmLibOption = Pick<
|
|
276
276
|
VmFunctionOption,
|
|
277
|
-
'summary' | 'params' | 'paramsType' | 'returns' | 'returnsType' | 'examples'
|
|
277
|
+
'summary' | 'params' | 'paramsType' | 'returns' | 'returnsType' | 'examples' | 'injectCp'
|
|
278
278
|
>;
|
|
279
279
|
/** 库函数 */
|
|
280
280
|
export type VmLib<T extends VmFunctionLike | VmConst = VmFunctionLike> = (T extends VmFunctionLike ? T : { value: T }) &
|
|
@@ -295,5 +295,6 @@ export function VmLib<
|
|
|
295
295
|
ret.returnsType = option.returnsType;
|
|
296
296
|
ret.summary = option.summary;
|
|
297
297
|
ret.examples = option.examples;
|
|
298
|
+
ret.injectCp = option.injectCp ?? false;
|
|
298
299
|
return ret as VmLib<T> & P;
|
|
299
300
|
}
|