@mirascript/mirascript 0.1.18 → 0.1.20

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 (86) 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-RLWIIOH5.js → chunk-BV5YXQYA.js} +1 -1
  4. package/dist/{chunk-RLWIIOH5.js.map → chunk-BV5YXQYA.js.map} +1 -1
  5. package/dist/chunk-OVTSNACH.js +152 -0
  6. package/dist/chunk-OVTSNACH.js.map +6 -0
  7. package/dist/{chunk-3BAOZ2P2.js → chunk-ZKL4MRMP.js} +185 -212
  8. package/dist/chunk-ZKL4MRMP.js.map +6 -0
  9. package/dist/cli/index.js +6 -5
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/compiler/emit/constants.d.ts +1 -2
  12. package/dist/compiler/emit/constants.d.ts.map +1 -1
  13. package/dist/compiler/emit/consts.d.ts +2 -2
  14. package/dist/compiler/emit/consts.d.ts.map +1 -1
  15. package/dist/compiler/emit/index.d.ts +2 -3
  16. package/dist/compiler/emit/index.d.ts.map +1 -1
  17. package/dist/compiler/emit/sourcemap.d.ts +1 -2
  18. package/dist/compiler/emit/sourcemap.d.ts.map +1 -1
  19. package/dist/compiler/emit-script.d.ts +10 -0
  20. package/dist/compiler/emit-script.d.ts.map +1 -0
  21. package/dist/compiler/generate-bytecode.d.ts +4 -2
  22. package/dist/compiler/generate-bytecode.d.ts.map +1 -1
  23. package/dist/compiler/index.d.ts +1 -6
  24. package/dist/compiler/index.d.ts.map +1 -1
  25. package/dist/compiler/load-module.d.ts +2 -0
  26. package/dist/compiler/load-module.d.ts.map +1 -0
  27. package/dist/compiler/worker.js +1 -1
  28. package/dist/helpers/constants.d.ts +2 -0
  29. package/dist/helpers/constants.d.ts.map +1 -1
  30. package/dist/helpers/serialize.d.ts.map +1 -1
  31. package/dist/helpers/utils.d.ts.map +1 -1
  32. package/dist/index.js +6 -11
  33. package/dist/subtle.d.ts +3 -1
  34. package/dist/subtle.d.ts.map +1 -1
  35. package/dist/subtle.js +3 -3
  36. package/dist/vm/checkpoint.d.ts +1 -1
  37. package/dist/vm/checkpoint.d.ts.map +1 -1
  38. package/dist/vm/helpers.d.ts +6 -1
  39. package/dist/vm/helpers.d.ts.map +1 -1
  40. package/dist/vm/lib/global/json.d.ts.map +1 -1
  41. package/dist/vm/lib/global/math-const.d.ts +24 -2
  42. package/dist/vm/lib/global/math-const.d.ts.map +1 -1
  43. package/dist/vm/lib/helpers.d.ts +5 -3
  44. package/dist/vm/lib/helpers.d.ts.map +1 -1
  45. package/dist/vm/lib/index.d.ts.map +1 -1
  46. package/dist/vm/lib/loader.d.ts +12 -5
  47. package/dist/vm/lib/loader.d.ts.map +1 -1
  48. package/dist/vm/types/context.d.ts +10 -18
  49. package/dist/vm/types/context.d.ts.map +1 -1
  50. package/dist/vm/types/extern.d.ts +1 -1
  51. package/dist/vm/types/extern.d.ts.map +1 -1
  52. package/dist/vm/types/module.d.ts +1 -1
  53. package/dist/vm/types/module.d.ts.map +1 -1
  54. package/dist/vm/types/wrapper.d.ts +4 -2
  55. package/dist/vm/types/wrapper.d.ts.map +1 -1
  56. package/package.json +4 -4
  57. package/src/compiler/emit/constants.ts +1 -2
  58. package/src/compiler/emit/consts.ts +166 -29
  59. package/src/compiler/emit/index.ts +40 -32
  60. package/src/compiler/emit/sourcemap.ts +21 -76
  61. package/src/compiler/emit-script.ts +30 -0
  62. package/src/compiler/generate-bytecode.ts +5 -8
  63. package/src/compiler/index.ts +2 -30
  64. package/src/compiler/load-module.ts +3 -0
  65. package/src/helpers/constants.ts +2 -0
  66. package/src/helpers/serialize.ts +1 -4
  67. package/src/helpers/utils.ts +2 -7
  68. package/src/subtle.ts +3 -1
  69. package/src/vm/checkpoint.ts +42 -17
  70. package/src/vm/helpers.ts +12 -5
  71. package/src/vm/lib/global/json.ts +2 -0
  72. package/src/vm/lib/global/math-const.ts +34 -2
  73. package/src/vm/lib/helpers.ts +10 -11
  74. package/src/vm/lib/index.ts +7 -3
  75. package/src/vm/lib/loader.ts +56 -19
  76. package/src/vm/types/context.ts +58 -30
  77. package/src/vm/types/extern.ts +1 -1
  78. package/src/vm/types/module.ts +1 -1
  79. package/src/vm/types/wrapper.ts +9 -5
  80. package/dist/chunk-3BAOZ2P2.js.map +0 -6
  81. package/dist/chunk-5AEWM4W6.js.map +0 -6
  82. package/dist/chunk-RIT53WVY.js +0 -1
  83. package/dist/chunk-RIT53WVY.js.map +0 -6
  84. package/dist/compiler/emit/globals.d.ts +0 -17
  85. package/dist/compiler/emit/globals.d.ts.map +0 -1
  86. package/src/compiler/emit/globals.ts +0 -39
