@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,11 +1,13 @@
|
|
|
1
1
|
import { wrapScript, type VmScript } from './create-script.js';
|
|
2
2
|
import type { TranspileOptions } from './types.js';
|
|
3
3
|
import { GlobalFallback } from '../vm/helpers.js';
|
|
4
|
+
import type { VmContext, VmValue } from '../vm/index.js';
|
|
4
5
|
import { isFinite } from '../helpers/utils.js';
|
|
6
|
+
import { keywords } from './keywords.js';
|
|
5
7
|
|
|
6
|
-
const REG_NUMBER_FULL =
|
|
7
|
-
//
|
|
8
|
-
const REG_IDENTIFIER_FAST = /^(
|
|
8
|
+
const REG_NUMBER_FULL = /^(?:[+-])?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
|
|
9
|
+
// 只识别 ASCII 范围内的标识符,以提升性能
|
|
10
|
+
const REG_IDENTIFIER_FAST = /^(?:\$+|@+|[A-Za-z])[a-zA-Z0-9_]*$/;
|
|
9
11
|
|
|
10
12
|
// 为避免结果不一致,只对常量进行处理
|
|
11
13
|
|
|
@@ -17,41 +19,103 @@ const REG_IDENTIFIER_FAST = /^(?:\$+|@+)[a-zA-Z0-9_]*$/;
|
|
|
17
19
|
|
|
18
20
|
const FAST_SCRIPT_MAX_LEN = 32;
|
|
19
21
|
|
|
22
|
+
/** 构造返回常量的函数 */
|
|
23
|
+
function constantFiniteNumber(value: number): () => number {
|
|
24
|
+
if (value === 0) {
|
|
25
|
+
if (Object.is(value, -0)) {
|
|
26
|
+
return () => -0;
|
|
27
|
+
} else {
|
|
28
|
+
return () => 0;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call
|
|
32
|
+
return new Function(`return () => ${value};`)() as () => number;
|
|
33
|
+
}
|
|
34
|
+
/** 构造返回常量的函数 */
|
|
35
|
+
function constantString(value: string): () => string {
|
|
36
|
+
const code = JSON.stringify(value);
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call
|
|
38
|
+
return new Function(`return () => ${code};`)() as () => string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** 构造返回常量的函数 */
|
|
42
|
+
function nan(): () => number {
|
|
43
|
+
return () => 0 / 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 构造返回常量的函数 */
|
|
47
|
+
function posInf(): () => number {
|
|
48
|
+
return () => +1 / 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 构造返回常量的函数 */
|
|
52
|
+
function negInf(): () => number {
|
|
53
|
+
return () => -1 / 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** 构造返回常量的函数 */
|
|
57
|
+
function constantBoolean(value: boolean): () => boolean {
|
|
58
|
+
if (value) {
|
|
59
|
+
return () => true;
|
|
60
|
+
} else {
|
|
61
|
+
return () => false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 构造返回常量的函数 */
|
|
66
|
+
function nil(): () => null {
|
|
67
|
+
return () => null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** 构造返回全局变量的函数 */
|
|
71
|
+
function globalVariable(id: string): (global: VmContext | undefined) => VmValue {
|
|
72
|
+
const code = `return (global = GlobalFallback()) => global.get(\`${id}\`);`;
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call
|
|
75
|
+
return new Function('GlobalFallback', code)(GlobalFallback) as (global: VmContext | undefined) => VmValue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let kw: Set<string> | undefined;
|
|
79
|
+
|
|
20
80
|
/**
|
|
21
81
|
* 对短代码进行编译
|
|
22
82
|
*/
|
|
23
83
|
function compileScriptFast(code: string, options: TranspileOptions): VmScript | undefined {
|
|
24
84
|
if (code.length > FAST_SCRIPT_MAX_LEN) return undefined; // 超过长度限制,直接返回 undefined
|
|
25
|
-
|
|
85
|
+
const mode = options.input_mode ?? 'Script';
|
|
26
86
|
const trimmedCode = code.trim();
|
|
27
87
|
if (!trimmedCode) {
|
|
28
|
-
return wrapScript(code, ()
|
|
88
|
+
return wrapScript(code, mode, nil());
|
|
29
89
|
}
|
|
30
90
|
switch (trimmedCode) {
|
|
31
91
|
case 'nil':
|
|
32
|
-
return wrapScript(code, ()
|
|
92
|
+
return wrapScript(code, mode, nil());
|
|
33
93
|
case 'true':
|
|
34
|
-
return wrapScript(code, (
|
|
94
|
+
return wrapScript(code, mode, constantBoolean(true));
|
|
35
95
|
case 'false':
|
|
36
|
-
return wrapScript(code, (
|
|
96
|
+
return wrapScript(code, mode, constantBoolean(false));
|
|
37
97
|
case 'nan':
|
|
38
|
-
return wrapScript(code, ()
|
|
98
|
+
return wrapScript(code, mode, nan());
|
|
39
99
|
case 'inf':
|
|
40
100
|
case '+inf':
|
|
41
|
-
return wrapScript(code, ()
|
|
101
|
+
return wrapScript(code, mode, posInf());
|
|
42
102
|
case '-inf':
|
|
43
|
-
return wrapScript(code, ()
|
|
103
|
+
return wrapScript(code, mode, negInf());
|
|
44
104
|
}
|
|
45
105
|
if (REG_IDENTIFIER_FAST.test(trimmedCode)) {
|
|
46
106
|
// 直接返回标识符
|
|
47
107
|
const id = trimmedCode;
|
|
48
|
-
|
|
108
|
+
kw ??= new Set(keywords());
|
|
109
|
+
if (kw.has(id)) {
|
|
110
|
+
return undefined; // 关键字不处理
|
|
111
|
+
}
|
|
112
|
+
return wrapScript(code, mode, globalVariable(id));
|
|
49
113
|
}
|
|
50
114
|
if (REG_NUMBER_FULL.test(trimmedCode)) {
|
|
51
115
|
const num = Number(trimmedCode);
|
|
52
116
|
if (!isFinite(num)) return undefined;
|
|
53
117
|
// 直接返回数字
|
|
54
|
-
return wrapScript(code, (
|
|
118
|
+
return wrapScript(code, mode, constantFiniteNumber(num));
|
|
55
119
|
}
|
|
56
120
|
return undefined;
|
|
57
121
|
}
|
|
@@ -65,8 +129,9 @@ function compileTemplateFast(code: string, options: TranspileOptions): VmScript
|
|
|
65
129
|
if (code.length > FAST_TEMPLATE_MAX_LEN) return undefined; // 超过长度限制,直接返回 undefined
|
|
66
130
|
|
|
67
131
|
if (!code.includes('$')) {
|
|
132
|
+
const mode = options.input_mode ?? 'Template';
|
|
68
133
|
// 不包含插值的模板
|
|
69
|
-
return wrapScript(code, (
|
|
134
|
+
return wrapScript(code, mode, constantString(code));
|
|
70
135
|
}
|
|
71
136
|
return undefined;
|
|
72
137
|
}
|
|
@@ -2,36 +2,36 @@ import { kVmScript, VM_SCRIPT_NAME } from '../helpers/constants.js';
|
|
|
2
2
|
import { defineProperty } from '../helpers/utils.js';
|
|
3
3
|
import { keys, values } from '../vm/env.js';
|
|
4
4
|
import type { VmValue, VmContext } from '../vm/types/index.js';
|
|
5
|
-
import type { ScriptInput } from './types.js';
|
|
5
|
+
import type { InputMode, ScriptInput } from './types.js';
|
|
6
6
|
|
|
7
7
|
/** Mirascript 脚本 */
|
|
8
8
|
export type VmScriptLike = (global?: VmContext) => VmValue;
|
|
9
9
|
|
|
10
10
|
/** Mirascript 脚本 */
|
|
11
11
|
export type VmScript = VmScriptLike & {
|
|
12
|
-
readonly [kVmScript]:
|
|
12
|
+
readonly [kVmScript]: InputMode;
|
|
13
13
|
/** 原始代码 */
|
|
14
14
|
readonly source: string;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
/** 生成 JS 函数 */
|
|
18
|
-
export function wrapScript(source: ScriptInput, script: VmScriptLike): VmScript {
|
|
18
|
+
export function wrapScript(source: ScriptInput, mode: InputMode, script: VmScriptLike): VmScript {
|
|
19
19
|
/* c8 ignore next 3 */
|
|
20
20
|
if (kVmScript in script) {
|
|
21
21
|
return script as VmScriptLike as VmScript;
|
|
22
22
|
}
|
|
23
23
|
defineProperty(script, 'name', { value: VM_SCRIPT_NAME });
|
|
24
|
-
defineProperty(script, kVmScript, { value:
|
|
24
|
+
defineProperty(script, kVmScript, { value: mode });
|
|
25
25
|
if (typeof source === 'string') {
|
|
26
|
-
defineProperty(script, 'source', { value: source
|
|
26
|
+
defineProperty(script, 'source', { value: source });
|
|
27
27
|
} else if (source instanceof Uint8Array) {
|
|
28
|
-
defineProperty(script, 'source', { value: '<buffer>'
|
|
28
|
+
defineProperty(script, 'source', { value: '<buffer>' });
|
|
29
29
|
}
|
|
30
30
|
return script as VmScript;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/** 生成 JS 函数 */
|
|
34
|
-
export function createScript(source: ScriptInput, code: string): VmScript {
|
|
34
|
+
export function createScript(source: ScriptInput, mode: InputMode, code: string): VmScript {
|
|
35
35
|
let script;
|
|
36
36
|
try {
|
|
37
37
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call
|
|
@@ -40,5 +40,5 @@ export function createScript(source: ScriptInput, code: string): VmScript {
|
|
|
40
40
|
} catch (error) {
|
|
41
41
|
throw new Error(`Failed to create script`, { cause: error });
|
|
42
42
|
}
|
|
43
|
-
return wrapScript(source, script);
|
|
43
|
+
return wrapScript(source, mode, script);
|
|
44
44
|
}
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export const SCRIPT_PREFIX = `
|
|
2
|
-
export const GLOBAL_HINT = `/* globals */`;
|
|
1
|
+
export const SCRIPT_PREFIX = `return ((global = GlobalFallback()) => { try { CpEnter();`;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { VmPrimitive } from '../../vm/index.js';
|
|
2
|
-
|
|
2
|
+
const { stringify } = JSON;
|
|
3
3
|
/** 将值转为 JS 字面量 */
|
|
4
4
|
export function toJsLiteral(value: VmPrimitive | undefined): string {
|
|
5
5
|
/* c8 ignore next 2 */
|
|
6
6
|
if (value === null) return 'null';
|
|
7
7
|
if (value === undefined) return 'undefined';
|
|
8
8
|
if (typeof value == 'string') {
|
|
9
|
-
return
|
|
9
|
+
return stringify(value);
|
|
10
10
|
}
|
|
11
11
|
// JSON 无法处理 NaN 等特殊数字
|
|
12
12
|
if (value === 0) {
|
|
@@ -16,32 +16,169 @@ export function toJsLiteral(value: VmPrimitive | undefined): string {
|
|
|
16
16
|
return String(value satisfies number | boolean);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
19
|
+
const DECODER = new TextDecoder();
|
|
20
|
+
const { fromCharCode } = String;
|
|
21
|
+
/** 解码 Ascii */
|
|
22
|
+
function shortStringInJS(buf: Uint8Array, begin: number, length: number): string | undefined {
|
|
23
|
+
if (length < 4) {
|
|
24
|
+
if (length < 2) {
|
|
25
|
+
if (length === 0) return '';
|
|
26
|
+
const a = buf[begin]!;
|
|
27
|
+
if ((a & 0x80) > 0) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
return fromCharCode(a);
|
|
31
|
+
}
|
|
32
|
+
const a = buf[begin++]!;
|
|
33
|
+
const b = buf[begin++]!;
|
|
34
|
+
if ((a & 0x80) > 0 || (b & 0x80) > 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (length < 3) return fromCharCode(a, b);
|
|
38
|
+
const c = buf[begin++]!;
|
|
39
|
+
if ((c & 0x80) > 0) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
return fromCharCode(a, b, c);
|
|
43
|
+
}
|
|
44
|
+
const a = buf[begin++]!;
|
|
45
|
+
const b = buf[begin++]!;
|
|
46
|
+
const c = buf[begin++]!;
|
|
47
|
+
const d = buf[begin++]!;
|
|
48
|
+
if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (length < 6) {
|
|
52
|
+
if (length === 4) return fromCharCode(a, b, c, d);
|
|
53
|
+
const e = buf[begin++]!;
|
|
54
|
+
if ((e & 0x80) > 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
return fromCharCode(a, b, c, d, e);
|
|
58
|
+
}
|
|
59
|
+
const e = buf[begin++]!;
|
|
60
|
+
const f = buf[begin++]!;
|
|
61
|
+
if ((e & 0x80) > 0 || (f & 0x80) > 0) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (length < 8) {
|
|
65
|
+
if (length < 7) return fromCharCode(a, b, c, d, e, f);
|
|
66
|
+
const g = buf[begin++]!;
|
|
67
|
+
if ((g & 0x80) > 0) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
return fromCharCode(a, b, c, d, e, f, g);
|
|
71
|
+
}
|
|
72
|
+
const g = buf[begin++]!;
|
|
73
|
+
const h = buf[begin++]!;
|
|
74
|
+
if ((g & 0x80) > 0 || (h & 0x80) > 0) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (length < 10) {
|
|
78
|
+
if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);
|
|
79
|
+
const i = buf[begin++]!;
|
|
80
|
+
if ((i & 0x80) > 0) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i);
|
|
84
|
+
}
|
|
85
|
+
const i = buf[begin++]!;
|
|
86
|
+
const j = buf[begin++]!;
|
|
87
|
+
if ((i & 0x80) > 0 || (j & 0x80) > 0) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (length < 12) {
|
|
91
|
+
if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);
|
|
92
|
+
const k = buf[begin++]!;
|
|
93
|
+
if ((k & 0x80) > 0) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
|
|
97
|
+
}
|
|
98
|
+
const k = buf[begin++]!;
|
|
99
|
+
const l = buf[begin++]!;
|
|
100
|
+
if ((k & 0x80) > 0 || (l & 0x80) > 0) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (length < 14) {
|
|
104
|
+
if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);
|
|
105
|
+
const m = buf[begin++]!;
|
|
106
|
+
if ((m & 0x80) > 0) {
|
|
107
|
+
begin -= 13;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
|
|
111
|
+
}
|
|
112
|
+
const m = buf[begin++]!;
|
|
113
|
+
const n = buf[begin++]!;
|
|
114
|
+
if ((m & 0x80) > 0 || (n & 0x80) > 0) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
|
|
118
|
+
const o = buf[begin++]!;
|
|
119
|
+
if ((o & 0x80) > 0) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** 解码字符串 */
|
|
126
|
+
function decodeStr(buffer: Uint8Array, offset: number, length: number): string {
|
|
127
|
+
// Fast path for short ascii strings
|
|
128
|
+
if (length < 16) {
|
|
129
|
+
const shortStr = shortStringInJS(buffer, offset, length);
|
|
130
|
+
if (shortStr !== undefined) {
|
|
131
|
+
return shortStr;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return DECODER.decode(buffer.subarray(offset, offset + length));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** 读取常量表 */
|
|
138
|
+
export function readConsts(constChunk: Uint8Array): VmPrimitive[] {
|
|
139
|
+
const reader = new DataView(constChunk.buffer, constChunk.byteOffset, constChunk.byteLength);
|
|
140
|
+
const consts: VmPrimitive[] = [];
|
|
141
|
+
let offset = 0;
|
|
142
|
+
const length = reader.byteLength;
|
|
143
|
+
while (offset < length) {
|
|
144
|
+
const type = reader.getUint8(offset);
|
|
145
|
+
switch (type) {
|
|
146
|
+
/* c8 ignore next 2 */
|
|
147
|
+
case 0:
|
|
148
|
+
consts.push(null);
|
|
149
|
+
offset += 1;
|
|
150
|
+
break;
|
|
151
|
+
case 1:
|
|
152
|
+
consts.push(true);
|
|
153
|
+
offset += 1;
|
|
154
|
+
break;
|
|
155
|
+
case 2:
|
|
156
|
+
consts.push(false);
|
|
157
|
+
offset += 1;
|
|
158
|
+
break;
|
|
159
|
+
case 3: {
|
|
160
|
+
const ordinal = reader.getInt32(offset + 1, true);
|
|
161
|
+
consts.push(ordinal);
|
|
162
|
+
offset += 5;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case 4: {
|
|
166
|
+
const num = reader.getFloat64(offset + 1, true);
|
|
167
|
+
consts.push(num);
|
|
168
|
+
offset += 9;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case 5: {
|
|
172
|
+
const len = reader.getUint32(offset + 1, true);
|
|
173
|
+
const str = decodeStr(constChunk, offset + 5, len);
|
|
174
|
+
consts.push(str);
|
|
175
|
+
offset += 5 + len;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
/* c8 ignore next 2 */
|
|
179
|
+
default:
|
|
180
|
+
throw new Error(`Unknown constant type: ${type}`);
|
|
181
|
+
}
|
|
46
182
|
}
|
|
183
|
+
return consts;
|
|
47
184
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { OpCode } from '@mirascript/bindings';
|
|
2
|
+
import { toString } from '../../helpers/convert/to-string.js';
|
|
2
3
|
import type { VmPrimitive } from '../../vm/index.js';
|
|
3
4
|
import type { ScriptInput, TranspileOptions } from '../types.js';
|
|
4
5
|
import type { IRange } from '../diagnostic.js';
|
|
5
|
-
import {
|
|
6
|
-
import { readConst, toJsLiteral } from './consts.js';
|
|
6
|
+
import { readConsts, toJsLiteral } from './consts.js';
|
|
7
7
|
import { createSourceMap } from './sourcemap.js';
|
|
8
|
+
import { SCRIPT_PREFIX } from './constants.js';
|
|
8
9
|
|
|
9
10
|
/** 生成代码 */
|
|
10
11
|
export function emit(
|
|
@@ -29,8 +30,6 @@ function createArray<T>(length: number, fn: (index: number) => T): T[] {
|
|
|
29
30
|
return result;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
const SCRIPT_PREFIX = `'use strict'; return ((global = GlobalFallback()) => { try { CpEnter();`;
|
|
33
|
-
const GLOBAL_HINT = `/* globals */`;
|
|
34
33
|
/** 代码生成 */
|
|
35
34
|
export class Emitter {
|
|
36
35
|
constructor(
|
|
@@ -47,22 +46,21 @@ export class Emitter {
|
|
|
47
46
|
this.constSize = reader.getUint32(8 + this.codeSize, true);
|
|
48
47
|
|
|
49
48
|
this.codeReader = new DataView(chunk.buffer, chunk.byteOffset + 8, this.codeSize);
|
|
50
|
-
this.
|
|
49
|
+
this.constVals = readConsts(
|
|
50
|
+
new Uint8Array(chunk.buffer, chunk.byteOffset + 12 + this.codeSize, this.constSize),
|
|
51
|
+
);
|
|
51
52
|
}
|
|
52
|
-
readonly pretty;
|
|
53
|
+
readonly pretty: boolean;
|
|
53
54
|
readonly chunkSize: number;
|
|
54
55
|
readonly codeSize: number;
|
|
55
|
-
private readonly constReader: DataView;
|
|
56
56
|
/** 读取常量表 */
|
|
57
57
|
private readConsts(): void {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.constLits.push(toJsLiteral(constant));
|
|
62
|
-
i += size;
|
|
58
|
+
const { constVals, constLits } = this;
|
|
59
|
+
for (let i = 0, len = constVals.length; i < len; i++) {
|
|
60
|
+
constLits.push(toJsLiteral(constVals[i]));
|
|
63
61
|
}
|
|
64
62
|
}
|
|
65
|
-
readonly constVals: VmPrimitive[]
|
|
63
|
+
readonly constVals: VmPrimitive[];
|
|
66
64
|
readonly constLits: string[] = [];
|
|
67
65
|
|
|
68
66
|
readonly constSize: number;
|
|
@@ -70,6 +68,8 @@ export class Emitter {
|
|
|
70
68
|
private codeOffset = 0;
|
|
71
69
|
private closureCounter = 0;
|
|
72
70
|
private identCounter = 0;
|
|
71
|
+
/** 记录函数声明所在位置 */
|
|
72
|
+
private readonly functions: number[] = [];
|
|
73
73
|
|
|
74
74
|
/** 制造缩进 */
|
|
75
75
|
private ident(len = 0): string {
|
|
@@ -77,10 +77,16 @@ export class Emitter {
|
|
|
77
77
|
return ' '.repeat(this.identCounter + len);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
readonly globals: GlobalMap = new Map();
|
|
81
80
|
/** 读取全局变量 */
|
|
82
|
-
private rg(constIdx: number
|
|
83
|
-
|
|
81
|
+
private rg(constIdx: number): string {
|
|
82
|
+
const constName = this.constVals[constIdx]!;
|
|
83
|
+
let lit;
|
|
84
|
+
if (typeof constName == 'string') {
|
|
85
|
+
lit = this.constLits[constIdx]!;
|
|
86
|
+
} else {
|
|
87
|
+
lit = toJsLiteral(toString(constName, undefined));
|
|
88
|
+
}
|
|
89
|
+
return `global.get(${lit})`;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
readonly codeLines: string[] = [];
|
|
@@ -327,14 +333,21 @@ export class Emitter {
|
|
|
327
333
|
}
|
|
328
334
|
return `${wv} = null`;
|
|
329
335
|
});
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
336
|
+
let regs = '_';
|
|
337
|
+
for (let i = 1 + argn; i < regn + 1; i++) {
|
|
338
|
+
regs += ',' + this.wv(i, -1);
|
|
339
|
+
}
|
|
333
340
|
const script = startFunc && !varg && argn === 0;
|
|
334
341
|
if (script) {
|
|
335
342
|
code = `${SCRIPT_PREFIX} var ${regs};`;
|
|
336
343
|
} else {
|
|
337
|
-
|
|
344
|
+
if (!this.options.sourceMap) {
|
|
345
|
+
code = `${this.wv(reg)} = Function(null ,(${args.join(', ')}) => { try { CpEnter(); var ${regs};`;
|
|
346
|
+
} else {
|
|
347
|
+
const index = this.functions.length;
|
|
348
|
+
this.functions.push(this.codeLines.length);
|
|
349
|
+
code = `${this.wv(reg)} = Function(fnName_${index} ,(${args.join(', ')}) => { try { CpEnter(); var ${regs};`;
|
|
350
|
+
}
|
|
338
351
|
}
|
|
339
352
|
if (varg) {
|
|
340
353
|
code += ` var ${this.wv(argn, -1)} = Vargs(vargs);`;
|
|
@@ -415,7 +428,7 @@ export class Emitter {
|
|
|
415
428
|
const args = createArray(n, () => read());
|
|
416
429
|
const ns = read();
|
|
417
430
|
const spreads = createArray(ns, () => read());
|
|
418
|
-
const callTarget = opcode === OpCode.Call ? this.rg(func
|
|
431
|
+
const callTarget = opcode === OpCode.Call ? this.rg(func) : this.rv(func);
|
|
419
432
|
code = `${this.wv(reg)} = $Call(${callTarget}, [${args
|
|
420
433
|
.map((a, i) => {
|
|
421
434
|
if (spreads.includes(i)) return `...$ArraySpread(${this.rv(a)})`;
|
|
@@ -520,7 +533,7 @@ export class Emitter {
|
|
|
520
533
|
case OpCode.GetGlobal: {
|
|
521
534
|
reg = read();
|
|
522
535
|
const i = read();
|
|
523
|
-
code = `${this.wv(reg)} = ${this.rg(i
|
|
536
|
+
code = `${this.wv(reg)} = ${this.rg(i)};`;
|
|
524
537
|
break;
|
|
525
538
|
}
|
|
526
539
|
case OpCode.GetGlobalDyn: {
|
|
@@ -705,19 +718,14 @@ export class Emitter {
|
|
|
705
718
|
read(): void {
|
|
706
719
|
this.readConsts();
|
|
707
720
|
this.readCode();
|
|
708
|
-
if (this.globals.size > 0) {
|
|
709
|
-
let globalsInit = '';
|
|
710
|
-
for (const { v } of this.globals.values()) {
|
|
711
|
-
globalsInit += globalsInit ? `, ${v}` : `var ${GLOBAL_HINT} ${v}`;
|
|
712
|
-
}
|
|
713
|
-
this.codeLines[0] += globalsInit + ';';
|
|
714
|
-
}
|
|
715
721
|
this.addSourceMap();
|
|
716
722
|
}
|
|
717
723
|
/** 添加源映射 */
|
|
718
724
|
addSourceMap(): void {
|
|
719
|
-
if (
|
|
720
|
-
|
|
721
|
-
|
|
725
|
+
if (this.options.sourceMap) {
|
|
726
|
+
createSourceMap(this.source, this.sourcemaps, this.codeLines, this.functions, this.options);
|
|
727
|
+
} else {
|
|
728
|
+
this.codeLines.unshift(`'use strict';`);
|
|
729
|
+
}
|
|
722
730
|
}
|
|
723
731
|
}
|