@aster-cloud/aster-lang-ts 1.0.30 → 1.0.32
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/scripts/aster.js +11 -26
- package/dist/scripts/aster.js.map +1 -1
- package/dist/scripts/corpus-loader.d.ts +17 -0
- package/dist/scripts/corpus-loader.d.ts.map +1 -0
- package/dist/scripts/corpus-loader.js +60 -0
- package/dist/scripts/corpus-loader.js.map +1 -0
- package/dist/scripts/emit-classfiles-core.js +5 -13
- package/dist/scripts/emit-classfiles-core.js.map +1 -1
- package/dist/scripts/emit-classfiles.js +7 -12
- package/dist/scripts/emit-classfiles.js.map +1 -1
- package/dist/scripts/generate_error_codes.js +2 -0
- package/dist/scripts/generate_error_codes.js.map +1 -1
- package/dist/scripts/golden.js +4 -1
- package/dist/scripts/golden.js.map +1 -1
- package/dist/scripts/test-lossless-golden.js +2 -1
- package/dist/scripts/test-lossless-golden.js.map +1 -1
- package/dist/scripts/validate_error_codes.js +1 -0
- package/dist/scripts/validate_error_codes.js.map +1 -1
- package/dist/scripts/verify-lexicon-sync.js +5 -1
- package/dist/scripts/verify-lexicon-sync.js.map +1 -1
- package/dist/src/ast/ast.d.ts +1 -1
- package/dist/src/ast/ast.d.ts.map +1 -1
- package/dist/src/ast/ast.js +2 -1
- package/dist/src/ast/ast.js.map +1 -1
- package/dist/src/browser.d.ts +7 -0
- package/dist/src/browser.d.ts.map +1 -1
- package/dist/src/browser.js +4 -0
- package/dist/src/browser.js.map +1 -1
- package/dist/src/cli/utils/gradle-env.d.ts +9 -0
- package/dist/src/cli/utils/gradle-env.d.ts.map +1 -0
- package/dist/src/cli/utils/gradle-env.js +18 -0
- package/dist/src/cli/utils/gradle-env.js.map +1 -0
- package/dist/src/cli/utils/out-dir.d.ts +18 -0
- package/dist/src/cli/utils/out-dir.d.ts.map +1 -0
- package/dist/src/cli/utils/out-dir.js +49 -0
- package/dist/src/cli/utils/out-dir.js.map +1 -0
- package/dist/src/cnl-printer.d.ts +10 -0
- package/dist/src/cnl-printer.d.ts.map +1 -0
- package/dist/src/cnl-printer.js +360 -0
- package/dist/src/cnl-printer.js.map +1 -0
- package/dist/src/config/lexicons/de-DE.d.ts.map +1 -1
- package/dist/src/config/lexicons/de-DE.js +8 -2
- package/dist/src/config/lexicons/de-DE.js.map +1 -1
- package/dist/src/config/lexicons/en-US.d.ts.map +1 -1
- package/dist/src/config/lexicons/en-US.js +8 -4
- package/dist/src/config/lexicons/en-US.js.map +1 -1
- package/dist/src/config/lexicons/hi-IN.d.ts.map +1 -1
- package/dist/src/config/lexicons/hi-IN.js +8 -2
- package/dist/src/config/lexicons/hi-IN.js.map +1 -1
- package/dist/src/config/lexicons/template.d.ts.map +1 -1
- package/dist/src/config/lexicons/template.js +12 -0
- package/dist/src/config/lexicons/template.js.map +1 -1
- package/dist/src/config/lexicons/zh-CN.d.ts.map +1 -1
- package/dist/src/config/lexicons/zh-CN.js +9 -3
- package/dist/src/config/lexicons/zh-CN.js.map +1 -1
- package/dist/src/config/semantic.d.ts +6 -0
- package/dist/src/config/semantic.d.ts.map +1 -1
- package/dist/src/config/semantic.js +8 -0
- package/dist/src/config/semantic.js.map +1 -1
- package/dist/src/config/token-kind.d.ts +12 -0
- package/dist/src/config/token-kind.d.ts.map +1 -1
- package/dist/src/config/token-kind.js +18 -0
- package/dist/src/config/token-kind.js.map +1 -1
- package/dist/src/core/core_ir.d.ts +1 -1
- package/dist/src/core/core_ir.d.ts.map +1 -1
- package/dist/src/core/core_ir.js +3 -1
- package/dist/src/core/core_ir.js.map +1 -1
- package/dist/src/core/interpreter.d.ts.map +1 -1
- package/dist/src/core/interpreter.js +222 -18
- package/dist/src/core/interpreter.js.map +1 -1
- package/dist/src/cst/cst_printer.d.ts.map +1 -1
- package/dist/src/cst/cst_printer.js +66 -32
- package/dist/src/cst/cst_printer.js.map +1 -1
- package/dist/src/diagnostics/diagnostics.d.ts +1 -0
- package/dist/src/diagnostics/diagnostics.d.ts.map +1 -1
- package/dist/src/diagnostics/diagnostics.js +1 -0
- package/dist/src/diagnostics/diagnostics.js.map +1 -1
- package/dist/src/diagnostics/error_codes.d.ts +10 -2
- package/dist/src/diagnostics/error_codes.d.ts.map +1 -1
- package/dist/src/diagnostics/error_codes.js +64 -0
- package/dist/src/diagnostics/error_codes.js.map +1 -1
- package/dist/src/effects/effect_inference.js +1 -1
- package/dist/src/effects/effect_inference.js.map +1 -1
- package/dist/src/formatter.d.ts +3 -0
- package/dist/src/formatter.d.ts.map +1 -1
- package/dist/src/formatter.js +8 -261
- package/dist/src/formatter.js.map +1 -1
- package/dist/src/frontend/transformers.d.ts.map +1 -1
- package/dist/src/frontend/transformers.js +10 -2
- package/dist/src/frontend/transformers.js.map +1 -1
- package/dist/src/governance/controls-registry.data.d.ts +40 -0
- package/dist/src/governance/controls-registry.data.d.ts.map +1 -0
- package/dist/src/governance/controls-registry.data.js +35 -0
- package/dist/src/governance/controls-registry.data.js.map +1 -0
- package/dist/src/governance/controls.d.ts +23 -0
- package/dist/src/governance/controls.d.ts.map +1 -0
- package/dist/src/governance/controls.js +77 -0
- package/dist/src/governance/controls.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/lower_to_core.js +1 -1
- package/dist/src/lower_to_core.js.map +1 -1
- package/dist/src/lsp/completion.d.ts +22 -1
- package/dist/src/lsp/completion.d.ts.map +1 -1
- package/dist/src/lsp/completion.js +42 -1
- package/dist/src/lsp/completion.js.map +1 -1
- package/dist/src/lsp/debounce-map.d.ts +16 -0
- package/dist/src/lsp/debounce-map.d.ts.map +1 -0
- package/dist/src/lsp/debounce-map.js +32 -0
- package/dist/src/lsp/debounce-map.js.map +1 -0
- package/dist/src/lsp/server.js +6 -8
- package/dist/src/lsp/server.js.map +1 -1
- package/dist/src/lsp/utils.d.ts.map +1 -1
- package/dist/src/lsp/utils.js +2 -0
- package/dist/src/lsp/utils.js.map +1 -1
- package/dist/src/lsp/workspace/file-watcher.d.ts +1 -0
- package/dist/src/lsp/workspace/file-watcher.d.ts.map +1 -1
- package/dist/src/lsp/workspace/file-watcher.js +24 -14
- package/dist/src/lsp/workspace/file-watcher.js.map +1 -1
- package/dist/src/manifest.d.ts +2 -0
- package/dist/src/manifest.d.ts.map +1 -1
- package/dist/src/manifest.js.map +1 -1
- package/dist/src/package/manifest-parser.d.ts +10 -0
- package/dist/src/package/manifest-parser.d.ts.map +1 -1
- package/dist/src/package/manifest-parser.js +27 -28
- package/dist/src/package/manifest-parser.js.map +1 -1
- package/dist/src/package/manifest-schema.d.ts +15 -0
- package/dist/src/package/manifest-schema.d.ts.map +1 -0
- package/dist/src/package/manifest-schema.js +116 -0
- package/dist/src/package/manifest-schema.js.map +1 -0
- package/dist/src/parser/context.d.ts +2 -0
- package/dist/src/parser/context.d.ts.map +1 -1
- package/dist/src/parser/context.js +1 -0
- package/dist/src/parser/context.js.map +1 -1
- package/dist/src/parser/decl-parser.d.ts.map +1 -1
- package/dist/src/parser/decl-parser.js +26 -10
- package/dist/src/parser/decl-parser.js.map +1 -1
- package/dist/src/parser/expr-stmt-parser.d.ts +4 -1
- package/dist/src/parser/expr-stmt-parser.d.ts.map +1 -1
- package/dist/src/parser/expr-stmt-parser.js +199 -6
- package/dist/src/parser/expr-stmt-parser.js.map +1 -1
- package/dist/src/parser/import-parser.d.ts +12 -0
- package/dist/src/parser/import-parser.d.ts.map +1 -1
- package/dist/src/parser/import-parser.js +28 -0
- package/dist/src/parser/import-parser.js.map +1 -1
- package/dist/src/parser.js +1 -1
- package/dist/src/parser.js.map +1 -1
- package/dist/src/typecheck/browser.d.ts +3 -0
- package/dist/src/typecheck/browser.d.ts.map +1 -1
- package/dist/src/typecheck/browser.js +10 -1
- package/dist/src/typecheck/browser.js.map +1 -1
- package/dist/src/typecheck/context.d.ts +3 -0
- package/dist/src/typecheck/context.d.ts.map +1 -1
- package/dist/src/typecheck/context.js.map +1 -1
- package/dist/src/typecheck/expression.d.ts +6 -0
- package/dist/src/typecheck/expression.d.ts.map +1 -1
- package/dist/src/typecheck/expression.js +54 -2
- package/dist/src/typecheck/expression.js.map +1 -1
- package/dist/src/typecheck/generics.d.ts.map +1 -1
- package/dist/src/typecheck/generics.js +3 -0
- package/dist/src/typecheck/generics.js.map +1 -1
- package/dist/src/typecheck/governance.d.ts +43 -0
- package/dist/src/typecheck/governance.d.ts.map +1 -0
- package/dist/src/typecheck/governance.js +162 -0
- package/dist/src/typecheck/governance.js.map +1 -0
- package/dist/src/typecheck/module.d.ts.map +1 -1
- package/dist/src/typecheck/module.js +10 -1
- package/dist/src/typecheck/module.js.map +1 -1
- package/dist/src/typecheck/statement.d.ts.map +1 -1
- package/dist/src/typecheck/statement.js +2 -2
- package/dist/src/typecheck/statement.js.map +1 -1
- package/dist/src/typecheck/type_system.d.ts.map +1 -1
- package/dist/src/typecheck/type_system.js +4 -0
- package/dist/src/typecheck/type_system.js.map +1 -1
- package/dist/src/typecheck/verdict_signatures.d.ts +19 -0
- package/dist/src/typecheck/verdict_signatures.d.ts.map +1 -0
- package/dist/src/typecheck/verdict_signatures.js +23 -0
- package/dist/src/typecheck/verdict_signatures.js.map +1 -0
- package/dist/src/types/base.d.ts +2 -0
- package/dist/src/types/base.d.ts.map +1 -1
- package/dist/test/helpers/sibling-source.d.ts +10 -0
- package/dist/test/helpers/sibling-source.d.ts.map +1 -0
- package/dist/test/helpers/sibling-source.js +20 -0
- package/dist/test/helpers/sibling-source.js.map +1 -0
- package/dist/test/unit/allocation-budget.test.d.ts +2 -0
- package/dist/test/unit/allocation-budget.test.d.ts.map +1 -0
- package/dist/test/unit/allocation-budget.test.js +160 -0
- package/dist/test/unit/allocation-budget.test.js.map +1 -0
- package/dist/test/unit/apply-call.test.js +5 -0
- package/dist/test/unit/apply-call.test.js.map +1 -1
- package/dist/test/unit/canonicalizer/possessive-anchor.test.js +10 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.js.map +1 -1
- package/dist/test/unit/cli/gradle-env-guard.test.d.ts +2 -0
- package/dist/test/unit/cli/gradle-env-guard.test.d.ts.map +1 -0
- package/dist/test/unit/cli/gradle-env-guard.test.js +53 -0
- package/dist/test/unit/cli/gradle-env-guard.test.js.map +1 -0
- package/dist/test/unit/cli/gradle-env.test.d.ts +2 -0
- package/dist/test/unit/cli/gradle-env.test.d.ts.map +1 -0
- package/dist/test/unit/cli/gradle-env.test.js +31 -0
- package/dist/test/unit/cli/gradle-env.test.js.map +1 -0
- package/dist/test/unit/cli/out-dir.test.d.ts +2 -0
- package/dist/test/unit/cli/out-dir.test.d.ts.map +1 -0
- package/dist/test/unit/cli/out-dir.test.js +96 -0
- package/dist/test/unit/cli/out-dir.test.js.map +1 -0
- package/dist/test/unit/eval-regression-poker-straight-high.test.d.ts +2 -0
- package/dist/test/unit/eval-regression-poker-straight-high.test.d.ts.map +1 -0
- package/dist/test/unit/eval-regression-poker-straight-high.test.js +61 -0
- package/dist/test/unit/eval-regression-poker-straight-high.test.js.map +1 -0
- package/dist/test/unit/formatter-corpus-roundtrip.test.d.ts +2 -0
- package/dist/test/unit/formatter-corpus-roundtrip.test.d.ts.map +1 -0
- package/dist/test/unit/formatter-corpus-roundtrip.test.js +190 -0
- package/dist/test/unit/formatter-corpus-roundtrip.test.js.map +1 -0
- package/dist/test/unit/formatter-roundtrip.test.d.ts +2 -0
- package/dist/test/unit/formatter-roundtrip.test.d.ts.map +1 -0
- package/dist/test/unit/formatter-roundtrip.test.js +125 -0
- package/dist/test/unit/formatter-roundtrip.test.js.map +1 -0
- package/dist/test/unit/governance/controls-registry-parity.test.d.ts +2 -0
- package/dist/test/unit/governance/controls-registry-parity.test.d.ts.map +1 -0
- package/dist/test/unit/governance/controls-registry-parity.test.js +28 -0
- package/dist/test/unit/governance/controls-registry-parity.test.js.map +1 -0
- package/dist/test/unit/governance/controls-registry-profiles.test.d.ts +2 -0
- package/dist/test/unit/governance/controls-registry-profiles.test.d.ts.map +1 -0
- package/dist/test/unit/governance/controls-registry-profiles.test.js +106 -0
- package/dist/test/unit/governance/controls-registry-profiles.test.js.map +1 -0
- package/dist/test/unit/governance/governance-check.test.d.ts +2 -0
- package/dist/test/unit/governance/governance-check.test.d.ts.map +1 -0
- package/dist/test/unit/governance/governance-check.test.js +169 -0
- package/dist/test/unit/governance/governance-check.test.js.map +1 -0
- package/dist/test/unit/governance/profile-check.test.d.ts +2 -0
- package/dist/test/unit/governance/profile-check.test.d.ts.map +1 -0
- package/dist/test/unit/governance/profile-check.test.js +139 -0
- package/dist/test/unit/governance/profile-check.test.js.map +1 -0
- package/dist/test/unit/governance/verdict-typecheck.test.d.ts +2 -0
- package/dist/test/unit/governance/verdict-typecheck.test.d.ts.map +1 -0
- package/dist/test/unit/governance/verdict-typecheck.test.js +80 -0
- package/dist/test/unit/governance/verdict-typecheck.test.js.map +1 -0
- package/dist/test/unit/interpreter/verdict-builtins.test.d.ts +2 -0
- package/dist/test/unit/interpreter/verdict-builtins.test.d.ts.map +1 -0
- package/dist/test/unit/interpreter/verdict-builtins.test.js +72 -0
- package/dist/test/unit/interpreter/verdict-builtins.test.js.map +1 -0
- package/dist/test/unit/list-numeric-fail-loud.test.d.ts +2 -0
- package/dist/test/unit/list-numeric-fail-loud.test.d.ts.map +1 -0
- package/dist/test/unit/list-numeric-fail-loud.test.js +83 -0
- package/dist/test/unit/list-numeric-fail-loud.test.js.map +1 -0
- package/dist/test/unit/lossless-mirror.test.d.ts +2 -0
- package/dist/test/unit/lossless-mirror.test.d.ts.map +1 -0
- package/dist/test/unit/lossless-mirror.test.js +41 -0
- package/dist/test/unit/lossless-mirror.test.js.map +1 -0
- package/dist/test/unit/lossless-reflow.test.d.ts +2 -0
- package/dist/test/unit/lossless-reflow.test.d.ts.map +1 -0
- package/dist/test/unit/lossless-reflow.test.js +84 -0
- package/dist/test/unit/lossless-reflow.test.js.map +1 -0
- package/dist/test/unit/lowering/lowering.test.js +35 -35
- package/dist/test/unit/lowering/lowering.test.js.map +1 -1
- package/dist/test/unit/lsp/control-completion.test.d.ts +2 -0
- package/dist/test/unit/lsp/control-completion.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/control-completion.test.js +46 -0
- package/dist/test/unit/lsp/control-completion.test.js.map +1 -0
- package/dist/test/unit/lsp/debounce-map.test.d.ts +2 -0
- package/dist/test/unit/lsp/debounce-map.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/debounce-map.test.js +47 -0
- package/dist/test/unit/lsp/debounce-map.test.js.map +1 -0
- package/dist/test/unit/lsp/file-watcher-restart.test.d.ts +2 -0
- package/dist/test/unit/lsp/file-watcher-restart.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/file-watcher-restart.test.js +40 -0
- package/dist/test/unit/lsp/file-watcher-restart.test.js.map +1 -0
- package/dist/test/unit/manifest-parser.test.js +8 -0
- package/dist/test/unit/manifest-parser.test.js.map +1 -1
- package/dist/test/unit/manifest-schema.test.d.ts +7 -5
- package/dist/test/unit/manifest-schema.test.d.ts.map +1 -1
- package/dist/test/unit/manifest-schema.test.js +48 -10
- package/dist/test/unit/manifest-schema.test.js.map +1 -1
- package/dist/test/unit/mapping/redos-budget.test.js +60 -23
- package/dist/test/unit/mapping/redos-budget.test.js.map +1 -1
- package/dist/test/unit/parser/governance-sugar.test.d.ts +2 -0
- package/dist/test/unit/parser/governance-sugar.test.d.ts.map +1 -0
- package/dist/test/unit/parser/governance-sugar.test.js +258 -0
- package/dist/test/unit/parser/governance-sugar.test.js.map +1 -0
- package/dist/test/unit/parser/module-header.test.d.ts +2 -0
- package/dist/test/unit/parser/module-header.test.d.ts.map +1 -0
- package/dist/test/unit/parser/module-header.test.js +25 -0
- package/dist/test/unit/parser/module-header.test.js.map +1 -0
- package/dist/test/unit/parser/positional-annotation-args.test.d.ts +2 -0
- package/dist/test/unit/parser/positional-annotation-args.test.d.ts.map +1 -0
- package/dist/test/unit/parser/positional-annotation-args.test.js +26 -0
- package/dist/test/unit/parser/positional-annotation-args.test.js.map +1 -0
- package/dist/test/unit/parser/prefix-operator-call.test.d.ts +2 -0
- package/dist/test/unit/parser/prefix-operator-call.test.d.ts.map +1 -0
- package/dist/test/unit/parser/prefix-operator-call.test.js +185 -0
- package/dist/test/unit/parser/prefix-operator-call.test.js.map +1 -0
- package/dist/test/unit/scripts/corpus-loader.test.d.ts +2 -0
- package/dist/test/unit/scripts/corpus-loader.test.d.ts.map +1 -0
- package/dist/test/unit/scripts/corpus-loader.test.js +85 -0
- package/dist/test/unit/scripts/corpus-loader.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -38,6 +38,12 @@ function decimalRoundingMode(mode) {
|
|
|
38
38
|
throw new InterpreterError(`Decimal: unknown rounding mode ${JSON.stringify(mode)}; use "HALF_UP", "HALF_EVEN" or "DOWN".`);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* 十进制数值字面量(整数/小数/科学计数)。无歧义分支、线性匹配,见 decimalScale 里
|
|
43
|
+
* 记录的 ReDoS 教训。"0x10"/"Infinity"/"NaN"/"" 一律不匹配——与 Java
|
|
44
|
+
* `Double.parseDouble` 拒绝十六进制整数、与本仓 toInt 拒绝空串同向。
|
|
45
|
+
*/
|
|
46
|
+
const NUMERIC_LITERAL_RE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
41
47
|
/** scale 参数校验:必须是 0..18 的整数(v1 上限 scale 18,与 ADR 0025 一致)。 */
|
|
42
48
|
function decimalScale(scale) {
|
|
43
49
|
let n;
|
|
@@ -56,7 +62,7 @@ function decimalScale(scale) {
|
|
|
56
62
|
// scale 可由宿主传入,属不可控输入,必须走线性匹配。
|
|
57
63
|
// 改写后语义完全一致("2"/"1e1"/"+2"/"2.0"/".5"/"2." 接受;
|
|
58
64
|
// "2d"/"0x10"/"0x1p3"/"" 拒绝),2 万字符耗时降到 0.07ms。
|
|
59
|
-
if (
|
|
65
|
+
if (!NUMERIC_LITERAL_RE.test(t)) {
|
|
60
66
|
throw new InterpreterError(`Decimal: scale must be an integer in [0, 18], got ${JSON.stringify(scale)}.`);
|
|
61
67
|
}
|
|
62
68
|
n = Number(t);
|
|
@@ -119,6 +125,60 @@ function toInt(v, ctx) {
|
|
|
119
125
|
}
|
|
120
126
|
return bad();
|
|
121
127
|
}
|
|
128
|
+
/** Verdict 构造参数必须是非空白 Text:空/纯空白理由等于没有决策(与 @id 拒空白同口径),非 Text 不做隐式转换(与 truffle 一致)。 */
|
|
129
|
+
function verdictText(v, fn, field) {
|
|
130
|
+
if (typeof v !== 'string')
|
|
131
|
+
throw new InterpreterError(`${fn}: ${field} must be Text, got ${Array.isArray(v) ? 'List' : typeof v}`);
|
|
132
|
+
if (v.trim() === '')
|
|
133
|
+
throw new InterpreterError(`${fn}: ${field} must not be empty`);
|
|
134
|
+
return v;
|
|
135
|
+
}
|
|
136
|
+
/** 布尔上下文遇到 Verdict 时的统一错误信息(与 truffle Builtins.toBool 同文)。 */
|
|
137
|
+
const VERDICT_AS_BOOL_ERROR = 'Verdict cannot be used as Bool; compare its outcome field instead';
|
|
138
|
+
/** 运行时 Verdict 值:带 `__type: "Verdict"` 标记的对象。 */
|
|
139
|
+
function isVerdictValue(v) {
|
|
140
|
+
return typeof v === 'object' && v !== null && v.__type === 'Verdict';
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 严格数值换算——truffle `Builtins.toDouble` 接受集的**严格子集**(issue #193)。
|
|
144
|
+
*
|
|
145
|
+
* List.sum/min/max/sort/sortBy/minBy/maxBy 此前直接 `Number(x)`:`Number("2o")`
|
|
146
|
+
* 得 NaN,sum 静默产出 NaN、max 静默返回首元素,而 truffle 同输入抛
|
|
147
|
+
* `NumberFormatException`。与 `toDouble`(`Double.parseDouble`)的已知差异:本函数额外
|
|
148
|
+
* 拒绝 "NaN"/"Infinity"/类型后缀("1d")/十六进制浮点("0x1p3")——它们在合规引擎里
|
|
149
|
+
* 只可能是坏输入。另注意 truffle 的 `List.sum` 实际走 `toLong`(仅整数),两侧字符串
|
|
150
|
+
* 接受集尚未统一,见 aster-lang-ts#199。逐条语义:
|
|
151
|
+
* - number → 原样返回(含真实 NaN/Infinity:Java `doubleValue()` 同样不拦,
|
|
152
|
+
* 它们只能来自算术,不是坏输入);
|
|
153
|
+
* - bigint / Decimal → 转 double(对齐 `Number.doubleValue()`);
|
|
154
|
+
* - string → 严格十进制解析且结果有限;空串、"NaN"、"Infinity"、非数值文本拒绝;
|
|
155
|
+
* - 其他 → 抛错。
|
|
156
|
+
*
|
|
157
|
+
* ★不能退回裸 `Number(v)`:`Number([...])` 会对数组调 `toString()`,把整个嵌套结构
|
|
158
|
+
* 物化成巨型字符串再返回 NaN,那份巨串不经过 chargeAllocation(实测 9 层嵌套 +
|
|
159
|
+
* `List.sort` 10787ms / +707MB 且 success=true),且与 truffle 的类型闸门判定分叉。
|
|
160
|
+
*/
|
|
161
|
+
function toNum(v, ctx) {
|
|
162
|
+
const bad = () => {
|
|
163
|
+
throw new InterpreterError(`${ctx}: expected Number, got ${JSON.stringify(v) ?? String(v)}`);
|
|
164
|
+
};
|
|
165
|
+
if (typeof v === 'number')
|
|
166
|
+
return v;
|
|
167
|
+
if (typeof v === 'bigint')
|
|
168
|
+
return Number(v);
|
|
169
|
+
if (v instanceof Decimal)
|
|
170
|
+
return v.toNumber();
|
|
171
|
+
if (typeof v === 'string') {
|
|
172
|
+
const t = v.trim();
|
|
173
|
+
if (!NUMERIC_LITERAL_RE.test(t))
|
|
174
|
+
return bad();
|
|
175
|
+
const n = Number(t);
|
|
176
|
+
if (!Number.isFinite(n))
|
|
177
|
+
return bad();
|
|
178
|
+
return n;
|
|
179
|
+
}
|
|
180
|
+
return bad();
|
|
181
|
+
}
|
|
122
182
|
/** 把任意操作数转成 Decimal(Int/Long 精确提升;Double 禁;已是 Decimal 原样)。 */
|
|
123
183
|
function toDecimalArg(v, ctx) {
|
|
124
184
|
if (v instanceof Decimal)
|
|
@@ -481,6 +541,40 @@ const MAX_CALL_DEPTH = 50;
|
|
|
481
541
|
// statementLimit/MAX_STEPS 只数解释器步进不数 native range 循环 → range(0, 2e9) 会
|
|
482
542
|
// 撑爆内存。与 aster-lang-truffle Builtins.MAX_RANGE_SIZE 保持一致,维持双引擎 parity。
|
|
483
543
|
const MAX_RANGE_SIZE = 1_000_000;
|
|
544
|
+
/**
|
|
545
|
+
* 单次执行允许物化的元素总数上限(累计分配预算)。
|
|
546
|
+
*
|
|
547
|
+
* ★**为什么单个 builtin 的上限不够**:`List.range` 封顶 1e6、`List.combinations`
|
|
548
|
+
* 封顶 C(n,k)≤5000,单看都对,但**组合起来没有任何约束**:
|
|
549
|
+
*
|
|
550
|
+
* Let v0 be List.range(0, 1000000).
|
|
551
|
+
* Let v1 be List.concat(v0, v0). -- 每行翻倍
|
|
552
|
+
* ... 重复 8 次 ...
|
|
553
|
+
*
|
|
554
|
+
* 不到 200 字节源码即可把规模推到约 1.3 亿元素。实测本引擎 7 轮=1.28 亿元素吃掉
|
|
555
|
+
* 3.3GB 堆且**返回 success**(静默成功,最坏形态);8 轮=2.56 亿则撞 V8 的
|
|
556
|
+
* `Array.concat` 2^27 上限报 `Invalid array length`——**那是 V8 的硬限不是我们的防护**。
|
|
557
|
+
* truffle 侧 1403ms 抛 `Java heap space`。
|
|
558
|
+
*
|
|
559
|
+
* ★**为什么既有防护全没拦住**:`MAX_STEPS` 只数解释器步进——`concat` 在它眼里是
|
|
560
|
+
* 1 步,底层却是几千万次数组拷贝;宿主侧 5 秒 wall-clock 看门狗**来不及**
|
|
561
|
+
* (OOM 在 1.4 秒),它只对「慢」攻击有效,对「快而肥」的从不参与。
|
|
562
|
+
*
|
|
563
|
+
* ★**为什么是累计预算而非给 concat 加上限**:逐个 builtin 打补丁是打地鼠
|
|
564
|
+
* (map/filter/groupBy 同样能放大)。收敛成单一防线,在 `evalStdlibCall`
|
|
565
|
+
* 唯一调用点统一计量。
|
|
566
|
+
*
|
|
567
|
+
* 与 aster-lang-truffle `AllocationBudget.MAX_ALLOCATION_BUDGET` **必须同值**,
|
|
568
|
+
* 否则同一规则两引擎一个通过一个拒绝 = parity 分叉。
|
|
569
|
+
*
|
|
570
|
+
* 固定常量,不跟随堆/GC 状态——跟随运行时状态会破坏「两引擎逐字节一致 + 可回放」。
|
|
571
|
+
*/
|
|
572
|
+
const MAX_ALLOCATION_BUDGET = 10_000_000;
|
|
573
|
+
/**
|
|
574
|
+
* 计量递归的深度上限,与 truffle `Builtins.CHARGE_MAX_DEPTH` **必须同值**。
|
|
575
|
+
* 固定常量不跟随运行时栈——同 VALUE_EQUALS_MAX_DEPTH 的理由。
|
|
576
|
+
*/
|
|
577
|
+
const CHARGE_MAX_DEPTH = 8;
|
|
484
578
|
/** evalStdlibCall 的哨兵返回值:表示"不是已知 stdlib 调用"。用 Symbol 避免与任何合法返回值冲突。 */
|
|
485
579
|
const NOT_STDLIB = Symbol('not-stdlib');
|
|
486
580
|
/**
|
|
@@ -565,6 +659,10 @@ const BUILTIN_ARITY = {
|
|
|
565
659
|
'Result.tapError': [2, 2],
|
|
566
660
|
'Result.unwrap': [1, 1],
|
|
567
661
|
'Result.unwrapErr': [1, 1],
|
|
662
|
+
'Verdict.allow': [0, 0],
|
|
663
|
+
'Verdict.deny': [1, 1],
|
|
664
|
+
'Verdict.require_approval': [2, 2],
|
|
665
|
+
'Verdict.escalate': [1, 1],
|
|
568
666
|
'Text.concat': [2, 2],
|
|
569
667
|
'Text.contains': [2, 2],
|
|
570
668
|
'Text.equals': [2, 2],
|
|
@@ -647,6 +745,8 @@ class Interpreter {
|
|
|
647
745
|
enumVariantToEnum;
|
|
648
746
|
/** 执行步数计数器(防无限循环) */
|
|
649
747
|
steps = 0;
|
|
748
|
+
/** 本次执行累计物化的集合元素数(见 MAX_ALLOCATION_BUDGET)。 */
|
|
749
|
+
allocated = 0;
|
|
650
750
|
/** 步数上限(默认 MAX_STEPS,可由受信调用方上调) */
|
|
651
751
|
maxSteps;
|
|
652
752
|
/** 调用深度计数器(防无限递归) */
|
|
@@ -971,8 +1071,13 @@ class Interpreter {
|
|
|
971
1071
|
// 必须在用户函数查找之前,否则 Text.concat 落入未定义函数分支。
|
|
972
1072
|
if (call.target.kind === 'Name' && call.target.name.includes('.')) {
|
|
973
1073
|
const stdlib = this.evalStdlibCall(call.target.name, call.args, env);
|
|
974
|
-
if (stdlib !== NOT_STDLIB)
|
|
1074
|
+
if (stdlib !== NOT_STDLIB) {
|
|
1075
|
+
// 累计分配预算:在**唯一调用点**统一计量,而非 evalStdlibCall 内部 70+ 个
|
|
1076
|
+
// return 逐个补——逐个补必然漏掉新增的那个。与 truffle Builtins.call 同位置。
|
|
1077
|
+
// (本引擎这里确实是唯一调用点;truffle 侧则不是,内联特化会绕过 Builtins.call。)
|
|
1078
|
+
this.chargeAllocation(stdlib);
|
|
975
1079
|
return stdlib;
|
|
1080
|
+
}
|
|
976
1081
|
}
|
|
977
1082
|
// 函数调用:可能是用户定义的模块函数,或绑定在环境里的闭包变量
|
|
978
1083
|
// (Let f be function with …)。先求值参数,再分派。
|
|
@@ -1389,28 +1494,38 @@ class Interpreter {
|
|
|
1389
1494
|
return acc;
|
|
1390
1495
|
}
|
|
1391
1496
|
// === 通用集合 stdlib(ADR 0024 受控扩展,与 truffle Builtins 镜像,逐位 parity)===
|
|
1392
|
-
// 数值序用
|
|
1497
|
+
// 数值序用 toNum 严格换算后比较(非数值元素响亮失败,见 toNum);排序稳定、升序。
|
|
1393
1498
|
case 'List.sum': {
|
|
1394
1499
|
const l = reqList('List.sum', a()[0]);
|
|
1395
1500
|
let s = 0;
|
|
1396
1501
|
for (const x of l)
|
|
1397
|
-
s +=
|
|
1502
|
+
s += toNum(x, 'List.sum');
|
|
1398
1503
|
return s;
|
|
1399
1504
|
}
|
|
1400
1505
|
case 'List.min': {
|
|
1401
1506
|
const l = reqNonEmpty('List.min', a()[0]);
|
|
1402
1507
|
let best = l[0];
|
|
1403
|
-
|
|
1404
|
-
|
|
1508
|
+
let bestK = toNum(best, 'List.min');
|
|
1509
|
+
for (const x of l) {
|
|
1510
|
+
const k = toNum(x, 'List.min');
|
|
1511
|
+
if (k < bestK) {
|
|
1405
1512
|
best = x;
|
|
1513
|
+
bestK = k;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1406
1516
|
return best;
|
|
1407
1517
|
}
|
|
1408
1518
|
case 'List.max': {
|
|
1409
1519
|
const l = reqNonEmpty('List.max', a()[0]);
|
|
1410
1520
|
let best = l[0];
|
|
1411
|
-
|
|
1412
|
-
|
|
1521
|
+
let bestK = toNum(best, 'List.max');
|
|
1522
|
+
for (const x of l) {
|
|
1523
|
+
const k = toNum(x, 'List.max');
|
|
1524
|
+
if (k > bestK) {
|
|
1413
1525
|
best = x;
|
|
1526
|
+
bestK = k;
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1414
1529
|
return best;
|
|
1415
1530
|
}
|
|
1416
1531
|
case 'List.distinct': {
|
|
@@ -1455,11 +1570,13 @@ class Interpreter {
|
|
|
1455
1570
|
// 与 truffle 逐位一致:纯整数索引推进算法(不依赖语言细节)。DoS 防护:n≤64 +
|
|
1456
1571
|
// 结果数 C(n,k)≤上限(先算组合数,超限即抛,不先生成)——多租户沙箱铁律。
|
|
1457
1572
|
// 边界:k<0 抛错;k>n 返回 [];k=0 返回 [[]]。保留元素原值与相对顺序。
|
|
1573
|
+
// k 走严格 toInt(对齐 truffle `toInt`:Integer.parseInt / intValue)——裸 Number()
|
|
1574
|
+
// 下 ""→0、true→1、"0x2"→2 会被静默接受,Number.isInteger 只拦得住 NaN。
|
|
1458
1575
|
case 'List.combinations': {
|
|
1459
1576
|
const l = reqList('List.combinations', a()[0]);
|
|
1460
|
-
const k =
|
|
1461
|
-
if (
|
|
1462
|
-
throw new InterpreterError(`List.combinations: k 须为非负整数,got ${
|
|
1577
|
+
const k = toInt(a()[1], 'List.combinations');
|
|
1578
|
+
if (k < 0)
|
|
1579
|
+
throw new InterpreterError(`List.combinations: k 须为非负整数,got ${k}`);
|
|
1463
1580
|
const n = l.length;
|
|
1464
1581
|
const MAX_N = 64, MAX_RESULT = 5000;
|
|
1465
1582
|
if (n > MAX_N)
|
|
@@ -1495,7 +1612,7 @@ class Interpreter {
|
|
|
1495
1612
|
}
|
|
1496
1613
|
case 'List.sort': {
|
|
1497
1614
|
const l = reqList('List.sort', a()[0]);
|
|
1498
|
-
return [...l].sort((x, y) =>
|
|
1615
|
+
return [...l].sort((x, y) => toNum(x, 'List.sort') - toNum(y, 'List.sort'));
|
|
1499
1616
|
}
|
|
1500
1617
|
case 'List.count': {
|
|
1501
1618
|
const l = reqList('List.count', this.evalExpr(argExprs[0], env));
|
|
@@ -1509,15 +1626,16 @@ class Interpreter {
|
|
|
1509
1626
|
case 'List.sortBy': {
|
|
1510
1627
|
const l = reqList('List.sortBy', this.evalExpr(argExprs[0], env));
|
|
1511
1628
|
const keyFn = callableArg(1);
|
|
1512
|
-
|
|
1629
|
+
const key = (x) => toNum(this.applyCallable(keyFn, [x]), 'List.sortBy');
|
|
1630
|
+
return [...l].sort((x, y) => key(x) - key(y));
|
|
1513
1631
|
}
|
|
1514
1632
|
case 'List.minBy': {
|
|
1515
1633
|
const l = reqNonEmpty('List.minBy', this.evalExpr(argExprs[0], env));
|
|
1516
1634
|
const keyFn = callableArg(1);
|
|
1517
1635
|
let best = l[0];
|
|
1518
|
-
let bestK =
|
|
1636
|
+
let bestK = toNum(this.applyCallable(keyFn, [best]), 'List.minBy');
|
|
1519
1637
|
for (const x of l) {
|
|
1520
|
-
const k =
|
|
1638
|
+
const k = toNum(this.applyCallable(keyFn, [x]), 'List.minBy');
|
|
1521
1639
|
if (k < bestK) {
|
|
1522
1640
|
best = x;
|
|
1523
1641
|
bestK = k;
|
|
@@ -1529,9 +1647,9 @@ class Interpreter {
|
|
|
1529
1647
|
const l = reqNonEmpty('List.maxBy', this.evalExpr(argExprs[0], env));
|
|
1530
1648
|
const keyFn = callableArg(1);
|
|
1531
1649
|
let best = l[0];
|
|
1532
|
-
let bestK =
|
|
1650
|
+
let bestK = toNum(this.applyCallable(keyFn, [best]), 'List.maxBy');
|
|
1533
1651
|
for (const x of l) {
|
|
1534
|
-
const k =
|
|
1652
|
+
const k = toNum(this.applyCallable(keyFn, [x]), 'List.maxBy');
|
|
1535
1653
|
if (k > bestK) {
|
|
1536
1654
|
best = x;
|
|
1537
1655
|
bestK = k;
|
|
@@ -1634,6 +1752,20 @@ class Interpreter {
|
|
|
1634
1752
|
}
|
|
1635
1753
|
throw new InterpreterError('Result.tapError: expected Result (Ok or Err)');
|
|
1636
1754
|
}
|
|
1755
|
+
// === Verdict(ADR 0039):与 truffle Builtins 同键序;reason/role 必须是非空 Text ===
|
|
1756
|
+
case 'Verdict.allow': return { __type: 'Verdict', outcome: 'ALLOW' };
|
|
1757
|
+
case 'Verdict.deny': {
|
|
1758
|
+
const [r] = a();
|
|
1759
|
+
return { __type: 'Verdict', outcome: 'DENY', reason: verdictText(r, 'Verdict.deny', 'reason') };
|
|
1760
|
+
}
|
|
1761
|
+
case 'Verdict.require_approval': {
|
|
1762
|
+
const [role, r] = a();
|
|
1763
|
+
return { __type: 'Verdict', outcome: 'REQUIRE_APPROVAL', role: verdictText(role, 'Verdict.require_approval', 'role'), reason: verdictText(r, 'Verdict.require_approval', 'reason') };
|
|
1764
|
+
}
|
|
1765
|
+
case 'Verdict.escalate': {
|
|
1766
|
+
const [r] = a();
|
|
1767
|
+
return { __type: 'Verdict', outcome: 'ESCALATE', reason: verdictText(r, 'Verdict.escalate', 'reason') };
|
|
1768
|
+
}
|
|
1637
1769
|
default:
|
|
1638
1770
|
return NOT_STDLIB;
|
|
1639
1771
|
}
|
|
@@ -1757,7 +1889,15 @@ class Interpreter {
|
|
|
1757
1889
|
switch (op) {
|
|
1758
1890
|
case '+': {
|
|
1759
1891
|
if (typeof left === 'string' || typeof right === 'string') {
|
|
1760
|
-
|
|
1892
|
+
const joined = String(left) + String(right);
|
|
1893
|
+
// ★`+` 的字符串分支走 evalBinary,**不经过 evalStdlibCall**,
|
|
1894
|
+
// 而 chargeAllocation 的唯一调用点在那条 stdlib 分支上 → 此处必须单独记账。
|
|
1895
|
+
// 否则用 `plus` 代替 `Text.concat` 做翻倍即可完全绕过预算:
|
|
1896
|
+
// 实测 20 轮 → 3355 万字符 success=true,而同一份 IR 在 truffle 侧
|
|
1897
|
+
// (`+`→`add` builtin 经 Builtins.call 被计量)是 REJECTED
|
|
1898
|
+
// ——这是一条真实发生的**双引擎判定分叉**。
|
|
1899
|
+
this.chargeAllocation(joined);
|
|
1900
|
+
return joined;
|
|
1761
1901
|
}
|
|
1762
1902
|
this.assertNumbers(op, left, right);
|
|
1763
1903
|
return left + right;
|
|
@@ -1845,10 +1985,74 @@ class Interpreter {
|
|
|
1845
1985
|
throw new InterpreterError(`Maximum execution steps (${this.maxSteps}) exceeded — possible infinite loop`);
|
|
1846
1986
|
}
|
|
1847
1987
|
}
|
|
1988
|
+
/**
|
|
1989
|
+
* 按返回值的规模扣减分配预算(见 MAX_ALLOCATION_BUDGET)。
|
|
1990
|
+
*
|
|
1991
|
+
* 逐个**顶层**元素计量。嵌套结构(如 groupBy 的 Map<K, List>)的内层元素本身
|
|
1992
|
+
* 来自已被计过的入参列表——内层每个 List.range 都各自经过本方法单独计量,
|
|
1993
|
+
* 故不会漏计,也不会因重复计量误拒合法的分组操作。
|
|
1994
|
+
*
|
|
1995
|
+
* 与 truffle `Builtins.chargeForResult` 口径逐条对齐:
|
|
1996
|
+
* Array↔Collection、Map↔Map、string↔CharSequence。
|
|
1997
|
+
* ★两边的类型判据必须保持等价——若将来有 builtin 返回 Set(Java 的 Set 是
|
|
1998
|
+
* Collection、会被计量;TS 的 Set 不是 Array、不会被计量),就是 parity 分叉。
|
|
1999
|
+
* 现已确认两侧均无 builtin 返回 Set。
|
|
2000
|
+
*/
|
|
2001
|
+
chargeAllocation(result) {
|
|
2002
|
+
const count = this.countElements(result, 0);
|
|
2003
|
+
if (count <= 0)
|
|
2004
|
+
return;
|
|
2005
|
+
// 先判溢出再累加,与 truffle AllocationBudget.charge 同形。
|
|
2006
|
+
if (count > MAX_ALLOCATION_BUDGET ||
|
|
2007
|
+
this.allocated > MAX_ALLOCATION_BUDGET - count) {
|
|
2008
|
+
this.allocated = MAX_ALLOCATION_BUDGET + 1;
|
|
2009
|
+
throw new InterpreterError(`分配预算耗尽:单次执行累计物化元素数超过上限 ${MAX_ALLOCATION_BUDGET},拒绝以防内存耗尽 DoS`);
|
|
2010
|
+
}
|
|
2011
|
+
this.allocated += count;
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
2014
|
+
* 递归统计返回值物化出的元素总数(深度有界)。
|
|
2015
|
+
*
|
|
2016
|
+
* ★**为什么必须递归、不能只计顶层**:只计顶层有一个 Critical 绕过——
|
|
2017
|
+
* `List.groupBy(a, one)` 把 1e6 元素全归进同一组,返回的 Map 顶层 size=1,
|
|
2018
|
+
* **只扣 1 点而真实物化 1e6**。重复 20 次即 840 字节源码 → 实测 Java 侧 1972ms
|
|
2019
|
+
* 物化 210MB 且 SUCCESS;本引擎抬高 maxSteps 后同样 success、+396MB。
|
|
2020
|
+
*
|
|
2021
|
+
* 此前注释写的「内层元素来自已被计过的入参列表,故不会漏计」是**错的**:
|
|
2022
|
+
* 入参只被扣过**一次**,却可被**重新物化任意多次**。
|
|
2023
|
+
*
|
|
2024
|
+
* 深度上限固定 8,与 truffle `CHARGE_MAX_DEPTH` **必须同值**;理由同
|
|
2025
|
+
* VALUE_EQUALS_MAX_DEPTH:固定常量不跟随运行时栈,否则同一规则在不同机器
|
|
2026
|
+
* 给出不同结果。超深不再下探(宁可少算也不抛)。
|
|
2027
|
+
*
|
|
2028
|
+
* 字符串按字符数计(`Text.concat` 翻倍向量,见 MAX_ALLOCATION_BUDGET 注释)。
|
|
2029
|
+
*/
|
|
2030
|
+
countElements(value, depth) {
|
|
2031
|
+
if (depth > CHARGE_MAX_DEPTH)
|
|
2032
|
+
return 0;
|
|
2033
|
+
if (Array.isArray(value)) {
|
|
2034
|
+
let n = value.length;
|
|
2035
|
+
for (const e of value)
|
|
2036
|
+
n += this.countElements(e, depth + 1);
|
|
2037
|
+
return n;
|
|
2038
|
+
}
|
|
2039
|
+
if (value instanceof Map) {
|
|
2040
|
+
let n = value.size;
|
|
2041
|
+
for (const e of value.values())
|
|
2042
|
+
n += this.countElements(e, depth + 1);
|
|
2043
|
+
return n;
|
|
2044
|
+
}
|
|
2045
|
+
if (typeof value === 'string')
|
|
2046
|
+
return value.length;
|
|
2047
|
+
return 0;
|
|
2048
|
+
}
|
|
1848
2049
|
/** 判断值是否为 truthy */
|
|
1849
2050
|
isTruthy(value) {
|
|
1850
2051
|
if (value === null || value === undefined)
|
|
1851
2052
|
return false;
|
|
2053
|
+
// Verdict 不可当布尔用(ADR 0039 §2.2):DENY 若按对象真值放行即 fail-open,必须直接失败(与 truffle toBool 一致)
|
|
2054
|
+
if (isVerdictValue(value))
|
|
2055
|
+
throw new InterpreterError(VERDICT_AS_BOOL_ERROR);
|
|
1852
2056
|
if (typeof value === 'boolean')
|
|
1853
2057
|
return value;
|
|
1854
2058
|
if (typeof value === 'number')
|