@@ -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 { GLOBAL_HINT, SCRIPT_PREFIX } from './constants.js';
5
- import type { GlobalMap } from './globals.js';
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
- const SOURCE_OFFSET = 3;
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: readonly string[],
106
- globals: GlobalMap,
42
+ codeLines: string[],
43
+ functions: readonly number[],
107
44
  options: TranspileOptions,
108
- ): [string, string] {
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
- const globalLine = codeLines[0];
158
- if (globalLine?.includes(GLOBAL_HINT)) {
159
- addGlobalMappings(globalLine, `${fileName} <globals>`, map, globals);
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
- return [
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, 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
  }
@@ -1,45 +1,17 @@
1
- import { loadModule } from '@mirascript/bindings';
2
1
  import type { ScriptInput, TranspileOptions } from './types.js';
3
- import { emit } from './emit/index.js';
4
2
  import { createScript, type VmScript } from './create-script.js';
5
3
  import { compileFast } from './compile-fast.js';
6
- import { DiagnosticCode, formatDiagnostics, parseDiagnostics } from './diagnostic.js';
7
4
  import { generateBytecode, generateBytecodeSync } from './generate-bytecode.js';
8
5
  import { compileWorker } from './worker-manager.js';
9
- await loadModule();
6
+ import { emitScript, reportDiagnostic } from './emit-script.js';
7
+ import './load-module.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 reportDiagnostic(source: ScriptInput, diagnostics: Uint32Array, fileName: string | undefined): never {
20
- const parsed = parseDiagnostics(source, diagnostics);
21
- const messages = formatDiagnostics(parsed.errors, source, fileName);
22
- throw new Error(`Failed to compile:\n${messages.join('\n')}`);
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);
35
- }
36
- const sourcemaps = options.sourceMap
37
- ? parseDiagnostics(source, diagnostics, (c) => c === DiagnosticCode.SourceMap).sourcemaps
38
- : [];
39
- const target = emit(source, code, sourcemaps, options);
40
- return createScript(source, target);
41
- }
42
-
43
15
  /**
44
16
  * 生成 MiraScript 对应的 JavaScript 代码
45
17
  */
@@ -0,0 +1,3 @@
1
+ import { loadModule } from '@mirascript/bindings';
2
+
3
+ await loadModule();
@@ -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]/;
@@ -1,5 +1,5 @@
1
1
  import type { VmArray, VmExtern, VmFunction, VmModule, VmAny, VmRecord } from '../vm/index.js';
2
- import { REG_IDENTIFIER, REG_ORDINAL } from './constants.js';
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 */
@@ -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/subtle.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { getModule } from '@mirascript/bindings';
2
+ import './compiler/load-module.js';
2
3
 
3
4
  export * as constants from './helpers/constants.js';
4
5
  export * as convert from './helpers/convert/index.js';
@@ -18,7 +19,8 @@ export {
18
19
  } from './helpers/serialize.js';
19
20
  export { lib } from './vm/lib/index.js';
20
21
  export * from './compiler/diagnostic.js';
21
- export { generateBytecode, generateBytecodeSync, emitScript } from './compiler/index.js';
22
+ export { emitScript } from './compiler/emit-script.js';
23
+ export { generateBytecode, generateBytecodeSync, type VmBytecodeResult } from './compiler/generate-bytecode.js';
22
24
 
23
25
  /** 所有 MiraScript 关键字 */
