@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.
Files changed (82) hide show
  1. package/dist/{chunk-5AEWM4W6.js → chunk-4T5YE7LC.js} +241 -177
  2. package/dist/chunk-4T5YE7LC.js.map +6 -0
  3. package/dist/chunk-RNLB52WP.js +1 -0
  4. package/dist/chunk-RY7PYMXX.js +204 -0
  5. package/dist/chunk-RY7PYMXX.js.map +6 -0
  6. package/dist/{chunk-BFSB6SQK.js → chunk-XNGYORPT.js} +94 -192
  7. package/dist/chunk-XNGYORPT.js.map +6 -0
  8. package/dist/cli/index.js +6 -5
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/compiler/compile-fast.d.ts.map +1 -1
  11. package/dist/compiler/create-script.d.ts +4 -4
  12. package/dist/compiler/create-script.d.ts.map +1 -1
  13. package/dist/compiler/emit/constants.d.ts +1 -2
  14. package/dist/compiler/emit/constants.d.ts.map +1 -1
  15. package/dist/compiler/emit/consts.d.ts +2 -2
  16. package/dist/compiler/emit/consts.d.ts.map +1 -1
  17. package/dist/compiler/emit/index.d.ts +2 -3
  18. package/dist/compiler/emit/index.d.ts.map +1 -1
  19. package/dist/compiler/emit/sourcemap.d.ts +1 -2
  20. package/dist/compiler/emit/sourcemap.d.ts.map +1 -1
  21. package/dist/compiler/emit-script.d.ts +10 -0
  22. package/dist/compiler/emit-script.d.ts.map +1 -0
  23. package/dist/compiler/generate-bytecode.d.ts +4 -2
  24. package/dist/compiler/generate-bytecode.d.ts.map +1 -1
  25. package/dist/compiler/index.d.ts +1 -6
  26. package/dist/compiler/index.d.ts.map +1 -1
  27. package/dist/compiler/keywords.d.ts +4 -0
  28. package/dist/compiler/keywords.d.ts.map +1 -0
  29. package/dist/compiler/load-module.d.ts +2 -0
  30. package/dist/compiler/load-module.d.ts.map +1 -0
  31. package/dist/compiler/worker.js +1 -1
  32. package/dist/helpers/constants.d.ts +2 -0
  33. package/dist/helpers/constants.d.ts.map +1 -1
  34. package/dist/helpers/serialize.d.ts.map +1 -1
  35. package/dist/helpers/utils.d.ts.map +1 -1
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +6 -11
  39. package/dist/subtle.d.ts +5 -3
  40. package/dist/subtle.d.ts.map +1 -1
  41. package/dist/subtle.js +6 -5
  42. package/dist/vm/checkpoint.d.ts +1 -1
  43. package/dist/vm/checkpoint.d.ts.map +1 -1
  44. package/dist/vm/helpers.d.ts +6 -1
  45. package/dist/vm/helpers.d.ts.map +1 -1
  46. package/dist/vm/lib/global/json.d.ts.map +1 -1
  47. package/dist/vm/lib/helpers.d.ts +1 -1
  48. package/dist/vm/lib/helpers.d.ts.map +1 -1
  49. package/dist/vm/types/context.d.ts +7 -16
  50. package/dist/vm/types/context.d.ts.map +1 -1
  51. package/package.json +4 -4
  52. package/src/compiler/compile-fast.ts +79 -14
  53. package/src/compiler/create-script.ts +8 -8
  54. package/src/compiler/emit/constants.ts +1 -2
  55. package/src/compiler/emit/consts.ts +166 -29
  56. package/src/compiler/emit/index.ts +40 -32
  57. package/src/compiler/emit/sourcemap.ts +21 -76
  58. package/src/compiler/emit-script.ts +30 -0
  59. package/src/compiler/generate-bytecode.ts +5 -8
  60. package/src/compiler/index.ts +16 -35
  61. package/src/compiler/keywords.ts +9 -0
  62. package/src/compiler/load-module.ts +3 -0
  63. package/src/helpers/constants.ts +2 -0
  64. package/src/helpers/serialize.ts +1 -4
  65. package/src/helpers/utils.ts +2 -7
  66. package/src/index.ts +2 -0
  67. package/src/subtle.ts +5 -9
  68. package/src/vm/checkpoint.ts +42 -17
  69. package/src/vm/helpers.ts +12 -5
  70. package/src/vm/lib/global/json.ts +2 -0
  71. package/src/vm/lib/helpers.ts +2 -1
  72. package/src/vm/types/context.ts +42 -29
  73. package/src/vm/types/wrapper.ts +3 -3
  74. package/dist/chunk-5AEWM4W6.js.map +0 -6
  75. package/dist/chunk-BFSB6SQK.js.map +0 -6
  76. package/dist/chunk-RIT53WVY.js +0 -1
  77. package/dist/chunk-RLWIIOH5.js +0 -12
  78. package/dist/chunk-RLWIIOH5.js.map +0 -6
  79. package/dist/compiler/emit/globals.d.ts +0 -17
  80. package/dist/compiler/emit/globals.d.ts.map +0 -1
  81. package/src/compiler/emit/globals.ts +0 -39
  82. /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 = /^\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