24
26
  export let keywords: () => readonly string[] = () => {
@@ -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 * 24; // 减去一天,防止系统时间被调整到过去时出问题
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
- cp = timestamp();
19
- } else if (timestamp() - cp > cpTimeout) {
20
- throw new RangeError('Execution timeout');
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(timeout = CP_DEFAULT_TIMEOUT): void {
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 = Math.ceil(timeout);
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
- export const Function = (fn: VmFunctionLike, name: string = VM_FUNCTION_ANONYMOUS_NAME): VmFunction => {
34
- defineProperty(fn, 'name', { value: name });
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
  );
@@ -1,2 +1,34 @@
1
- const { PI, E } = Math;
2
- export { PI as '@pi', E as '@e' };
1
+ import { VmLib } from '../helpers.js';
2
+
3
+ export const PI = VmLib(Math.PI, {
4
+ summary: '圆周率',
5
+ returnsType: 'number',
6
+ });
7
+ export const E = VmLib(Math.E, {
8
+ summary: '自然对数的底数',
9
+ returnsType: 'number',
10
+ });
11
+ export const SQRT1_2 = VmLib(Math.SQRT1_2, {
12
+ summary: '1 的平方根除以 2',
13
+ returnsType: 'number',
14
+ });
15
+ export const SQRT2 = VmLib(Math.SQRT2, {
16
+ summary: '2 的平方根',
17
+ returnsType: 'number',
18
+ });
19
+ export const LN2 = VmLib(Math.LN2, {
20
+ summary: '2 的自然对数',
21
+ returnsType: 'number',
22
+ });
23
+ export const LN10 = VmLib(Math.LN10, {
24
+ summary: '10 的自然对数',
25
+ returnsType: 'number',
26
+ });
27
+ export const LOG2E = VmLib(Math.LOG2E, {
28
+ summary: 'e 以 2 为底的对数',
29
+ returnsType: 'number',
30
+ });
31
+ export const LOG10E = VmLib(Math.LOG10E, {
32
+ summary: 'e 以 10 为底的对数',
33
+ returnsType: 'number',
34
+ });
@@ -274,22 +274,20 @@ 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
- export type VmLib<T extends VmFunctionLike = VmFunctionLike> = T & VmLibOption;
280
+ export type VmLib<T extends VmFunctionLike | VmConst = VmFunctionLike> = (T extends VmFunctionLike ? T : { value: T }) &
281
+ VmLibOption;
281
282
 
282
283
  /** 创建库函数 */
283
- export function VmLib<const T extends VmFunctionLike, P extends Record<string, unknown> = Record<never, never>>(
284
- fn: T,
285
- option: VmLibOption,
286
- properties?: P,
287
- ): VmLib<T> & P {
288
- /* c8 ignore next 2 */
289
- if (typeof fn != 'function') throw new TypeError('Invalid function');
290
- if (isVmFunction(fn)) throw new TypeError('Cannot create VmLib from a VmFunction');
284
+ export function VmLib<
285
+ const T extends VmFunctionLike | VmConst,
286
+ P extends Record<string, unknown> = Record<never, never>,
287
+ >(value: T, option: VmLibOption, properties?: P): VmLib<T> & P {
288
+ if (isVmFunction(value)) throw new TypeError('Cannot create VmLib from a VmFunction');
291
289
 
292
- const ret = fn as Writable<VmLib<T>> & P;
290
+ const ret = (typeof value == 'function' ? value : { value: value }) as unknown as Writable<VmLib<T>> & P;
293
291
  Object.assign(ret, properties);
294
292
  ret.params = option.params;
295
293
  ret.paramsType = option.paramsType;
@@ -297,5 +295,6 @@ export function VmLib<const T extends VmFunctionLike, P extends Record<string, u
297
295
  ret.returnsType = option.returnsType;
298
296
  ret.summary = option.summary;
299
297
  ret.examples = option.examples;
298
+ ret.injectCp = option.injectCp ?? false;
300
299
  return ret as VmLib<T> & P;
301
300
  }
@@ -1,16 +1,20 @@
1
1
  import * as global from './global/index.js';
2
2
  import * as mods from './mod/index.js';
3
- import { VM_SHARED_CONTEXT } from '../types/context.js';
3
+ import { VM_SHARED_CONTEXT, VM_SHARED_CONTEXT_DESCRIPTIONS } from '../types/context.js';
4
4
  import { create, entries } from '../../helpers/utils.js';
5
5
  import { createModule, wrapEntry, type RawValue } from './loader.js';
6
6
 
7
7
  for (const [name, value] of entries(global)) {
8
- VM_SHARED_CONTEXT[name] = wrapEntry(name, value as RawValue, 'global');
8
+ const [wrappedValue, description] = wrapEntry(name, value as RawValue, 'global');
9
+ VM_SHARED_CONTEXT[name] = wrappedValue;
10
+ VM_SHARED_CONTEXT_DESCRIPTIONS[name] = description;
9
11
  }
10
12
 
11
13
  for (const [name, value] of entries(mods)) {
12
14
  const mod = createModule(name, value as Record<string, RawValue>);
13
- VM_SHARED_CONTEXT[name] = wrapEntry(name, mod, 'global');
15
+ const [wrappedValue, description] = wrapEntry(name, mod, 'global');
16
+ VM_SHARED_CONTEXT[name] = wrappedValue;
17
+ VM_SHARED_CONTEXT_DESCRIPTIONS[name] = description;
14
18
  }
15
19
 
16
20
  export const lib: Readonly<typeof global & typeof mods> = Object.freeze(Object.assign(create(null), global, mods));