7
- // 只识别特殊变量名,其他标识符可能有与关键字冲突等情况,需要编译器处理
8
- const REG_IDENTIFIER_FAST = /^(?:\$+|@+)[a-zA-Z0-9_]*$/;
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, () => null);
88
+ return wrapScript(code, mode, nil());
29
89
  }
30
90
  switch (trimmedCode) {
31
91
  case 'nil':
32
- return wrapScript(code, () => null);
92
+ return wrapScript(code, mode, nil());
33
93
  case 'true':
34
- return wrapScript(code, () => true);
94
+ return wrapScript(code, mode, constantBoolean(true));
35
95
  case 'false':
36
- return wrapScript(code, () => false);
96
+ return wrapScript(code, mode, constantBoolean(false));
37
97
  case 'nan':
38
- return wrapScript(code, () => 0 / 0);
98
+ return wrapScript(code, mode, nan());
39
99
  case 'inf':
40
100
  case '+inf':
41
- return wrapScript(code, () => 1 / 0);
101
+ return wrapScript(code, mode, posInf());
42
102
  case '-inf':
43
- return wrapScript(code, () => -1 / 0);
103
+ return wrapScript(code, mode, negInf());
44
104
  }
45
105
  if (REG_IDENTIFIER_FAST.test(trimmedCode)) {
46
106
  // 直接返回标识符
47
107
  const id = trimmedCode;
48
- return wrapScript(code, (global = GlobalFallback()) => global.get(id));
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, () => num);
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, () => 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]: true;
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: true });
24
+ defineProperty(script, kVmScript, { value: mode });
25
25
  if (typeof source === 'string') {
26
- defineProperty(script, 'source', { value: source, configurable: true });
26
+ defineProperty(script, 'source', { value: source });
27
27
  } else if (source instanceof Uint8Array) {
28
- defineProperty(script, 'source', { value: '<buffer>', configurable: true });
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 = `'use strict'; return ((global = GlobalFallback()) => { try { CpEnter();`;
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 JSON.stringify(value);
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
- export function readConst(reader: DataView, offset: number): [value: VmPrimitive, consumed: number] {
21
- const type = reader.getUint8(offset);
22
- switch (type) {
23
- /* c8 ignore next 2 */
24
- case 0:
25
- return [null, 1];
26
- case 1:
27
- return [true, 1];
28
- case 2:
29
- return [false, 1];
30
- case 3: {
31
- const ordinal = reader.getInt32(offset + 1, true);
32
- return [ordinal, 5];
33
- }
34
- case 4: {
35
- const num = reader.getFloat64(offset + 1, true);
36
- return [num, 9];
37
- }
38
- case 5: {
39
- const len = reader.getUint32(offset + 1, true);
40
- const str = new TextDecoder().decode(new Uint8Array(reader.buffer, reader.byteOffset + offset + 5, len));
41
- return [str, 5 + len];
42
- }
43
- /* c8 ignore next 2 */
44
- default:
45
- throw new Error(`Unknown constant type: ${type}`);
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 { readGlobal, type GlobalMap } from './globals.js';
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.constReader = new DataView(chunk.buffer, chunk.byteOffset + 12 + this.codeSize, this.constSize);
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
- for (let i = 0, index = 0; i < this.constSize; index++) {
59
- const [constant, size] = readConst(this.constReader, i);
60
- this.constVals.push(constant);
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, eager: boolean): string {
83
- return readGlobal(this, constIdx).e;
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
- const regs = createArray(regn - argn + 1, (i) => (i ? this.wv(i + argn, -1) : this.wv(0, -1))).join(
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
- code = `${this.wv(reg)} = Function((${args.join(', ')}) => { try { CpEnter(); var ${regs};`;
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, false) : this.rv(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, false)};`;
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 (!this.options.sourceMap) return;
720
- const mapLines = createSourceMap(this.source, this.sourcemaps, this.codeLines, this.globals, this.options);
721
- this.codeLines.push(...mapLines);
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
  }