@velarscript/compiler 0.10.1
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/LICENSE +201 -0
- package/README.md +70 -0
- package/dist/analyzer.d.ts +1175 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +12130 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/application-package-host.d.ts +28 -0
- package/dist/application-package-host.d.ts.map +1 -0
- package/dist/application-package-host.js +2 -0
- package/dist/application-package-host.js.map +1 -0
- package/dist/ast.d.ts +853 -0
- package/dist/ast.d.ts.map +1 -0
- package/dist/ast.js +245 -0
- package/dist/ast.js.map +1 -0
- package/dist/class-runtime.d.ts +6 -0
- package/dist/class-runtime.d.ts.map +1 -0
- package/dist/class-runtime.js +45 -0
- package/dist/class-runtime.js.map +1 -0
- package/dist/collection-lowering-runtime.d.ts +7 -0
- package/dist/collection-lowering-runtime.d.ts.map +1 -0
- package/dist/collection-lowering-runtime.js +952 -0
- package/dist/collection-lowering-runtime.js.map +1 -0
- package/dist/collection-runtime.d.ts +16 -0
- package/dist/collection-runtime.d.ts.map +1 -0
- package/dist/collection-runtime.js +231 -0
- package/dist/collection-runtime.js.map +1 -0
- package/dist/core-vocabulary.d.ts +141 -0
- package/dist/core-vocabulary.d.ts.map +1 -0
- package/dist/core-vocabulary.js +88 -0
- package/dist/core-vocabulary.js.map +1 -0
- package/dist/diagnostic.d.ts +42 -0
- package/dist/diagnostic.d.ts.map +1 -0
- package/dist/diagnostic.js +66 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/embedded-javascript.d.ts +74 -0
- package/dist/embedded-javascript.d.ts.map +1 -0
- package/dist/embedded-javascript.js +341 -0
- package/dist/embedded-javascript.js.map +1 -0
- package/dist/embedded-module.d.ts +11 -0
- package/dist/embedded-module.d.ts.map +1 -0
- package/dist/embedded-module.js +67 -0
- package/dist/embedded-module.js.map +1 -0
- package/dist/embedded-source.d.ts +19 -0
- package/dist/embedded-source.d.ts.map +1 -0
- package/dist/embedded-source.js +61 -0
- package/dist/embedded-source.js.map +1 -0
- package/dist/emitter.d.ts +188 -0
- package/dist/emitter.d.ts.map +1 -0
- package/dist/emitter.js +3254 -0
- package/dist/emitter.js.map +1 -0
- package/dist/error-runtime.d.ts +17 -0
- package/dist/error-runtime.d.ts.map +1 -0
- package/dist/error-runtime.js +109 -0
- package/dist/error-runtime.js.map +1 -0
- package/dist/extension.d.ts +373 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +25 -0
- package/dist/extension.js.map +1 -0
- package/dist/formatter.d.ts +13 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +1440 -0
- package/dist/formatter.js.map +1 -0
- package/dist/framework-host.d.ts +87 -0
- package/dist/framework-host.d.ts.map +1 -0
- package/dist/framework-host.js +2 -0
- package/dist/framework-host.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +932 -0
- package/dist/index.js.map +1 -0
- package/dist/interpolated-string.d.ts +47 -0
- package/dist/interpolated-string.d.ts.map +1 -0
- package/dist/interpolated-string.js +344 -0
- package/dist/interpolated-string.js.map +1 -0
- package/dist/json-runtime.d.ts +2 -0
- package/dist/json-runtime.d.ts.map +1 -0
- package/dist/json-runtime.js +193 -0
- package/dist/json-runtime.js.map +1 -0
- package/dist/language-guidance.d.ts +31 -0
- package/dist/language-guidance.d.ts.map +1 -0
- package/dist/language-guidance.js +187 -0
- package/dist/language-guidance.js.map +1 -0
- package/dist/lexer.d.ts +85 -0
- package/dist/lexer.d.ts.map +1 -0
- package/dist/lexer.js +1004 -0
- package/dist/lexer.js.map +1 -0
- package/dist/limits.d.ts +2 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +2 -0
- package/dist/limits.js.map +1 -0
- package/dist/mechanical-fix.d.ts +26 -0
- package/dist/mechanical-fix.d.ts.map +1 -0
- package/dist/mechanical-fix.js +61 -0
- package/dist/mechanical-fix.js.map +1 -0
- package/dist/narrowing-runtime.d.ts +5 -0
- package/dist/narrowing-runtime.d.ts.map +1 -0
- package/dist/narrowing-runtime.js +27 -0
- package/dist/narrowing-runtime.js.map +1 -0
- package/dist/number-runtime.d.ts +3 -0
- package/dist/number-runtime.d.ts.map +1 -0
- package/dist/number-runtime.js +37 -0
- package/dist/number-runtime.js.map +1 -0
- package/dist/parser.d.ts +245 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +3582 -0
- package/dist/parser.js.map +1 -0
- package/dist/primitive-runtime.d.ts +4 -0
- package/dist/primitive-runtime.d.ts.map +1 -0
- package/dist/primitive-runtime.js +39 -0
- package/dist/primitive-runtime.js.map +1 -0
- package/dist/promise-runtime.d.ts +6 -0
- package/dist/promise-runtime.d.ts.map +1 -0
- package/dist/promise-runtime.js +86 -0
- package/dist/promise-runtime.js.map +1 -0
- package/dist/reactive-bridge-runtime.d.ts +7 -0
- package/dist/reactive-bridge-runtime.d.ts.map +1 -0
- package/dist/reactive-bridge-runtime.js +112 -0
- package/dist/reactive-bridge-runtime.js.map +1 -0
- package/dist/runtime-abi.d.ts +24 -0
- package/dist/runtime-abi.d.ts.map +1 -0
- package/dist/runtime-abi.js +24 -0
- package/dist/runtime-abi.js.map +1 -0
- package/dist/semantic.d.ts +142 -0
- package/dist/semantic.d.ts.map +1 -0
- package/dist/semantic.js +845 -0
- package/dist/semantic.js.map +1 -0
- package/dist/source-names.d.ts +32 -0
- package/dist/source-names.d.ts.map +1 -0
- package/dist/source-names.js +101 -0
- package/dist/source-names.js.map +1 -0
- package/dist/source.d.ts +19 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +64 -0
- package/dist/source.js.map +1 -0
- package/dist/text-runtime.d.ts +2 -0
- package/dist/text-runtime.d.ts.map +1 -0
- package/dist/text-runtime.js +285 -0
- package/dist/text-runtime.js.map +1 -0
- package/dist/token.d.ts +19 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +51 -0
- package/dist/token.js.map +1 -0
- package/dist/type-registry-runtime.d.ts +7 -0
- package/dist/type-registry-runtime.d.ts.map +1 -0
- package/dist/type-registry-runtime.js +51 -0
- package/dist/type-registry-runtime.js.map +1 -0
- package/dist/type-validation-runtime.d.ts +17 -0
- package/dist/type-validation-runtime.d.ts.map +1 -0
- package/dist/type-validation-runtime.js +150 -0
- package/dist/type-validation-runtime.js.map +1 -0
- package/dist/types.d.ts +356 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1170 -0
- package/dist/types.js.map +1 -0
- package/dist/utf8-runtime.d.ts +7 -0
- package/dist/utf8-runtime.d.ts.map +1 -0
- package/dist/utf8-runtime.js +43 -0
- package/dist/utf8-runtime.js.map +1 -0
- package/package.json +48 -0
package/dist/emitter.js
ADDED
|
@@ -0,0 +1,3254 @@
|
|
|
1
|
+
import { blockContainsDirectAwait, expressionContainsDirectAwait as containsDirectAwait, testFunctionName } from "./ast.js";
|
|
2
|
+
import { VELAR_CLASS_FIELD_MODULE, VELAR_CLASS_FIELD_RUNTIME } from "./class-runtime.js";
|
|
3
|
+
import { VELAR_COLLECTION_HOST_EXPORTS, VELAR_COLLECTION_HOST_MODULE, VELAR_COLLECTION_IDENTITY_RUNTIME, VELAR_COLLECTION_LIST_RUNTIME, VELAR_COLLECTION_RECORD_RUNTIME, VELAR_COLLECTION_SET_MAP_RUNTIME, VELAR_COLLECTION_TYPE_RUNTIME } from "./collection-runtime.js";
|
|
4
|
+
import { VELAR_COLLECTION_LOWERING_EXPORTS, VELAR_COLLECTION_LOWERING_MODULE, VELAR_COLLECTION_LOWERING_RUNTIME } from "./collection-lowering-runtime.js";
|
|
5
|
+
import { describeType, formatTypeReference, formatTypeSyntax, mapNestedTypes, resolveTypeReference, semanticTypeIdentity, typeContainsParameter } from "./types.js";
|
|
6
|
+
import { disposeMemberKey, iterateMemberKey } from "./analyzer.js";
|
|
7
|
+
import { VELAR_ERROR_NORMALIZATION_MODULE, VELAR_ERROR_NORMALIZATION_RUNTIME, VELAR_HOST_ERROR_NAMES, VELAR_HOST_ERROR_RUNTIME } from "./error-runtime.js";
|
|
8
|
+
import { embeddedJavaScriptSpecifier } from "./embedded-module.js";
|
|
9
|
+
import { VELAR_NARROWING_MODULE, VELAR_NARROWING_RUNTIME } from "./narrowing-runtime.js";
|
|
10
|
+
import { VELAR_NUMBER_METHOD_RUNTIME } from "./number-runtime.js";
|
|
11
|
+
import { VELAR_PRIMITIVE_METHOD_MODULE } from "./primitive-runtime.js";
|
|
12
|
+
import { VELAR_PROMISE_NORMALIZATION_MODULE, VELAR_PROMISE_NORMALIZATION_RUNTIME } from "./promise-runtime.js";
|
|
13
|
+
import { VELAR_REACTIVE_BRIDGE_MODULE, VELAR_REACTIVE_BRIDGE_RUNTIME, VELAR_REACTIVE_COLLECTION_BRIDGE_RUNTIME } from "./reactive-bridge-runtime.js";
|
|
14
|
+
import { spanIdentity } from "./source.js";
|
|
15
|
+
import { VELAR_TEXT_METHOD_RUNTIME } from "./text-runtime.js";
|
|
16
|
+
import { VELAR_TYPE_REGISTRY_RUNTIME } from "./type-registry-runtime.js";
|
|
17
|
+
import { VELAR_RUNTIME_TYPE_COLLECTION_RUNTIME, VELAR_TYPE_VALIDATION_MODULE, VELAR_TYPE_VALIDATION_RUNTIME, VELAR_VALIDATION_ERROR_RUNTIME, } from "./type-validation-runtime.js";
|
|
18
|
+
const javaScriptNodeMarker = /\u0000VELAR_MAP_(\d+)\u0000/gu;
|
|
19
|
+
// ENM-U4 + COL-U5: the compiler-raised error types are nameable in source;
|
|
20
|
+
// their runtime classes carry compiler-owned names. The source names are
|
|
21
|
+
// reserved Core bindings, so a bare reference is always the builtin.
|
|
22
|
+
const builtinErrorRuntimeNames = new Map([
|
|
23
|
+
["ValidationError", "__VelarValidationError"],
|
|
24
|
+
["NarrowingError", "__VelarNarrowingError"],
|
|
25
|
+
["IndexError", "__VelarIndexError"],
|
|
26
|
+
]);
|
|
27
|
+
export class JavaScriptEmitter {
|
|
28
|
+
typeDeclarations = new Map();
|
|
29
|
+
runtimeTypes = new Set();
|
|
30
|
+
expandedRuntimeTypes = new Set();
|
|
31
|
+
/**
|
|
32
|
+
* D55 rule 121: the type parameters of the generic record currently being
|
|
33
|
+
* emitted. Inside that body a `T`-typed position is checked by the argument
|
|
34
|
+
* predicate the instantiation supplied, which is the only reading of `T` a
|
|
35
|
+
* validator can have once the type is erased.
|
|
36
|
+
*/
|
|
37
|
+
genericTypeParameters = null;
|
|
38
|
+
/** Hoisted `function __velarTypeOf_N()` bodies for instantiations written outside a generic. */
|
|
39
|
+
hoistedGenericInstances = new Map();
|
|
40
|
+
externModuleExports = new Map();
|
|
41
|
+
needsExternExportHelper = false;
|
|
42
|
+
hints;
|
|
43
|
+
forcedFunctionExports;
|
|
44
|
+
sharedRuntimeModules;
|
|
45
|
+
requiredRuntimeModules = new Set();
|
|
46
|
+
needsIndexHelpers = false;
|
|
47
|
+
needsBinaryHelpers = false;
|
|
48
|
+
needsBitwiseHelpers = false;
|
|
49
|
+
needsCollectionHelpers = false;
|
|
50
|
+
needsPrimitiveHelpers = false;
|
|
51
|
+
needsRecordHelpers = false;
|
|
52
|
+
needsObjectBindingHelpers = false;
|
|
53
|
+
needsListBindingHelpers = false;
|
|
54
|
+
needsDirectCollectionInfrastructure = false;
|
|
55
|
+
needsRuntimeTypeHelpers = false;
|
|
56
|
+
needsNumberHelper = false;
|
|
57
|
+
needsThrownValueHelper = false;
|
|
58
|
+
needsErrorCodeHelper = false;
|
|
59
|
+
requiredHostErrorClasses = new Set();
|
|
60
|
+
needsDetachedTaskHelper = false;
|
|
61
|
+
needsDisposalHelper = false;
|
|
62
|
+
needsIntegrityFailureHelper = false;
|
|
63
|
+
needsNarrowingErrorClass = false;
|
|
64
|
+
suppressPromiseNormalization = 0;
|
|
65
|
+
nextJavaScriptNodeId = 0;
|
|
66
|
+
javaScriptNodeSpans = new Map();
|
|
67
|
+
generatedMappings = [];
|
|
68
|
+
generatedCode = "";
|
|
69
|
+
sourcePath;
|
|
70
|
+
embeddedJavaScript = new Map();
|
|
71
|
+
constructor(hints, forcedFunctionExports = new Set(), options = {}) {
|
|
72
|
+
this.hints = hints;
|
|
73
|
+
this.forcedFunctionExports = forcedFunctionExports;
|
|
74
|
+
this.sharedRuntimeModules = options.sharedRuntimeModules === true;
|
|
75
|
+
this.sourcePath = options.sourcePath ?? "<source>";
|
|
76
|
+
}
|
|
77
|
+
emit(program) {
|
|
78
|
+
this.nextJavaScriptNodeId = 0;
|
|
79
|
+
this.javaScriptNodeSpans.clear();
|
|
80
|
+
this.requiredRuntimeModules.clear();
|
|
81
|
+
this.requiredHostErrorClasses.clear();
|
|
82
|
+
this.prepareEmbeddedJavaScript(program);
|
|
83
|
+
this.collectDeclarations(program);
|
|
84
|
+
this.collectRuntimeUses(program);
|
|
85
|
+
const statements = program.body
|
|
86
|
+
.map((statement) => this.emitJavaScriptNode(statement.span, () => this.emitStatement(statement, 0)))
|
|
87
|
+
.filter((item) => item.code.length > 0);
|
|
88
|
+
const needsDirectCollectionInfrastructure = this.needsDirectCollectionInfrastructure
|
|
89
|
+
|| this.needsRecordHelpers
|
|
90
|
+
|| this.needsObjectBindingHelpers
|
|
91
|
+
|| this.needsListBindingHelpers;
|
|
92
|
+
const helpers = [...this.additionalHelpers(program)];
|
|
93
|
+
const builtinValues = new Set(this.hints.builtinValueReferences.values());
|
|
94
|
+
if (builtinValues.has("Json")) {
|
|
95
|
+
helpers.push('import * as __velarJsonNamespace from "velar/json";');
|
|
96
|
+
this.requiredRuntimeModules.add("velar/json");
|
|
97
|
+
}
|
|
98
|
+
if (builtinValues.has("Promise")) {
|
|
99
|
+
helpers.push('import * as __velarPromiseNamespace from "velar/async";');
|
|
100
|
+
this.requiredRuntimeModules.add("velar/async");
|
|
101
|
+
}
|
|
102
|
+
if (builtinValues.has("Text")) {
|
|
103
|
+
helpers.push('import * as __velarTextNamespace from "velar/text";');
|
|
104
|
+
this.requiredRuntimeModules.add("velar/text");
|
|
105
|
+
}
|
|
106
|
+
if (builtinValues.has("Math")) {
|
|
107
|
+
helpers.push('import * as __velarMathNamespace from "velar/math";');
|
|
108
|
+
this.requiredRuntimeModules.add("velar/math");
|
|
109
|
+
}
|
|
110
|
+
if (builtinValues.has("range")) {
|
|
111
|
+
helpers.push('import { range as __velarRange } from "velar/collections";');
|
|
112
|
+
this.requiredRuntimeModules.add("velar/collections");
|
|
113
|
+
}
|
|
114
|
+
if (this.hints.nativeRangeForStatements.size > 0) {
|
|
115
|
+
helpers.push('import { range as __velarCountedRangeOwner } from "velar/collections";');
|
|
116
|
+
this.requiredRuntimeModules.add("velar/collections");
|
|
117
|
+
}
|
|
118
|
+
if (this.needsBinaryHelpers) {
|
|
119
|
+
helpers.push('import { Bytes as __velarBinaryRuntime } from "velar/binary";');
|
|
120
|
+
this.requiredRuntimeModules.add("velar/binary");
|
|
121
|
+
}
|
|
122
|
+
if (this.needsBitwiseHelpers) {
|
|
123
|
+
helpers.push([
|
|
124
|
+
"const __velarBitwiseApply = Reflect.apply;",
|
|
125
|
+
"const __velarBitwiseNumber = Number;",
|
|
126
|
+
"const __velarBitwiseNumberIsInteger = Number.isInteger;",
|
|
127
|
+
"const __velarBitwiseTypeError = TypeError;",
|
|
128
|
+
"const __velarBitwiseRangeError = RangeError;",
|
|
129
|
+
"function __velarBitwiseOperand(value, operator) {",
|
|
130
|
+
" if (typeof value !== \"number\" || !__velarBitwiseApply(__velarBitwiseNumberIsInteger, __velarBitwiseNumber, [value])) throw new __velarBitwiseTypeError(\"Bitwise '\" + operator + \"' requires integer operands\");",
|
|
131
|
+
" if (value < -2147483648 || value > 4294967295) throw new __velarBitwiseRangeError(\"Bitwise '\" + operator + \"' requires 32-bit integer operands\");",
|
|
132
|
+
" return value;",
|
|
133
|
+
"}",
|
|
134
|
+
"function __velarBitwiseUnary(value) { value = __velarBitwiseOperand(value, \"~\"); return ~value; }",
|
|
135
|
+
"function __velarBitwiseBinary(left, operator, right) {",
|
|
136
|
+
" left = __velarBitwiseOperand(left, operator);",
|
|
137
|
+
" if (operator === \"<<\" || operator === \">>\" || operator === \">>>\") {",
|
|
138
|
+
" if (typeof right !== \"number\" || !__velarBitwiseApply(__velarBitwiseNumberIsInteger, __velarBitwiseNumber, [right])) throw new __velarBitwiseTypeError(\"Bitwise '\" + operator + \"' requires an integer shift count\");",
|
|
139
|
+
" if (right < 0 || right > 31) throw new __velarBitwiseRangeError(\"Bitwise '\" + operator + \"' requires a shift count from 0 to 31\");",
|
|
140
|
+
" return operator === \"<<\" ? left << right : operator === \">>\" ? left >> right : left >>> right;",
|
|
141
|
+
" }",
|
|
142
|
+
" right = __velarBitwiseOperand(right, operator);",
|
|
143
|
+
" return operator === \"&\" ? left & right : operator === \"|\" ? left | right : left ^ right;",
|
|
144
|
+
"}",
|
|
145
|
+
].join("\n"));
|
|
146
|
+
}
|
|
147
|
+
if ([...this.hints.extensionCalls.values()].includes("core.duration-arithmetic"))
|
|
148
|
+
helpers.push([
|
|
149
|
+
"const __velarDurationApply = Reflect.apply;",
|
|
150
|
+
"const __velarDurationPattern = /^([+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+))(ms|s)$/;",
|
|
151
|
+
"const __velarDurationRegExpExec = RegExp.prototype.exec;",
|
|
152
|
+
"const __velarDurationNumber = Number;",
|
|
153
|
+
"const __velarDurationNumberIsFinite = Number.isFinite;",
|
|
154
|
+
"const __velarDurationObjectIs = Object.is;",
|
|
155
|
+
"const __velarDurationString = String;",
|
|
156
|
+
"const __velarDurationTypeError = TypeError;",
|
|
157
|
+
"const __velarDurationRangeError = RangeError;",
|
|
158
|
+
"function __velarDurationMilliseconds(value) {",
|
|
159
|
+
" if (typeof value !== \"string\") throw new __velarDurationTypeError(\"Duration arithmetic requires Duration values\");",
|
|
160
|
+
" const match = __velarDurationApply(__velarDurationRegExpExec, __velarDurationPattern, [value]);",
|
|
161
|
+
" if (!match) throw new __velarDurationTypeError(\"Duration arithmetic requires Duration values\");",
|
|
162
|
+
" const milliseconds = __velarDurationNumber(match[1]) * (match[2] === \"s\" ? 1000 : 1);",
|
|
163
|
+
" if (!__velarDurationNumberIsFinite(milliseconds)) throw new __velarDurationRangeError(\"Duration arithmetic must produce a finite value\");",
|
|
164
|
+
" return milliseconds;",
|
|
165
|
+
"}",
|
|
166
|
+
"function __velarDurationValue(milliseconds) {",
|
|
167
|
+
" if (!__velarDurationNumberIsFinite(milliseconds)) throw new __velarDurationRangeError(\"Duration arithmetic must produce a finite value\");",
|
|
168
|
+
" return __velarDurationString(__velarDurationObjectIs(milliseconds, -0) ? 0 : milliseconds) + \"ms\";",
|
|
169
|
+
"}",
|
|
170
|
+
"function __velarDurationUnary(operator, value) { const milliseconds = __velarDurationMilliseconds(value); return __velarDurationValue(operator === \"-\" ? -milliseconds : milliseconds); }",
|
|
171
|
+
"function __velarDurationMath(operator, left, right) {",
|
|
172
|
+
" if (operator === \"+\" || operator === \"-\") { const a = __velarDurationMilliseconds(left), b = __velarDurationMilliseconds(right); return __velarDurationValue(operator === \"+\" ? a + b : a - b); }",
|
|
173
|
+
" if (operator === \"*\" && typeof left === \"number\") return __velarDurationValue(left * __velarDurationMilliseconds(right));",
|
|
174
|
+
" const milliseconds = __velarDurationMilliseconds(left);",
|
|
175
|
+
" if (typeof right !== \"number\" || !__velarDurationNumberIsFinite(right)) throw new __velarDurationTypeError(\"Duration scaling requires a finite number\");",
|
|
176
|
+
" if (operator === \"/\" && right === 0) throw new __velarDurationRangeError(\"Duration arithmetic cannot divide by zero\");",
|
|
177
|
+
" return __velarDurationValue(operator === \"*\" ? milliseconds * right : milliseconds / right);",
|
|
178
|
+
"}",
|
|
179
|
+
].join("\n"));
|
|
180
|
+
helpers.push(...this.reactiveBridgeHelpers(this.hints.javaScriptCallBoundaries.size > 0, this.sharedRuntimeModules ? needsDirectCollectionInfrastructure : this.needsCollectionHelpers));
|
|
181
|
+
// Runtime imports are selected from JavaScript identifier tokens, not raw
|
|
182
|
+
// substrings. Raw text produces two wrong answers: a user string such as
|
|
183
|
+
// "__velarStringTrim" looks like a call, and a real
|
|
184
|
+
// __velarStringReplaceAll call also looks like __velarStringReplace.
|
|
185
|
+
// Generated helper names are reserved, so an identifier token is an exact
|
|
186
|
+
// compiler-owned use once string/comment/template text has been skipped.
|
|
187
|
+
const generatedIdentifiers = javaScriptIdentifiers([
|
|
188
|
+
...statements.map((statement) => statement.code),
|
|
189
|
+
...helpers,
|
|
190
|
+
]);
|
|
191
|
+
const usesGeneratedName = (name) => generatedIdentifiers.has(name);
|
|
192
|
+
if (this.needsExternExportHelper) {
|
|
193
|
+
// W-22: an extern module declaration is trusted for shapes, but the
|
|
194
|
+
// declared export must actually exist. The bridge verifies presence at
|
|
195
|
+
// module initialization with one property probe per imported name; a
|
|
196
|
+
// declared export that legitimately holds undefined stays importable
|
|
197
|
+
// because the boundary is membership in the module namespace, not the
|
|
198
|
+
// value.
|
|
199
|
+
helpers.push([
|
|
200
|
+
"function __velarExternExport(namespace, name, source) {",
|
|
201
|
+
" const value = namespace[name];",
|
|
202
|
+
" if (value === undefined && !(name in namespace)) {",
|
|
203
|
+
" throw new TypeError(name === \"default\"",
|
|
204
|
+
" ? `Extern module '${source}' declares 'default', but the JavaScript module has no default export; declare the module's real named exports instead`",
|
|
205
|
+
" : `Extern module '${source}' declares '${name}', but the JavaScript module has no such export; prototype methods and instance members belong on a declared class or singleton const, not module exports`);",
|
|
206
|
+
" }",
|
|
207
|
+
" return value;",
|
|
208
|
+
"}",
|
|
209
|
+
].join("\n"));
|
|
210
|
+
}
|
|
211
|
+
if (this.needsPrimitiveHelpers) {
|
|
212
|
+
if (this.sharedRuntimeModules) {
|
|
213
|
+
this.requiredRuntimeModules.add(VELAR_PRIMITIVE_METHOD_MODULE);
|
|
214
|
+
const imports = [
|
|
215
|
+
["stringSize", "__velarStringSize"], ["stringTrim", "__velarStringTrim"], ["stringUpper", "__velarStringUpper"], ["stringLower", "__velarStringLower"],
|
|
216
|
+
["stringSlice", "__velarStringSlice"], ["stringChar", "__velarStringChar"], ["stringHas", "__velarStringHas"], ["stringIndex", "__velarStringIndex"],
|
|
217
|
+
["stringCount", "__velarStringCount"], ["stringStartsWith", "__velarStringStartsWith"], ["stringEndsWith", "__velarStringEndsWith"],
|
|
218
|
+
["stringSplit", "__velarStringSplit"], ["stringReplace", "__velarStringReplace"], ["stringReplaceAll", "__velarStringReplaceAll"],
|
|
219
|
+
["stringPadStart", "__velarStringPadStart"], ["stringPadEnd", "__velarStringPadEnd"], ["stringRepeat", "__velarStringRepeat"],
|
|
220
|
+
["stringIsBlank", "__velarStringIsBlank"], ["stringCompare", "__velarStringCompare"], ["orderCompare", "__velarOrderCompare"],
|
|
221
|
+
["numberAbs", "__velarNumberAbs"], ["numberRound", "__velarNumberRound"], ["numberFloor", "__velarNumberFloor"],
|
|
222
|
+
["numberCeil", "__velarNumberCeil"], ["numberToFixed", "__velarNumberToFixed"],
|
|
223
|
+
["numberIsInteger", "__velarNumberIsInteger"], ["numberIsNaN", "__velarNumberIsNaN"], ["numberIsFinite", "__velarNumberIsFinite"],
|
|
224
|
+
].filter(([, local]) => usesGeneratedName(local));
|
|
225
|
+
helpers.push(`import { ${imports.map(([exported, local]) => `${exported} as ${local}`).join(", ")} } from ${JSON.stringify(VELAR_PRIMITIVE_METHOD_MODULE)};`);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
helpers.push(VELAR_TEXT_METHOD_RUNTIME);
|
|
229
|
+
helpers.push(VELAR_NUMBER_METHOD_RUNTIME);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (this.hints.instanceFieldReads.size > 0 || this.hints.privateInstanceFieldReads.size > 0 || this.hints.staticFieldReads.size > 0) {
|
|
233
|
+
if (this.sharedRuntimeModules) {
|
|
234
|
+
this.requiredRuntimeModules.add(VELAR_CLASS_FIELD_MODULE);
|
|
235
|
+
const imports = [
|
|
236
|
+
["readInstanceField", "__velarReadInstanceField"],
|
|
237
|
+
["readPrivateField", "__velarReadPrivateField"],
|
|
238
|
+
["readStaticField", "__velarReadStaticField"],
|
|
239
|
+
].filter(([, local]) => usesGeneratedName(local));
|
|
240
|
+
helpers.push(`import { ${imports.map(([exported, local]) => `${exported} as ${local}`).join(", ")} } from ${JSON.stringify(VELAR_CLASS_FIELD_MODULE)};`);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
helpers.push(VELAR_CLASS_FIELD_RUNTIME);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (this.hints.runtimeNarrowings.size > 0 || this.needsNarrowingErrorClass) {
|
|
247
|
+
if (this.sharedRuntimeModules) {
|
|
248
|
+
this.requireRuntimeModule(VELAR_NARROWING_MODULE);
|
|
249
|
+
const imports = [
|
|
250
|
+
...(this.hints.runtimeNarrowings.size > 0 ? ["narrow as __velarNarrow"] : []),
|
|
251
|
+
...(this.needsNarrowingErrorClass ? ["NarrowingError as __VelarNarrowingError"] : []),
|
|
252
|
+
];
|
|
253
|
+
helpers.push(`import { ${imports.join(", ")} } from ${JSON.stringify(VELAR_NARROWING_MODULE)};`);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
helpers.push(VELAR_NARROWING_RUNTIME);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const needsPromiseNormalization = this.hints.normalizedPromiseValues.size > 0
|
|
260
|
+
|| this.hints.asyncResolvedValues.size > 0
|
|
261
|
+
|| this.hints.asyncForStatements.size > 0;
|
|
262
|
+
if (needsPromiseNormalization) {
|
|
263
|
+
if (this.sharedRuntimeModules) {
|
|
264
|
+
this.requireRuntimeModule(VELAR_PROMISE_NORMALIZATION_MODULE);
|
|
265
|
+
const imports = [
|
|
266
|
+
...(this.hints.normalizedPromiseValues.size > 0 || this.hints.asyncForStatements.size > 0
|
|
267
|
+
? ["normalizePromiseValue as __velarNormalizePromiseValue"]
|
|
268
|
+
: []),
|
|
269
|
+
...(this.hints.asyncResolvedValues.size > 0
|
|
270
|
+
? ["asyncResolvedValue as __velarAsyncResolvedValue"]
|
|
271
|
+
: []),
|
|
272
|
+
];
|
|
273
|
+
helpers.push(`import { ${imports.join(", ")} } from ${JSON.stringify(VELAR_PROMISE_NORMALIZATION_MODULE)};`);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
helpers.push(VELAR_PROMISE_NORMALIZATION_RUNTIME);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (this.hints.asyncForStatements.size > 0) {
|
|
280
|
+
helpers.push([
|
|
281
|
+
"const __velarAsyncPullGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;",
|
|
282
|
+
"const __velarAsyncPullGetPrototypeOf = Object.getPrototypeOf;",
|
|
283
|
+
"const __velarAsyncPullApply = Reflect.apply;",
|
|
284
|
+
"const __velarAsyncPullArguments = [];",
|
|
285
|
+
"const __velarAsyncPullTypeError = TypeError;",
|
|
286
|
+
// Class methods live on the prototype (charter section 18), so the
|
|
287
|
+
// capture walks the chain with descriptor reads. The first level that
|
|
288
|
+
// declares 'next' decides: only a data-valued function is accepted,
|
|
289
|
+
// and an accessor is rejected without ever being invoked.
|
|
290
|
+
"function __velarAsyncPullNext(source) {",
|
|
291
|
+
" if ((typeof source !== \"object\" && typeof source !== \"function\") || source === null) {",
|
|
292
|
+
" throw new __velarAsyncPullTypeError(\"async for requires a data-valued next method\");",
|
|
293
|
+
" }",
|
|
294
|
+
" for (let owner = source; owner !== null; owner = __velarAsyncPullGetPrototypeOf(owner)) {",
|
|
295
|
+
" const descriptor = __velarAsyncPullGetOwnPropertyDescriptor(owner, \"next\");",
|
|
296
|
+
" if (!descriptor) continue;",
|
|
297
|
+
" if (!(\"value\" in descriptor) || typeof descriptor.value !== \"function\") {",
|
|
298
|
+
" throw new __velarAsyncPullTypeError(\"async for requires a data-valued next method\");",
|
|
299
|
+
" }",
|
|
300
|
+
" return descriptor.value;",
|
|
301
|
+
" }",
|
|
302
|
+
" throw new __velarAsyncPullTypeError(\"async for requires a data-valued next method\");",
|
|
303
|
+
"}",
|
|
304
|
+
"function __velarAsyncPullCall(source, next) {",
|
|
305
|
+
" return __velarAsyncPullApply(next, source, __velarAsyncPullArguments);",
|
|
306
|
+
"}",
|
|
307
|
+
].join("\n"));
|
|
308
|
+
}
|
|
309
|
+
if (this.needsDetachedTaskHelper) {
|
|
310
|
+
helpers.push(...this.detachedTaskHelpers());
|
|
311
|
+
}
|
|
312
|
+
if (this.needsDisposalHelper) {
|
|
313
|
+
helpers.push(...this.disposalHelpers());
|
|
314
|
+
}
|
|
315
|
+
if (this.needsIntegrityFailureHelper) {
|
|
316
|
+
helpers.push(...this.integrityFailureHelpers());
|
|
317
|
+
}
|
|
318
|
+
const needsErrorNormalizationRuntime = this.needsThrownValueHelper || this.needsErrorCodeHelper;
|
|
319
|
+
if (needsErrorNormalizationRuntime && !this.includesErrorNormalizationRuntime()) {
|
|
320
|
+
if (this.sharedRuntimeModules) {
|
|
321
|
+
this.requireRuntimeModule(VELAR_ERROR_NORMALIZATION_MODULE);
|
|
322
|
+
const imports = [
|
|
323
|
+
...(this.needsErrorCodeHelper ? ["errorCode as __velarErrorCode"] : []),
|
|
324
|
+
...(this.needsThrownValueHelper ? ["normalizeError as __velarNormalizeError"] : []),
|
|
325
|
+
];
|
|
326
|
+
helpers.push(`import { ${imports.join(", ")} } from ${JSON.stringify(VELAR_ERROR_NORMALIZATION_MODULE)};`);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
helpers.push(VELAR_ERROR_NORMALIZATION_RUNTIME);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// D50 rule 89: a named capability error is a leaf class the compiler owns,
|
|
333
|
+
// so a source reference resolves to the one runtime class every capability
|
|
334
|
+
// constructs — the same wiring the three compiler-raised errors use.
|
|
335
|
+
if (this.requiredHostErrorClasses.size > 0) {
|
|
336
|
+
const imports = [...this.requiredHostErrorClasses].sort().map((name) => `${name} as __Velar${name}`);
|
|
337
|
+
if (this.sharedRuntimeModules) {
|
|
338
|
+
this.requireRuntimeModule(VELAR_ERROR_NORMALIZATION_MODULE);
|
|
339
|
+
helpers.push(`import { ${imports.join(", ")} } from ${JSON.stringify(VELAR_ERROR_NORMALIZATION_MODULE)};`);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
helpers.push(VELAR_HOST_ERROR_RUNTIME);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const needsRuntimeTypeRuntime = this.needsRuntimeTypeHelpers || this.runtimeTypes.size > 0
|
|
346
|
+
|| program.body.some((statement) => statement.kind === "EnumDeclaration");
|
|
347
|
+
if (needsDirectCollectionInfrastructure && this.sharedRuntimeModules) {
|
|
348
|
+
this.requireRuntimeModule(VELAR_COLLECTION_HOST_MODULE);
|
|
349
|
+
helpers.push([
|
|
350
|
+
"import {",
|
|
351
|
+
...VELAR_COLLECTION_HOST_EXPORTS.map((name) => ` ${name},`),
|
|
352
|
+
`} from ${JSON.stringify(VELAR_COLLECTION_HOST_MODULE)};`,
|
|
353
|
+
].join("\n"));
|
|
354
|
+
}
|
|
355
|
+
else if (!this.sharedRuntimeModules && (this.needsCollectionHelpers || needsRuntimeTypeRuntime)) {
|
|
356
|
+
helpers.push(VELAR_COLLECTION_IDENTITY_RUNTIME);
|
|
357
|
+
}
|
|
358
|
+
if (needsRuntimeTypeRuntime) {
|
|
359
|
+
if (this.sharedRuntimeModules) {
|
|
360
|
+
this.requireRuntimeModule(VELAR_TYPE_VALIDATION_MODULE);
|
|
361
|
+
const imports = [
|
|
362
|
+
["registerRuntimeType", "__velarRegisterRuntimeType"], ["ValidationError", "__VelarValidationError"],
|
|
363
|
+
["validationState", "__velarValidationState"], ["validationSet", "__velarValidationSet"],
|
|
364
|
+
["validationWeakMapGet", "__velarValidationWeakMapGet"], ["validationWeakMapSet", "__velarValidationWeakMapSet"], ["validationWeakMapDelete", "__velarValidationWeakMapDelete"],
|
|
365
|
+
["validationSetHas", "__velarValidationSetHas"], ["validationSetAdd", "__velarValidationSetAdd"], ["validationSetDelete", "__velarValidationSetDelete"], ["validationSetSize", "__velarValidationSetSize"],
|
|
366
|
+
["validationIsArray", "__velarValidationIsArray"], ["validationOwnDescriptor", "__velarValidationOwnDescriptor"],
|
|
367
|
+
["validationIsInstance", "__velarValidationIsInstance"], ["validationIsPromise", "__velarValidationIsPromise"],
|
|
368
|
+
["validationIsPlainObject", "__velarValidationIsPlainObject"], ["validationRejectionHint", "__velarValidationRejectionHint"],
|
|
369
|
+
["validationFreeze", "__velarValidationFreeze"],
|
|
370
|
+
["listTypeIs", "__velarListTypeIs"], ["setTypeIs", "__velarSetTypeIs"], ["mapTypeIs", "__velarMapTypeIs"], ["recordTypeIs", "__velarRecordTypeIs"],
|
|
371
|
+
].filter(([, local]) => usesGeneratedName(local));
|
|
372
|
+
helpers.push(`import { ${imports.map(([exported, local]) => `${exported} as ${local}`).join(", ")} } from ${JSON.stringify(VELAR_TYPE_VALIDATION_MODULE)};`);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
helpers.push(VELAR_COLLECTION_TYPE_RUNTIME);
|
|
376
|
+
helpers.push(VELAR_TYPE_REGISTRY_RUNTIME);
|
|
377
|
+
helpers.push(VELAR_TYPE_VALIDATION_RUNTIME);
|
|
378
|
+
helpers.push(VELAR_RUNTIME_TYPE_COLLECTION_RUNTIME);
|
|
379
|
+
helpers.push(VELAR_VALIDATION_ERROR_RUNTIME);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (this.needsCollectionHelpers) {
|
|
383
|
+
if (this.sharedRuntimeModules) {
|
|
384
|
+
this.requireRuntimeModule(VELAR_COLLECTION_LOWERING_MODULE);
|
|
385
|
+
const imports = VELAR_COLLECTION_LOWERING_EXPORTS.filter((name) => usesGeneratedName(name)
|
|
386
|
+
|| (this.needsListBindingHelpers && name === "__velarValidateDenseList"));
|
|
387
|
+
helpers.push([
|
|
388
|
+
"import {",
|
|
389
|
+
...imports.map((name) => ` ${name},`),
|
|
390
|
+
`} from ${JSON.stringify(VELAR_COLLECTION_LOWERING_MODULE)};`,
|
|
391
|
+
].join("\n"));
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
helpers.push(VELAR_COLLECTION_LIST_RUNTIME);
|
|
395
|
+
helpers.push(VELAR_COLLECTION_SET_MAP_RUNTIME);
|
|
396
|
+
helpers.push(VELAR_COLLECTION_RECORD_RUNTIME);
|
|
397
|
+
helpers.push(VELAR_COLLECTION_LOWERING_RUNTIME);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (this.needsRecordHelpers) {
|
|
401
|
+
helpers.push([
|
|
402
|
+
"const __velarMaxRecordFields = 1000000;",
|
|
403
|
+
"",
|
|
404
|
+
"function __velarSetRecordField(output, field, value, count) {",
|
|
405
|
+
" const present = __velarCollectionRecordGetOwnPropertyDescriptor(output, field) !== undefined;",
|
|
406
|
+
" if (!present && count >= __velarMaxRecordFields) throw new __velarCollectionRecordNativeRangeError(\"A record cannot exceed 1000000 fields\");",
|
|
407
|
+
" __velarCollectionRecordDefineProperty(output, field, { value: value ?? null, writable: true, enumerable: true, configurable: true });",
|
|
408
|
+
" return present ? count : count + 1;",
|
|
409
|
+
"}",
|
|
410
|
+
"function __velarSpreadRecord(output, value, count) {",
|
|
411
|
+
" if (value === null || typeof value !== \"object\" || __velarCollectionListIsArray(value)) throw new __velarCollectionNativeTypeError(\"Object spread requires a record object\");",
|
|
412
|
+
" if (__velarCollectionRecordOwnSymbols(value).length > 0) throw new __velarCollectionNativeTypeError(\"Object spread cannot copy symbol fields\");",
|
|
413
|
+
" __velarReactiveCollectionTrack(value);",
|
|
414
|
+
" const fields = __velarCollectionRecordOwnNames(value);",
|
|
415
|
+
" if (fields.length > __velarMaxRecordFields) throw new __velarCollectionRecordNativeRangeError(\"Object spread cannot inspect more than 1000000 fields\");",
|
|
416
|
+
" for (let index = 0; index < fields.length; index += 1) {",
|
|
417
|
+
" const field = fields[index];",
|
|
418
|
+
" const descriptor = __velarCollectionRecordGetOwnPropertyDescriptor(value, field);",
|
|
419
|
+
" if (!descriptor) throw new __velarCollectionNativeTypeError(\"Object spread source changed while it was being copied\");",
|
|
420
|
+
" if (!descriptor.enumerable) continue;",
|
|
421
|
+
" if (!(\"value\" in descriptor)) throw new __velarCollectionNativeTypeError(\"Object spread cannot copy accessor field '\" + field + \"'\");",
|
|
422
|
+
" count = __velarSetRecordField(output, field, __velarReactiveCollectionRead(value, field, descriptor.value), count);",
|
|
423
|
+
" }",
|
|
424
|
+
" return count;",
|
|
425
|
+
"}",
|
|
426
|
+
"function __velarCreateRecord(parts) {",
|
|
427
|
+
" const output = {};",
|
|
428
|
+
" let count = 0;",
|
|
429
|
+
" for (let index = 0; index < parts.length; index += 1) {",
|
|
430
|
+
" const spread = parts[index][0];",
|
|
431
|
+
" const field = parts[index][1];",
|
|
432
|
+
" const read = parts[index][2];",
|
|
433
|
+
" if (spread) count = __velarSpreadRecord(output, read(), count);",
|
|
434
|
+
" else {",
|
|
435
|
+
" count = __velarSetRecordField(output, field, read(), count);",
|
|
436
|
+
" }",
|
|
437
|
+
" }",
|
|
438
|
+
" return output;",
|
|
439
|
+
"}",
|
|
440
|
+
"async function __velarCreateRecordAsync(parts) {",
|
|
441
|
+
" const output = {};",
|
|
442
|
+
" let count = 0;",
|
|
443
|
+
" for (let index = 0; index < parts.length; index += 1) {",
|
|
444
|
+
" const spread = parts[index][0];",
|
|
445
|
+
" const field = parts[index][1];",
|
|
446
|
+
" const asynchronous = parts[index][2];",
|
|
447
|
+
" const read = parts[index][3];",
|
|
448
|
+
" if (spread) count = __velarSpreadRecord(output, asynchronous ? await read() : read(), count);",
|
|
449
|
+
" else {",
|
|
450
|
+
" count = __velarSetRecordField(output, field, asynchronous ? await read() : read(), count);",
|
|
451
|
+
" }",
|
|
452
|
+
" }",
|
|
453
|
+
" return output;",
|
|
454
|
+
"}",
|
|
455
|
+
].join("\n"));
|
|
456
|
+
}
|
|
457
|
+
if (this.needsObjectBindingHelpers) {
|
|
458
|
+
helpers.push([
|
|
459
|
+
"const __velarMaxBindingFields = 1000000;",
|
|
460
|
+
"",
|
|
461
|
+
"function __velarRequireBindingObject(value, name) {",
|
|
462
|
+
" if (value === null || typeof value !== \"object\" || __velarCollectionListIsArray(value)) throw new __velarCollectionNativeTypeError(name + \" object binding requires a record object\");",
|
|
463
|
+
" return value;",
|
|
464
|
+
"}",
|
|
465
|
+
"function __velarReadBindingField(value, field, optional, name) {",
|
|
466
|
+
" __velarReactiveCollectionTrack(value, field);",
|
|
467
|
+
" const descriptor = __velarCollectionRecordGetOwnPropertyDescriptor(value, field);",
|
|
468
|
+
" if (descriptor === undefined) {",
|
|
469
|
+
" if (optional) return null;",
|
|
470
|
+
" throw new __velarCollectionNativeTypeError(name + \" object binding requires own data field '\" + field + \"'\");",
|
|
471
|
+
" }",
|
|
472
|
+
" if (!descriptor.enumerable || !(\"value\" in descriptor)) throw new __velarCollectionNativeTypeError(name + \" object binding requires enumerable data field '\" + field + \"'\");",
|
|
473
|
+
" return __velarReactiveCollectionRead(value, field, descriptor.value) ?? null;",
|
|
474
|
+
"}",
|
|
475
|
+
"function __velarBindingObjectRest(value, excluded, name) {",
|
|
476
|
+
" if (__velarCollectionRecordOwnSymbols(value).length > 0) throw new __velarCollectionNativeTypeError(name + \" object rest cannot copy symbol fields\");",
|
|
477
|
+
" __velarReactiveCollectionTrack(value);",
|
|
478
|
+
" const fields = __velarCollectionRecordOwnNames(value);",
|
|
479
|
+
" if (fields.length > __velarMaxBindingFields) throw new __velarCollectionRecordNativeRangeError(name + \" object rest cannot copy more than 1000000 fields\");",
|
|
480
|
+
" const output = {};",
|
|
481
|
+
" for (let fieldIndex = 0; fieldIndex < fields.length; fieldIndex += 1) {",
|
|
482
|
+
" const field = fields[fieldIndex];",
|
|
483
|
+
" let selected = false;",
|
|
484
|
+
" for (let index = 0; index < excluded.length; index += 1) if (excluded[index] === field) { selected = true; break; }",
|
|
485
|
+
" if (selected) continue;",
|
|
486
|
+
" const descriptor = __velarCollectionRecordGetOwnPropertyDescriptor(value, field);",
|
|
487
|
+
" if (!descriptor?.enumerable) continue;",
|
|
488
|
+
" if (!(\"value\" in descriptor)) throw new __velarCollectionNativeTypeError(name + \" object rest cannot copy accessor field '\" + field + \"'\");",
|
|
489
|
+
" __velarCollectionRecordDefineProperty(output, field, { value: __velarReactiveCollectionRead(value, field, descriptor.value) ?? null, writable: true, enumerable: true, configurable: true });",
|
|
490
|
+
" }",
|
|
491
|
+
" return output;",
|
|
492
|
+
"}",
|
|
493
|
+
].join("\n"));
|
|
494
|
+
}
|
|
495
|
+
if (this.needsListBindingHelpers) {
|
|
496
|
+
helpers.push([
|
|
497
|
+
"function __velarRequireBindingList(value, size, hasRest, name) {",
|
|
498
|
+
" __velarValidateDenseList(value, name + \" List binding\");",
|
|
499
|
+
" __velarReactiveCollectionTrack(value);",
|
|
500
|
+
" const valid = hasRest ? value.length >= size : value.length === size;",
|
|
501
|
+
" if (!valid) throw new __velarCollectionListNativeRangeError(name + \" List binding\" + (hasRest ? \" requires at least \" : \" requires exactly \") + size + (size === 1 ? \" item\" : \" items\") + \", received \" + value.length);",
|
|
502
|
+
" return value;",
|
|
503
|
+
"}",
|
|
504
|
+
"function __velarReadBindingListItem(value, index) {",
|
|
505
|
+
" return __velarReactiveCollectionRead(value, index, __velarCollectionListGetOwnPropertyDescriptor(value, index).value) ?? null;",
|
|
506
|
+
"}",
|
|
507
|
+
"function __velarBindingListRest(value, start) {",
|
|
508
|
+
" const output = new __velarCollectionNativeArray(value.length - start);",
|
|
509
|
+
" for (let index = start; index < value.length; index += 1) output[index - start] = __velarReactiveCollectionRead(value, index, __velarCollectionListGetOwnPropertyDescriptor(value, index).value) ?? null;",
|
|
510
|
+
" return output;",
|
|
511
|
+
"}",
|
|
512
|
+
].join("\n"));
|
|
513
|
+
}
|
|
514
|
+
if (this.needsNumberHelper) {
|
|
515
|
+
helpers.push([
|
|
516
|
+
"function __velarNumber(value) {",
|
|
517
|
+
" if (typeof value !== \"string\") throw new TypeError(\"number(text) requires a string\");",
|
|
518
|
+
" const text = value.trim();",
|
|
519
|
+
" if (!/^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$/u.test(text)) return null;",
|
|
520
|
+
" const parsed = Number(text);",
|
|
521
|
+
" return Number.isFinite(parsed) ? parsed : null;",
|
|
522
|
+
"}",
|
|
523
|
+
].join("\n"));
|
|
524
|
+
}
|
|
525
|
+
// D55 rule 121: one memoized accessor per instantiation written outside a
|
|
526
|
+
// generic body. They are `function` declarations so that
|
|
527
|
+
// `type Boxed = Box<string>` above the declaration of `Box` still reads a
|
|
528
|
+
// hoisted name instead of a `const` in its temporal dead zone.
|
|
529
|
+
const instances = [...this.hoistedGenericInstances].map(([expression, name]) => `function ${name}() {\n return ${expression};\n}`);
|
|
530
|
+
const chunks = [
|
|
531
|
+
...helpers.map((code) => ({ code, mappings: [] })),
|
|
532
|
+
...instances.map((code) => ({ code, mappings: [] })),
|
|
533
|
+
...statements.map((node) => this.renderJavaScriptNode(node)),
|
|
534
|
+
];
|
|
535
|
+
let output = "";
|
|
536
|
+
const mappings = [];
|
|
537
|
+
for (const chunk of chunks) {
|
|
538
|
+
if (output.length > 0)
|
|
539
|
+
output += "\n\n";
|
|
540
|
+
const offset = output.length;
|
|
541
|
+
output += chunk.code;
|
|
542
|
+
mappings.push(...chunk.mappings.map((mapping) => ({ ...mapping, offset: offset + mapping.offset })));
|
|
543
|
+
}
|
|
544
|
+
this.generatedCode = `${output}${output.length > 0 ? "\n" : ""}`;
|
|
545
|
+
this.generatedMappings = mappings.sort((left, right) => left.offset - right.offset);
|
|
546
|
+
return this.generatedCode;
|
|
547
|
+
}
|
|
548
|
+
sourceMap(source) {
|
|
549
|
+
return sourceMapFor(this.generatedCode, this.generatedMappings, source);
|
|
550
|
+
}
|
|
551
|
+
embeddedModules(source) {
|
|
552
|
+
return [...this.embeddedJavaScript.values()].map((module) => ({
|
|
553
|
+
specifier: module.specifier,
|
|
554
|
+
code: module.code,
|
|
555
|
+
sourceMap: sourceMapFor(module.code, module.mappings, source),
|
|
556
|
+
sourceSpan: module.statement.sourceSpan,
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
runtimeModules() {
|
|
560
|
+
return [...this.requiredRuntimeModules].sort();
|
|
561
|
+
}
|
|
562
|
+
emitJavaScriptNode(sourceSpan, render) {
|
|
563
|
+
const node = { id: this.nextJavaScriptNodeId++, code: render(), sourceSpan };
|
|
564
|
+
this.javaScriptNodeSpans.set(node.id, sourceSpan);
|
|
565
|
+
return node;
|
|
566
|
+
}
|
|
567
|
+
markJavaScriptNode(node) {
|
|
568
|
+
return node.code.length === 0 ? "" : `\u0000VELAR_MAP_${node.id}\u0000${node.code}`;
|
|
569
|
+
}
|
|
570
|
+
emitMappedJavaScript(sourceSpan, render) {
|
|
571
|
+
return this.markJavaScriptNode(this.emitJavaScriptNode(sourceSpan, render));
|
|
572
|
+
}
|
|
573
|
+
renderJavaScriptNode(node) {
|
|
574
|
+
let code = "";
|
|
575
|
+
let cursor = 0;
|
|
576
|
+
const mappings = [{ offset: 0, sourceSpan: node.sourceSpan }];
|
|
577
|
+
for (const marker of node.code.matchAll(javaScriptNodeMarker)) {
|
|
578
|
+
const markerIndex = marker.index;
|
|
579
|
+
code += node.code.slice(cursor, markerIndex);
|
|
580
|
+
const sourceSpan = this.javaScriptNodeSpans.get(Number(marker[1]));
|
|
581
|
+
if (!sourceSpan)
|
|
582
|
+
throw new Error("A generated JavaScript mapping marker has no source node");
|
|
583
|
+
mappings.push({ offset: code.length, sourceSpan });
|
|
584
|
+
cursor = markerIndex + marker[0].length;
|
|
585
|
+
}
|
|
586
|
+
code += node.code.slice(cursor);
|
|
587
|
+
return { code, mappings };
|
|
588
|
+
}
|
|
589
|
+
additionalHelpers(_program) {
|
|
590
|
+
return [];
|
|
591
|
+
}
|
|
592
|
+
reactiveBridgeHelpers(needsJavaScriptCallBoundary, needsCollections) {
|
|
593
|
+
if (!needsJavaScriptCallBoundary && !needsCollections)
|
|
594
|
+
return [];
|
|
595
|
+
if (this.sharedRuntimeModules) {
|
|
596
|
+
this.requiredRuntimeModules.add(VELAR_REACTIVE_BRIDGE_MODULE);
|
|
597
|
+
const imports = [
|
|
598
|
+
"reactiveRaw as __velarReactiveRaw",
|
|
599
|
+
"hostRaw as __velarHostRaw",
|
|
600
|
+
...(needsCollections ? [
|
|
601
|
+
"reactiveIterateKey as __velarReactiveIterateKey",
|
|
602
|
+
"reactiveStructureKey as __velarReactiveStructureKey",
|
|
603
|
+
"reactiveCollectionRead as __velarReactiveCollectionRead",
|
|
604
|
+
"reactiveCollectionTrack as __velarReactiveCollectionTrack",
|
|
605
|
+
"reactiveCollectionLink as __velarReactiveCollectionLink",
|
|
606
|
+
"reactiveCollectionTrigger as __velarReactiveCollectionTrigger",
|
|
607
|
+
"reactiveCollectionUnlink as __velarReactiveCollectionUnlink",
|
|
608
|
+
] : []),
|
|
609
|
+
];
|
|
610
|
+
return [`import { ${imports.join(", ")} } from ${JSON.stringify(VELAR_REACTIVE_BRIDGE_MODULE)};`];
|
|
611
|
+
}
|
|
612
|
+
return [VELAR_REACTIVE_BRIDGE_RUNTIME, ...(needsCollections ? [VELAR_REACTIVE_COLLECTION_BRIDGE_RUNTIME] : [])];
|
|
613
|
+
}
|
|
614
|
+
usesSharedRuntimeModules() {
|
|
615
|
+
return this.sharedRuntimeModules;
|
|
616
|
+
}
|
|
617
|
+
// The compiler-owned observer behind the 'async <expression>' statement
|
|
618
|
+
// (docs/contributing/runtime-boundary.md, B-DETACHED-ASYNC). The Promise and Reflect
|
|
619
|
+
// operations and the console channel are captured at module initialization;
|
|
620
|
+
// rejection is normalized to Error and reported on the host error channel
|
|
621
|
+
// without ending the process. Hosts with their own error chain override
|
|
622
|
+
// this (the Web emitter routes the report through the velar/app chain).
|
|
623
|
+
//
|
|
624
|
+
// The reporter itself must never fail outward: a rejection value carries a
|
|
625
|
+
// foreign error object whose 'stack' or 'message' may be a throwing getter,
|
|
626
|
+
// and a throw inside a rejection handler becomes an unhandled rejection that
|
|
627
|
+
// ends the Node process — the exact program termination this boundary
|
|
628
|
+
// forbids. Every foreign read is therefore guarded, and the Promise derived
|
|
629
|
+
// from adopting the task is observed rather than discarded.
|
|
630
|
+
detachedTaskHelpers() {
|
|
631
|
+
return [[
|
|
632
|
+
"const __velarDetachedPromiseThen = globalThis.Promise.prototype.then;",
|
|
633
|
+
"const __velarDetachedApply = Reflect.apply;",
|
|
634
|
+
"const __velarDetachedConsole = globalThis.console;",
|
|
635
|
+
"const __velarDetachedConsoleError = __velarDetachedConsole ? __velarDetachedConsole.error : null;",
|
|
636
|
+
"function __velarDetachedTrace(error) {",
|
|
637
|
+
" try { const trace = error.stack; if (typeof trace === \"string\" && trace !== \"\") return trace; } catch {}",
|
|
638
|
+
" try { const message = error.message; if (typeof message === \"string\" && message !== \"\") return message; } catch {}",
|
|
639
|
+
" return \"A detached task failed\";",
|
|
640
|
+
"}",
|
|
641
|
+
"function __velarDetachedReport(failure) {",
|
|
642
|
+
" try {",
|
|
643
|
+
" if (typeof __velarDetachedConsoleError !== \"function\") return null;",
|
|
644
|
+
" let error = null;",
|
|
645
|
+
" try { error = __velarNormalizeError(failure); } catch {}",
|
|
646
|
+
" const trace = error === null ? \"A detached task failed\" : __velarDetachedTrace(error);",
|
|
647
|
+
" __velarDetachedApply(__velarDetachedConsoleError, __velarDetachedConsole, [\"Detached async task failed: \" + trace]);",
|
|
648
|
+
" } catch {}",
|
|
649
|
+
" return null;",
|
|
650
|
+
"}",
|
|
651
|
+
"function __velarDetachedTask(task) {",
|
|
652
|
+
" try {",
|
|
653
|
+
" const observed = __velarDetachedApply(__velarDetachedPromiseThen, task, [null, __velarDetachedReport]);",
|
|
654
|
+
" __velarDetachedApply(__velarDetachedPromiseThen, observed, [null, __velarDetachedReport]);",
|
|
655
|
+
" } catch (failure) {",
|
|
656
|
+
" __velarDetachedReport(failure);",
|
|
657
|
+
" }",
|
|
658
|
+
" return null;",
|
|
659
|
+
"}",
|
|
660
|
+
].join("\n")];
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* D43 item 69 rule 8: a release that fails while an error is already in
|
|
664
|
+
* flight must not replace it. The original error keeps the throw; the release
|
|
665
|
+
* failure is normalized and reported through the host channel. The reporter
|
|
666
|
+
* itself never fails outward, for the same reason the detached-task reporter
|
|
667
|
+
* does not — a throw inside it would end the process.
|
|
668
|
+
*/
|
|
669
|
+
disposalHelpers() {
|
|
670
|
+
return [[
|
|
671
|
+
"const __velarDisposalApply = Reflect.apply;",
|
|
672
|
+
"const __velarDisposalConsole = globalThis.console;",
|
|
673
|
+
"const __velarDisposalConsoleError = __velarDisposalConsole ? __velarDisposalConsole.error : null;",
|
|
674
|
+
"function __velarDisposalTrace(error) {",
|
|
675
|
+
" try { const trace = error.stack; if (typeof trace === \"string\" && trace !== \"\") return trace; } catch {}",
|
|
676
|
+
" try { const message = error.message; if (typeof message === \"string\" && message !== \"\") return message; } catch {}",
|
|
677
|
+
" return \"A resource release failed\";",
|
|
678
|
+
"}",
|
|
679
|
+
"function __velarDisposalReport(failure) {",
|
|
680
|
+
" try {",
|
|
681
|
+
" if (typeof __velarDisposalConsoleError !== \"function\") return null;",
|
|
682
|
+
" let error = null;",
|
|
683
|
+
" try { error = __velarNormalizeError(failure); } catch {}",
|
|
684
|
+
" const trace = error === null ? \"A resource release failed\" : __velarDisposalTrace(error);",
|
|
685
|
+
" __velarDisposalApply(__velarDisposalConsoleError, __velarDisposalConsole, [\"Resource release failed while another error was in flight: \" + trace]);",
|
|
686
|
+
" } catch {}",
|
|
687
|
+
" return null;",
|
|
688
|
+
"}",
|
|
689
|
+
].join("\n")];
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* D51 rule 103: the three failures that mean "this program has a bug", by the
|
|
693
|
+
* one name each of them stamps on itself. A forged name can only make a
|
|
694
|
+
* failure propagate instead of becoming `null`, which is the safe direction:
|
|
695
|
+
* `try` never hides a guard, and `catch` still receives everything.
|
|
696
|
+
*/
|
|
697
|
+
integrityFailureHelpers() {
|
|
698
|
+
return [[
|
|
699
|
+
"const __velarIntegrityDescriptor = Object.getOwnPropertyDescriptor;",
|
|
700
|
+
"function __velarIsIntegrityFailure(value) {",
|
|
701
|
+
" if (value === null || (typeof value !== \"object\" && typeof value !== \"function\")) return false;",
|
|
702
|
+
" const descriptor = __velarIntegrityDescriptor(value, \"name\");",
|
|
703
|
+
" if (!descriptor || !(\"value\" in descriptor)) return false;",
|
|
704
|
+
" const name = descriptor.value;",
|
|
705
|
+
" return name === \"AssertionError\" || name === \"NarrowingError\" || name === \"IndexError\";",
|
|
706
|
+
"}",
|
|
707
|
+
].join("\n")];
|
|
708
|
+
}
|
|
709
|
+
requireRuntimeModule(source) {
|
|
710
|
+
this.requiredRuntimeModules.add(source);
|
|
711
|
+
}
|
|
712
|
+
includesErrorNormalizationRuntime() {
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
visitExtensionRuntimeExpression(_expression, _visitExpression) {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
visitExtensionRuntimeStatement(_statement, _visitExpression, _visitStatement) {
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
extensionExpressionContainsDirectAwait(_expression, _contains) {
|
|
722
|
+
return undefined;
|
|
723
|
+
}
|
|
724
|
+
extensionStatementContainsDirectAwait(_statement, _containsExpression, _containsBlock) {
|
|
725
|
+
return undefined;
|
|
726
|
+
}
|
|
727
|
+
collectDeclarations(program) {
|
|
728
|
+
for (const statement of program.body) {
|
|
729
|
+
if (statement.kind === "TypeDeclaration" || statement.kind === "TypeAliasDeclaration") {
|
|
730
|
+
this.typeDeclarations.set(statement.name, statement);
|
|
731
|
+
if (statement.exported) {
|
|
732
|
+
this.runtimeTypes.add(statement.name);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
else if (statement.kind === "ExternModuleDeclaration") {
|
|
736
|
+
const names = new Set(this.externModuleExports.get(statement.source));
|
|
737
|
+
for (const declaration of statement.functions)
|
|
738
|
+
names.add(declaration.name);
|
|
739
|
+
for (const declaration of statement.constants)
|
|
740
|
+
names.add(declaration.name);
|
|
741
|
+
for (const declaration of statement.classes)
|
|
742
|
+
names.add(declaration.name);
|
|
743
|
+
this.externModuleExports.set(statement.source, names);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
for (const name of [...this.runtimeTypes]) {
|
|
747
|
+
this.markRuntimeType({ kind: "named", name });
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
collectRuntimeUses(program) {
|
|
751
|
+
for (const guard of this.hints.runtimeNarrowings.values())
|
|
752
|
+
this.markRuntimeNarrowingType(guard.expected);
|
|
753
|
+
const visitExpression = (expression) => {
|
|
754
|
+
if (this.visitExtensionRuntimeExpression(expression, visitExpression))
|
|
755
|
+
return;
|
|
756
|
+
switch (expression.kind) {
|
|
757
|
+
case "FStringExpression":
|
|
758
|
+
for (const part of expression.parts) {
|
|
759
|
+
if (part.kind === "expression")
|
|
760
|
+
visitExpression(part.value);
|
|
761
|
+
}
|
|
762
|
+
break;
|
|
763
|
+
case "ListExpression":
|
|
764
|
+
expression.elements.forEach(visitExpression);
|
|
765
|
+
break;
|
|
766
|
+
case "ObjectExpression":
|
|
767
|
+
expression.properties.forEach((property) => visitExpression(property.value));
|
|
768
|
+
break;
|
|
769
|
+
case "SpreadExpression":
|
|
770
|
+
visitExpression(expression.value);
|
|
771
|
+
break;
|
|
772
|
+
case "UnaryExpression":
|
|
773
|
+
visitExpression(expression.operand);
|
|
774
|
+
break;
|
|
775
|
+
case "TryExpression":
|
|
776
|
+
visitExpression(expression.value);
|
|
777
|
+
break;
|
|
778
|
+
case "BinaryExpression":
|
|
779
|
+
visitExpression(expression.left);
|
|
780
|
+
visitExpression(expression.right);
|
|
781
|
+
break;
|
|
782
|
+
case "ComparisonChainExpression":
|
|
783
|
+
expression.operands.forEach(visitExpression);
|
|
784
|
+
break;
|
|
785
|
+
case "ConditionalExpression":
|
|
786
|
+
visitExpression(expression.condition);
|
|
787
|
+
visitExpression(expression.thenValue);
|
|
788
|
+
visitExpression(expression.elseValue);
|
|
789
|
+
break;
|
|
790
|
+
case "IsExpression":
|
|
791
|
+
this.markRuntimeType(resolveTypeReference(expression.type));
|
|
792
|
+
visitExpression(expression.value);
|
|
793
|
+
break;
|
|
794
|
+
case "ArrowFunctionExpression":
|
|
795
|
+
visitExpression(expression.body);
|
|
796
|
+
break;
|
|
797
|
+
case "CallExpression":
|
|
798
|
+
if (expression.callee.kind === "MemberExpression"
|
|
799
|
+
&& expression.callee.object.kind === "IdentifierExpression"
|
|
800
|
+
&& expression.callee.property === "parse"
|
|
801
|
+
&& this.typeDeclarations.has(expression.callee.object.name)) {
|
|
802
|
+
this.markRuntimeType({ kind: "named", name: expression.callee.object.name });
|
|
803
|
+
}
|
|
804
|
+
if (expression.callee.kind === "MemberExpression"
|
|
805
|
+
&& this.collectionHelper(expression.callee)) {
|
|
806
|
+
this.needsCollectionHelpers = true;
|
|
807
|
+
}
|
|
808
|
+
if (expression.callee.kind === "MemberExpression"
|
|
809
|
+
&& this.binaryHelper(expression.callee)) {
|
|
810
|
+
this.needsBinaryHelpers = true;
|
|
811
|
+
}
|
|
812
|
+
visitExpression(expression.callee);
|
|
813
|
+
expression.arguments.forEach(visitExpression);
|
|
814
|
+
break;
|
|
815
|
+
case "MemberExpression":
|
|
816
|
+
if (this.binaryHelper(expression) || this.hints.binarySizes.has(expression.span.end)) {
|
|
817
|
+
this.needsBinaryHelpers = true;
|
|
818
|
+
}
|
|
819
|
+
visitExpression(expression.object);
|
|
820
|
+
break;
|
|
821
|
+
case "IndexExpression":
|
|
822
|
+
if (this.hints.binaryIndexes.has(spanIdentity(expression.span)))
|
|
823
|
+
this.needsBinaryHelpers = true;
|
|
824
|
+
else
|
|
825
|
+
this.needsIndexHelpers = true;
|
|
826
|
+
visitExpression(expression.object);
|
|
827
|
+
visitExpression(expression.index);
|
|
828
|
+
break;
|
|
829
|
+
case "LiteralExpression":
|
|
830
|
+
break;
|
|
831
|
+
case "IdentifierExpression":
|
|
832
|
+
if (this.typeDeclarations.has(expression.name)) {
|
|
833
|
+
this.markRuntimeType({ kind: "named", name: expression.name });
|
|
834
|
+
}
|
|
835
|
+
break;
|
|
836
|
+
case "SuperExpression":
|
|
837
|
+
case "DynamicImportExpression":
|
|
838
|
+
break;
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
const visitStatement = (statement) => {
|
|
842
|
+
if (this.visitExtensionRuntimeStatement(statement, visitExpression, visitStatement))
|
|
843
|
+
return;
|
|
844
|
+
switch (statement.kind) {
|
|
845
|
+
case "VariableDeclaration":
|
|
846
|
+
visitExpression(statement.initializer);
|
|
847
|
+
break;
|
|
848
|
+
case "TestDeclaration":
|
|
849
|
+
statement.body.forEach(visitStatement);
|
|
850
|
+
break;
|
|
851
|
+
case "UsingDeclaration":
|
|
852
|
+
// Releasing a resource reports its own failure through the host
|
|
853
|
+
// channel, which carries the error-normalization runtime with it.
|
|
854
|
+
this.needsDisposalHelper = true;
|
|
855
|
+
this.needsThrownValueHelper = true;
|
|
856
|
+
visitExpression(statement.initializer);
|
|
857
|
+
break;
|
|
858
|
+
case "FunctionDeclaration":
|
|
859
|
+
statement.parameters.forEach((parameter) => { if (parameter.defaultValue)
|
|
860
|
+
visitExpression(parameter.defaultValue); });
|
|
861
|
+
statement.body.forEach(visitStatement);
|
|
862
|
+
break;
|
|
863
|
+
case "ClassDeclaration":
|
|
864
|
+
statement.parameters.forEach((parameter) => { if (parameter.defaultValue)
|
|
865
|
+
visitExpression(parameter.defaultValue); });
|
|
866
|
+
statement.base?.arguments.forEach(visitExpression);
|
|
867
|
+
statement.fields.forEach((field) => { if (field.initializer)
|
|
868
|
+
visitExpression(field.initializer); });
|
|
869
|
+
statement.initialization?.body.forEach(visitStatement);
|
|
870
|
+
statement.getters.forEach(visitStatement);
|
|
871
|
+
statement.methods.forEach(visitStatement);
|
|
872
|
+
statement.dispose?.body.forEach(visitStatement);
|
|
873
|
+
statement.iterate?.body.forEach(visitStatement);
|
|
874
|
+
break;
|
|
875
|
+
case "ReturnStatement":
|
|
876
|
+
if (statement.value)
|
|
877
|
+
visitExpression(statement.value);
|
|
878
|
+
break;
|
|
879
|
+
case "ThrowStatement":
|
|
880
|
+
visitExpression(statement.value);
|
|
881
|
+
break;
|
|
882
|
+
case "AssertStatement":
|
|
883
|
+
visitExpression(statement.condition);
|
|
884
|
+
if (statement.message)
|
|
885
|
+
visitExpression(statement.message);
|
|
886
|
+
break;
|
|
887
|
+
case "IfStatement":
|
|
888
|
+
visitExpression(statement.condition);
|
|
889
|
+
statement.thenBody.forEach(visitStatement);
|
|
890
|
+
statement.elseBody?.forEach(visitStatement);
|
|
891
|
+
break;
|
|
892
|
+
case "MatchStatement":
|
|
893
|
+
visitExpression(statement.value);
|
|
894
|
+
statement.cases.forEach((branch) => {
|
|
895
|
+
visitMatchPattern(branch.pattern);
|
|
896
|
+
if (branch.guard)
|
|
897
|
+
visitExpression(branch.guard);
|
|
898
|
+
branch.body.forEach(visitStatement);
|
|
899
|
+
});
|
|
900
|
+
break;
|
|
901
|
+
case "ForStatement":
|
|
902
|
+
visitExpression(statement.iterable);
|
|
903
|
+
statement.body.forEach(visitStatement);
|
|
904
|
+
break;
|
|
905
|
+
case "WhileStatement":
|
|
906
|
+
visitExpression(statement.condition);
|
|
907
|
+
statement.body.forEach(visitStatement);
|
|
908
|
+
break;
|
|
909
|
+
case "TryStatement":
|
|
910
|
+
statement.tryBody.forEach(visitStatement);
|
|
911
|
+
statement.catchBody?.forEach(visitStatement);
|
|
912
|
+
statement.finallyBody?.forEach(visitStatement);
|
|
913
|
+
break;
|
|
914
|
+
case "AssignmentStatement":
|
|
915
|
+
visitExpression(statement.target);
|
|
916
|
+
visitExpression(statement.value);
|
|
917
|
+
break;
|
|
918
|
+
case "ExpressionStatement":
|
|
919
|
+
visitExpression(statement.expression);
|
|
920
|
+
break;
|
|
921
|
+
case "AsyncStatement":
|
|
922
|
+
// The detached-task observer normalizes rejection values, so the
|
|
923
|
+
// error-normalization runtime travels with it.
|
|
924
|
+
this.needsDetachedTaskHelper = true;
|
|
925
|
+
this.needsThrownValueHelper = true;
|
|
926
|
+
visitExpression(statement.expression);
|
|
927
|
+
break;
|
|
928
|
+
case "EmbeddedJavaScriptDeclaration":
|
|
929
|
+
statement.captures.forEach((capture) => visitExpression({
|
|
930
|
+
kind: "IdentifierExpression",
|
|
931
|
+
name: capture.name,
|
|
932
|
+
span: capture.nameSpan,
|
|
933
|
+
}));
|
|
934
|
+
break;
|
|
935
|
+
case "ImportDeclaration":
|
|
936
|
+
case "ReExportDeclaration":
|
|
937
|
+
case "ExternModuleDeclaration":
|
|
938
|
+
case "TypeDeclaration":
|
|
939
|
+
case "TypeAliasDeclaration":
|
|
940
|
+
case "EnumDeclaration":
|
|
941
|
+
case "BreakStatement":
|
|
942
|
+
case "ContinueStatement":
|
|
943
|
+
case "PassStatement":
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
const visitMatchPattern = (pattern) => {
|
|
948
|
+
switch (pattern.kind) {
|
|
949
|
+
case "MatchValuePattern":
|
|
950
|
+
pattern.values.forEach(visitExpression);
|
|
951
|
+
break;
|
|
952
|
+
case "MatchTypePattern":
|
|
953
|
+
this.markRuntimeType(resolveTypeReference(pattern.type));
|
|
954
|
+
break;
|
|
955
|
+
case "MatchAsPattern":
|
|
956
|
+
visitMatchPattern(pattern.pattern);
|
|
957
|
+
break;
|
|
958
|
+
case "MatchObjectPattern":
|
|
959
|
+
pattern.entries.forEach((entry) => visitMatchPattern(entry.pattern));
|
|
960
|
+
break;
|
|
961
|
+
case "MatchListPattern":
|
|
962
|
+
pattern.elements.forEach(visitMatchPattern);
|
|
963
|
+
break;
|
|
964
|
+
case "MatchWildcardPattern":
|
|
965
|
+
case "MatchCapturePattern":
|
|
966
|
+
break;
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
program.body.forEach(visitStatement);
|
|
970
|
+
}
|
|
971
|
+
markRuntimeType(type) {
|
|
972
|
+
this.needsRuntimeTypeHelpers = true;
|
|
973
|
+
const visit = (value) => {
|
|
974
|
+
// D55 rule 121: `Box<string>` needs `Box`'s factory emitted and every
|
|
975
|
+
// argument's own runtime types marked; the application's display name is
|
|
976
|
+
// not a declaration, so the walk asks the application which one it is.
|
|
977
|
+
if (value.kind === "named" && value.application) {
|
|
978
|
+
for (const argument of value.application.arguments)
|
|
979
|
+
visit(argument);
|
|
980
|
+
visit({ kind: "named", name: value.application.name });
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
if (value.kind === "named" && this.typeDeclarations.has(value.name) && !this.runtimeTypes.has(value.name)) {
|
|
984
|
+
this.runtimeTypes.add(value.name);
|
|
985
|
+
}
|
|
986
|
+
if (value.kind === "named" && this.typeDeclarations.has(value.name) && !this.expandedRuntimeTypes.has(value.name)) {
|
|
987
|
+
this.expandedRuntimeTypes.add(value.name);
|
|
988
|
+
const declaration = this.typeDeclarations.get(value.name);
|
|
989
|
+
if (declaration.kind === "TypeDeclaration") {
|
|
990
|
+
declaration.fields.forEach((field) => visit(resolveTypeReference(field.type)));
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
visit(resolveTypeReference(declaration.target));
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
else if (value.kind === "optional") {
|
|
997
|
+
visit(value.inner);
|
|
998
|
+
}
|
|
999
|
+
else if (value.kind === "list") {
|
|
1000
|
+
visit(value.element);
|
|
1001
|
+
}
|
|
1002
|
+
else if (value.kind === "set") {
|
|
1003
|
+
visit(value.element);
|
|
1004
|
+
}
|
|
1005
|
+
else if (value.kind === "map") {
|
|
1006
|
+
visit(value.key);
|
|
1007
|
+
visit(value.value);
|
|
1008
|
+
}
|
|
1009
|
+
else if (value.kind === "record") {
|
|
1010
|
+
visit(value.value);
|
|
1011
|
+
}
|
|
1012
|
+
else if (value.kind === "promise") {
|
|
1013
|
+
visit(value.value);
|
|
1014
|
+
}
|
|
1015
|
+
else if (value.kind === "union") {
|
|
1016
|
+
value.members.forEach(visit);
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
visit(type);
|
|
1020
|
+
}
|
|
1021
|
+
markRuntimeNarrowingType(type) {
|
|
1022
|
+
if (type.kind === "optional") {
|
|
1023
|
+
this.markRuntimeNarrowingType(type.inner);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
if (type.kind === "union") {
|
|
1027
|
+
for (const member of type.members)
|
|
1028
|
+
this.markRuntimeNarrowingType(member);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
if (type.kind === "list" || type.kind === "set" || type.kind === "map" || type.kind === "record"
|
|
1032
|
+
|| type.kind === "promise" || type.kind === "named" || type.kind === "class") {
|
|
1033
|
+
this.markRuntimeType(type);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
emitMappedStatement(statement, depth) {
|
|
1037
|
+
return this.emitMappedJavaScript(statement.span, () => this.emitStatement(statement, depth));
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* D43 item 69: a `using` binding owns the rest of its block, so a statement
|
|
1041
|
+
* list is emitted as a whole. Everything after the binding moves inside the
|
|
1042
|
+
* release frame; a second `using` nests inside the first, which is what makes
|
|
1043
|
+
* release order the reverse of declaration order.
|
|
1044
|
+
*/
|
|
1045
|
+
emitStatementLines(statements, depth) {
|
|
1046
|
+
const owned = statements.findIndex((statement) => statement.kind === "UsingDeclaration");
|
|
1047
|
+
const plain = (values) => values.map((child) => this.emitMappedStatement(child, depth)).filter(Boolean);
|
|
1048
|
+
if (owned < 0)
|
|
1049
|
+
return plain(statements);
|
|
1050
|
+
return [
|
|
1051
|
+
...plain(statements.slice(0, owned)),
|
|
1052
|
+
...this.emitUsingScope(statements[owned], statements.slice(owned + 1), depth),
|
|
1053
|
+
];
|
|
1054
|
+
}
|
|
1055
|
+
emitUsingScope(statement, rest, depth) {
|
|
1056
|
+
const indentation = " ".repeat(depth);
|
|
1057
|
+
const contract = this.hints.usingDisposals.get(spanIdentity(statement.span));
|
|
1058
|
+
const initializer = this.emitMappedJavaScript(statement.span, () => `const ${statement.name} = ${this.emitMappedExpression(statement.initializer)};`);
|
|
1059
|
+
// A value with no resolved contract was already diagnosed; emitting the
|
|
1060
|
+
// binding alone keeps the rest of the block readable in a failed compile.
|
|
1061
|
+
if (!contract)
|
|
1062
|
+
return [`${indentation}${initializer}`, ...this.emitStatementLines(rest, depth)];
|
|
1063
|
+
const body = this.emitStatementLines(rest, depth + 1);
|
|
1064
|
+
this.needsDisposalHelper = true;
|
|
1065
|
+
const suffix = statement.span.start;
|
|
1066
|
+
const member = contract.owner === "class" ? `[${JSON.stringify(contract.member)}]` : `.${contract.member}`;
|
|
1067
|
+
const call = `${contract.asynchronous ? "await " : ""}${statement.name}${member}()`;
|
|
1068
|
+
const released = `__velarReleased${suffix}`;
|
|
1069
|
+
const failure = `__velarUsingFailure${suffix}`;
|
|
1070
|
+
return [
|
|
1071
|
+
`${indentation}${initializer}`,
|
|
1072
|
+
`${indentation}let ${released} = false;`,
|
|
1073
|
+
`${indentation}try {`,
|
|
1074
|
+
...body,
|
|
1075
|
+
// An error already in flight owns the failure: the release still runs,
|
|
1076
|
+
// but its own failure is reported to the host instead of replacing the
|
|
1077
|
+
// error the author is about to see (D43 item 69 rule 8).
|
|
1078
|
+
`${indentation}} catch (${failure}) {`,
|
|
1079
|
+
`${indentation} ${released} = true;`,
|
|
1080
|
+
`${indentation} try { ${call}; } catch (__velarDisposeFailure${suffix}) { __velarDisposalReport(__velarDisposeFailure${suffix}); }`,
|
|
1081
|
+
`${indentation} throw ${failure};`,
|
|
1082
|
+
`${indentation}} finally {`,
|
|
1083
|
+
// Normal completion, `return`, `break`, and `continue` all arrive here,
|
|
1084
|
+
// and a release failure on those paths throws normally.
|
|
1085
|
+
`${indentation} if (!${released}) ${call};`,
|
|
1086
|
+
`${indentation}}`,
|
|
1087
|
+
];
|
|
1088
|
+
}
|
|
1089
|
+
emitStatement(statement, depth) {
|
|
1090
|
+
const indentation = " ".repeat(depth);
|
|
1091
|
+
switch (statement.kind) {
|
|
1092
|
+
case "ImportDeclaration":
|
|
1093
|
+
return this.emitImport(statement, indentation);
|
|
1094
|
+
case "ReExportDeclaration": {
|
|
1095
|
+
const emittedSource = statement.source.endsWith(".vel") ? `${statement.source.slice(0, -4)}.js` : statement.source;
|
|
1096
|
+
const names = statement.specifiers
|
|
1097
|
+
.map((specifier) => specifier.imported === specifier.exported ? specifier.imported : `${specifier.imported} as ${specifier.exported}`)
|
|
1098
|
+
.join(", ");
|
|
1099
|
+
return `${indentation}export {${names.length > 0 ? ` ${names} ` : ""}} from ${JSON.stringify(emittedSource)};`;
|
|
1100
|
+
}
|
|
1101
|
+
case "ExternModuleDeclaration":
|
|
1102
|
+
return "";
|
|
1103
|
+
case "EmbeddedJavaScriptDeclaration":
|
|
1104
|
+
return this.emitEmbeddedJavaScript(statement, indentation);
|
|
1105
|
+
case "TypeDeclaration":
|
|
1106
|
+
return this.runtimeTypes.has(statement.name) ? this.emitTypeDeclaration(statement, depth) : "";
|
|
1107
|
+
case "TypeAliasDeclaration":
|
|
1108
|
+
return this.runtimeTypes.has(statement.name) ? this.emitTypeAliasDeclaration(statement, depth) : "";
|
|
1109
|
+
case "EnumDeclaration":
|
|
1110
|
+
return this.emitEnumDeclaration(statement, depth);
|
|
1111
|
+
case "ClassDeclaration":
|
|
1112
|
+
return this.emitClass(statement, depth);
|
|
1113
|
+
case "VariableDeclaration": {
|
|
1114
|
+
const initializer = this.emitMappedExpression(statement.initializer);
|
|
1115
|
+
if (statement.pattern.kind === "NameBindingPattern") {
|
|
1116
|
+
return `${indentation}${statement.exported ? "export " : ""}${statement.binding} ${statement.pattern.name} = ${initializer};`;
|
|
1117
|
+
}
|
|
1118
|
+
const valueName = `__velarBindingValue${statement.pattern.span.start}`;
|
|
1119
|
+
return [
|
|
1120
|
+
`${indentation}const ${valueName} = ${initializer};`,
|
|
1121
|
+
...this.emitBindingPatternStatements(statement.pattern, valueName, statement.binding, statement.exported, depth, "Variable"),
|
|
1122
|
+
].join("\n");
|
|
1123
|
+
}
|
|
1124
|
+
// D39 item 53: a test is an exported async function the runner calls by
|
|
1125
|
+
// its generated name; the author's name travels in the module interface
|
|
1126
|
+
// so the reporter can quote it verbatim.
|
|
1127
|
+
case "TestDeclaration": {
|
|
1128
|
+
const lines = [
|
|
1129
|
+
...this.emitStatementLines(statement.body, depth + 1),
|
|
1130
|
+
`${" ".repeat(depth + 1)}return null;`,
|
|
1131
|
+
];
|
|
1132
|
+
return `${indentation}export async function ${testFunctionName(statement)}() {\n${lines.join("\n")}\n${indentation}}`;
|
|
1133
|
+
}
|
|
1134
|
+
case "FunctionDeclaration": {
|
|
1135
|
+
const prefix = `${statement.exported || this.forcedFunctionExports.has(statement.name) ? "export " : ""}${statement.asynchronous ? "async " : ""}function`;
|
|
1136
|
+
const parameters = statement.parameters.map((parameter) => this.emitParameter(parameter.name, parameter.defaultValue, parameter.rest)).join(", ");
|
|
1137
|
+
const lines = [...this.emitStatementLines(statement.body, depth + 1)];
|
|
1138
|
+
if (!this.blockAlwaysReturns(statement.body))
|
|
1139
|
+
lines.push(`${" ".repeat(depth + 1)}return null;`);
|
|
1140
|
+
const body = lines.join("\n");
|
|
1141
|
+
return `${indentation}${prefix} ${statement.name}(${parameters}) {${body.length > 0 ? `\n${body}\n${indentation}` : ""}}`;
|
|
1142
|
+
}
|
|
1143
|
+
case "ReturnStatement": {
|
|
1144
|
+
if (!statement.value)
|
|
1145
|
+
return `${indentation}return null;`;
|
|
1146
|
+
const value = this.emitMappedExpression(statement.value);
|
|
1147
|
+
return `${indentation}return ${this.hints.asyncResolvedValues.has(spanIdentity(statement.value.span)) ? `__velarAsyncResolvedValue(${value})` : value};`;
|
|
1148
|
+
}
|
|
1149
|
+
case "ThrowStatement":
|
|
1150
|
+
return `${indentation}throw ${this.emitMappedExpression(statement.value)};`;
|
|
1151
|
+
case "AssertStatement": {
|
|
1152
|
+
const message = statement.message ? this.emitMappedExpression(statement.message) : JSON.stringify("Assertion failed");
|
|
1153
|
+
return [
|
|
1154
|
+
`${indentation}if (!(${this.emitCondition(statement.condition)})) {`,
|
|
1155
|
+
`${indentation} const __velarAssertionError = new Error(${message});`,
|
|
1156
|
+
`${indentation} __velarAssertionError.name = "AssertionError";`,
|
|
1157
|
+
`${indentation} throw __velarAssertionError;`,
|
|
1158
|
+
`${indentation}}`,
|
|
1159
|
+
].join("\n");
|
|
1160
|
+
}
|
|
1161
|
+
case "IfStatement": {
|
|
1162
|
+
const thenBody = this.emitStatementLines(statement.thenBody, depth + 1).join("\n");
|
|
1163
|
+
let output = `${indentation}if (${this.emitCondition(statement.condition)}) {${thenBody.length > 0 ? `\n${thenBody}\n${indentation}` : ""}}`;
|
|
1164
|
+
if (statement.elseBody) {
|
|
1165
|
+
const chained = statement.elseBody.length === 1 && statement.elseBody[0]?.kind === "IfStatement"
|
|
1166
|
+
? this.emitMappedStatement(statement.elseBody[0], 0)
|
|
1167
|
+
: null;
|
|
1168
|
+
if (chained) {
|
|
1169
|
+
output += ` else ${chained}`;
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
const elseBody = this.emitStatementLines(statement.elseBody, depth + 1).join("\n");
|
|
1173
|
+
output += ` else {${elseBody.length > 0 ? `\n${elseBody}\n${indentation}` : ""}}`;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
return output;
|
|
1177
|
+
}
|
|
1178
|
+
case "MatchStatement": {
|
|
1179
|
+
const suffix = statement.span.start;
|
|
1180
|
+
const valueName = `__velarMatchValue${suffix}`;
|
|
1181
|
+
const matchedName = `__velarMatchDone${suffix}`;
|
|
1182
|
+
const lines = [
|
|
1183
|
+
`${indentation}{`,
|
|
1184
|
+
`${indentation} const ${valueName} = ${this.emitMappedExpression(statement.value)};`,
|
|
1185
|
+
`${indentation} let ${matchedName} = false;`,
|
|
1186
|
+
];
|
|
1187
|
+
for (const branch of statement.cases) {
|
|
1188
|
+
const attemptName = `__velarMatchCase${branch.pattern.span.start}`;
|
|
1189
|
+
const attempt = this.emitMatchPatternAttempt(branch.pattern, valueName, `${indentation} `);
|
|
1190
|
+
lines.push(`${indentation} let ${attemptName} = null;`);
|
|
1191
|
+
lines.push(`${indentation} if (!${matchedName} && (${attemptName} = (() => {`);
|
|
1192
|
+
lines.push(...attempt.lines);
|
|
1193
|
+
lines.push(`${indentation} return [${attempt.bindings.map((binding) => binding.value).join(", ")}];`);
|
|
1194
|
+
lines.push(`${indentation} })()) !== null) {`);
|
|
1195
|
+
attempt.bindings.forEach((binding, index) => {
|
|
1196
|
+
lines.push(`${indentation} const ${binding.name} = ${attemptName}[${index}];`);
|
|
1197
|
+
});
|
|
1198
|
+
if (branch.guard) {
|
|
1199
|
+
lines.push(`${indentation} if (${this.emitCondition(branch.guard)}) {`);
|
|
1200
|
+
lines.push(`${indentation} ${matchedName} = true;`);
|
|
1201
|
+
lines.push(...this.emitStatementLines(branch.body, depth + 3));
|
|
1202
|
+
lines.push(`${indentation} }`);
|
|
1203
|
+
}
|
|
1204
|
+
else {
|
|
1205
|
+
lines.push(`${indentation} ${matchedName} = true;`);
|
|
1206
|
+
lines.push(...this.emitStatementLines(branch.body, depth + 2));
|
|
1207
|
+
}
|
|
1208
|
+
lines.push(`${indentation} }`);
|
|
1209
|
+
}
|
|
1210
|
+
lines.push(`${indentation}}`);
|
|
1211
|
+
return lines.join("\n");
|
|
1212
|
+
}
|
|
1213
|
+
case "ForStatement": {
|
|
1214
|
+
if (statement.asynchronous) {
|
|
1215
|
+
const suffix = statement.span.start;
|
|
1216
|
+
const sourceName = `__velarAsyncForSource${suffix}`;
|
|
1217
|
+
const nextName = `__velarAsyncForNext${suffix}`;
|
|
1218
|
+
const valueName = `__velarAsyncForValue${suffix}`;
|
|
1219
|
+
const indexName = `__velarAsyncForIndex${suffix}`;
|
|
1220
|
+
const bodyDepth = depth + 2;
|
|
1221
|
+
const lines = [
|
|
1222
|
+
`${indentation}{`,
|
|
1223
|
+
`${" ".repeat(depth + 1)}const ${sourceName} = ${this.emitMappedExpression(statement.iterable)};`,
|
|
1224
|
+
`${" ".repeat(depth + 1)}const ${nextName} = __velarAsyncPullNext(${sourceName});`,
|
|
1225
|
+
`${" ".repeat(depth + 1)}let ${indexName} = 0;`,
|
|
1226
|
+
`${" ".repeat(depth + 1)}while (true) {`,
|
|
1227
|
+
`${" ".repeat(bodyDepth)}const ${valueName} = await __velarNormalizePromiseValue(__velarAsyncPullCall(${sourceName}, ${nextName}));`,
|
|
1228
|
+
`${" ".repeat(bodyDepth)}if (${valueName} === null) break;`,
|
|
1229
|
+
...this.emitBindingPatternStatements(statement.pattern, valueName, "const", false, bodyDepth, "Async for"),
|
|
1230
|
+
...(statement.secondPattern
|
|
1231
|
+
? this.emitBindingPatternStatements(statement.secondPattern, indexName, "const", false, bodyDepth, "Async for second slot")
|
|
1232
|
+
: []),
|
|
1233
|
+
`${" ".repeat(bodyDepth)}${indexName} += 1;`,
|
|
1234
|
+
...this.emitStatementLines(statement.body, bodyDepth),
|
|
1235
|
+
`${" ".repeat(depth + 1)}}`,
|
|
1236
|
+
`${indentation}}`,
|
|
1237
|
+
];
|
|
1238
|
+
return lines.join("\n");
|
|
1239
|
+
}
|
|
1240
|
+
if (this.hints.nativeRangeForStatements.has(statement.span.start)
|
|
1241
|
+
&& statement.iterable.kind === "CallExpression"
|
|
1242
|
+
&& statement.pattern.kind === "NameBindingPattern") {
|
|
1243
|
+
const call = statement.iterable;
|
|
1244
|
+
const sourceArguments = call.arguments.map((argument) => this.emitMappedExpression(argument));
|
|
1245
|
+
const namedOrder = this.hints.namedArgumentOrders.get(spanIdentity(call.span));
|
|
1246
|
+
const arguments_ = namedOrder
|
|
1247
|
+
? namedOrder.map((source) => source === -1 ? "undefined" : `__velarNamedArguments[${source}]`)
|
|
1248
|
+
: sourceArguments;
|
|
1249
|
+
const emittedArguments = namedOrder
|
|
1250
|
+
? `...((__velarNamedArguments) => [${arguments_.join(", ")}])([${sourceArguments.join(", ")}])`
|
|
1251
|
+
: arguments_.join(", ");
|
|
1252
|
+
const suffix = statement.span.start;
|
|
1253
|
+
const boundsName = `__velarRangeBounds${suffix}`;
|
|
1254
|
+
const counterName = `__velarRangeCounter${suffix}`;
|
|
1255
|
+
const endName = `__velarRangeEnd${suffix}`;
|
|
1256
|
+
const stepName = `__velarRangeStep${suffix}`;
|
|
1257
|
+
const valueName = statement.pattern.name;
|
|
1258
|
+
const body = this.emitStatementLines(statement.body, depth + 2).join("\n");
|
|
1259
|
+
return [
|
|
1260
|
+
`${indentation}{`,
|
|
1261
|
+
`${indentation} const ${boundsName} = __velarCountedRangeOwner.__velarCounted(${emittedArguments});`,
|
|
1262
|
+
`${indentation} let ${counterName} = ${boundsName}[0];`,
|
|
1263
|
+
`${indentation} const ${endName} = ${boundsName}[1];`,
|
|
1264
|
+
`${indentation} const ${stepName} = ${boundsName}[2];`,
|
|
1265
|
+
`${indentation} for (; ${stepName} > 0 ? ${counterName} < ${endName} : ${counterName} > ${endName}; ${counterName} += ${stepName}) {`,
|
|
1266
|
+
`${indentation} const ${valueName} = ${counterName};${body.length > 0 ? `\n${body}` : ""}`,
|
|
1267
|
+
`${indentation} }`,
|
|
1268
|
+
`${indentation}}`,
|
|
1269
|
+
].join("\n");
|
|
1270
|
+
}
|
|
1271
|
+
this.needsCollectionHelpers = true;
|
|
1272
|
+
const iterable = this.emitMappedExpression(statement.iterable);
|
|
1273
|
+
if (!statement.secondPattern && statement.pattern.kind === "NameBindingPattern") {
|
|
1274
|
+
const body = this.emitStatementLines(statement.body, depth + 1).join("\n");
|
|
1275
|
+
return `${indentation}for (const ${statement.pattern.name} of __velarCollectionIterator(${iterable})) {${body.length > 0 ? `\n${body}\n${indentation}` : ""}}`;
|
|
1276
|
+
}
|
|
1277
|
+
if (statement.secondPattern) {
|
|
1278
|
+
const pairName = `__velarForPair${statement.pattern.span.start}`;
|
|
1279
|
+
const lines = [
|
|
1280
|
+
...this.emitBindingPatternStatements(statement.pattern, `${pairName}[0]`, "const", false, depth + 1, "For first slot"),
|
|
1281
|
+
...this.emitBindingPatternStatements(statement.secondPattern, `${pairName}[1]`, "const", false, depth + 1, "For second slot"),
|
|
1282
|
+
...this.emitStatementLines(statement.body, depth + 1),
|
|
1283
|
+
];
|
|
1284
|
+
return `${indentation}for (const ${pairName} of __velarCollectionPairIterator(${iterable})) {${lines.length > 0 ? `\n${lines.join("\n")}\n${indentation}` : ""}}`;
|
|
1285
|
+
}
|
|
1286
|
+
const valueName = `__velarForValue${statement.pattern.span.start}`;
|
|
1287
|
+
const lines = [
|
|
1288
|
+
...this.emitBindingPatternStatements(statement.pattern, valueName, "const", false, depth + 1, "For"),
|
|
1289
|
+
...this.emitStatementLines(statement.body, depth + 1),
|
|
1290
|
+
];
|
|
1291
|
+
return `${indentation}for (const ${valueName} of __velarCollectionIterator(${iterable})) {${lines.length > 0 ? `\n${lines.join("\n")}\n${indentation}` : ""}}`;
|
|
1292
|
+
}
|
|
1293
|
+
case "WhileStatement": {
|
|
1294
|
+
const body = this.emitStatementLines(statement.body, depth + 1).join("\n");
|
|
1295
|
+
return `${indentation}while (${this.emitCondition(statement.condition)}) {${body.length > 0 ? `\n${body}\n${indentation}` : ""}}`;
|
|
1296
|
+
}
|
|
1297
|
+
case "BreakStatement":
|
|
1298
|
+
return `${indentation}break;`;
|
|
1299
|
+
case "ContinueStatement":
|
|
1300
|
+
return `${indentation}continue;`;
|
|
1301
|
+
case "PassStatement":
|
|
1302
|
+
return "";
|
|
1303
|
+
case "TryStatement": {
|
|
1304
|
+
const tryBody = this.emitStatementLines(statement.tryBody, depth + 1).join("\n");
|
|
1305
|
+
let output = `${indentation}try {${tryBody.length > 0 ? `\n${tryBody}\n${indentation}` : ""}}`;
|
|
1306
|
+
if (statement.catchBody) {
|
|
1307
|
+
this.needsThrownValueHelper = true;
|
|
1308
|
+
const catchBody = this.emitStatementLines(statement.catchBody, depth + 1).join("\n");
|
|
1309
|
+
const catchName = statement.catchName ?? "error";
|
|
1310
|
+
const normalization = `${" ".repeat(depth + 1)}${catchName} = __velarNormalizeError(${catchName});`;
|
|
1311
|
+
output += ` catch (${catchName}) {\n${normalization}${catchBody.length > 0 ? `\n${catchBody}` : ""}\n${indentation}}`;
|
|
1312
|
+
}
|
|
1313
|
+
if (statement.finallyBody) {
|
|
1314
|
+
const finallyBody = this.emitStatementLines(statement.finallyBody, depth + 1).join("\n");
|
|
1315
|
+
output += ` finally {${finallyBody.length > 0 ? `\n${finallyBody}\n${indentation}` : ""}}`;
|
|
1316
|
+
}
|
|
1317
|
+
return output;
|
|
1318
|
+
}
|
|
1319
|
+
case "AssignmentStatement":
|
|
1320
|
+
if (statement.target.kind === "IndexExpression") {
|
|
1321
|
+
const binaryKind = this.hints.binaryIndexes.get(spanIdentity(statement.target.span));
|
|
1322
|
+
if (binaryKind)
|
|
1323
|
+
this.needsBinaryHelpers = true;
|
|
1324
|
+
else {
|
|
1325
|
+
this.needsIndexHelpers = true;
|
|
1326
|
+
this.needsCollectionHelpers = true;
|
|
1327
|
+
}
|
|
1328
|
+
const object = this.emitMappedExpression(statement.target.object);
|
|
1329
|
+
const index = this.emitMappedExpression(statement.target.index);
|
|
1330
|
+
if (binaryKind === "bytes") {
|
|
1331
|
+
return `${indentation}__velarBinaryRuntime.__velarSetIndex(${object}, ${index}, ${this.emitMappedExpression(statement.value)});`;
|
|
1332
|
+
}
|
|
1333
|
+
if (binaryKind) {
|
|
1334
|
+
const setHelper = this.binarySetIndexHelper(binaryKind);
|
|
1335
|
+
const getHelper = this.binaryIndexHelper(binaryKind);
|
|
1336
|
+
if (statement.operator === "=") {
|
|
1337
|
+
return `${indentation}__velarBinaryRuntime.${setHelper}(${object}, ${index}, ${this.emitMappedExpression(statement.value)});`;
|
|
1338
|
+
}
|
|
1339
|
+
const suffix = statement.span.start;
|
|
1340
|
+
const objectName = `__velarIndexObject${suffix}`;
|
|
1341
|
+
const keyName = `__velarIndexKey${suffix}`;
|
|
1342
|
+
const value = this.emitMappedExpression(statement.value);
|
|
1343
|
+
return [
|
|
1344
|
+
`${indentation}{`,
|
|
1345
|
+
`${indentation} const ${objectName} = ${object};`,
|
|
1346
|
+
`${indentation} const ${keyName} = ${index};`,
|
|
1347
|
+
`${indentation} __velarBinaryRuntime.${setHelper}(${objectName}, ${keyName}, ${this.emitCompoundOperation(`__velarBinaryRuntime.${getHelper}(${objectName}, ${keyName})`, statement.operator, value)});`,
|
|
1348
|
+
`${indentation}}`,
|
|
1349
|
+
].join("\n");
|
|
1350
|
+
}
|
|
1351
|
+
if (statement.operator === "=") {
|
|
1352
|
+
return `${indentation}__velarSetIndex(${object}, ${index}, ${this.emitMappedExpression(statement.value)});`;
|
|
1353
|
+
}
|
|
1354
|
+
const suffix = statement.span.start;
|
|
1355
|
+
const objectName = `__velarIndexObject${suffix}`;
|
|
1356
|
+
const keyName = `__velarIndexKey${suffix}`;
|
|
1357
|
+
const value = this.emitMappedExpression(statement.value);
|
|
1358
|
+
return [
|
|
1359
|
+
`${indentation}{`,
|
|
1360
|
+
`${indentation} const ${objectName} = ${object};`,
|
|
1361
|
+
`${indentation} const ${keyName} = ${index};`,
|
|
1362
|
+
`${indentation} __velarSetIndex(${objectName}, ${keyName}, ${this.emitCompoundOperation(`__velarIndex(${objectName}, ${keyName})`, statement.operator, value)});`,
|
|
1363
|
+
`${indentation}}`,
|
|
1364
|
+
].join("\n");
|
|
1365
|
+
}
|
|
1366
|
+
if (statement.operator !== "=" && statement.target.kind === "MemberExpression") {
|
|
1367
|
+
const key = spanIdentity(statement.target.span);
|
|
1368
|
+
const staticFieldOwnerDepth = this.hints.staticFieldReads.get(key);
|
|
1369
|
+
const guardedInstanceField = this.hints.instanceFieldReads.has(key);
|
|
1370
|
+
const guardedPrivateField = this.hints.privateInstanceFieldReads.has(key);
|
|
1371
|
+
if (staticFieldOwnerDepth !== undefined || guardedInstanceField || guardedPrivateField) {
|
|
1372
|
+
const suffix = statement.span.start;
|
|
1373
|
+
const objectName = `__velarMemberObject${suffix}`;
|
|
1374
|
+
const privateProperty = this.hints.privateMembers.has(key);
|
|
1375
|
+
const property = `${privateProperty ? "#" : ""}${statement.target.property}`;
|
|
1376
|
+
const read = staticFieldOwnerDepth !== undefined
|
|
1377
|
+
? `__velarReadStaticField(${objectName}, ${JSON.stringify(statement.target.property)}, ${staticFieldOwnerDepth})`
|
|
1378
|
+
: guardedPrivateField
|
|
1379
|
+
? `__velarReadPrivateField(${objectName}.${property}, ${JSON.stringify(statement.target.property)})`
|
|
1380
|
+
: `__velarReadInstanceField(${objectName}, ${JSON.stringify(statement.target.property)})`;
|
|
1381
|
+
const operation = this.emitCompoundOperation(read, statement.operator, this.emitMappedExpression(statement.value));
|
|
1382
|
+
return [
|
|
1383
|
+
`${indentation}{`,
|
|
1384
|
+
`${indentation} const ${objectName} = ${this.emitMappedExpression(statement.target.object)};`,
|
|
1385
|
+
`${indentation} ${objectName}.${property} = ${operation};`,
|
|
1386
|
+
`${indentation}}`,
|
|
1387
|
+
].join("\n");
|
|
1388
|
+
}
|
|
1389
|
+
if (this.bitwiseAssignmentOperator(statement.operator)) {
|
|
1390
|
+
const suffix = statement.span.start;
|
|
1391
|
+
const objectName = `__velarMemberObject${suffix}`;
|
|
1392
|
+
const privateProperty = this.hints.privateMembers.has(key);
|
|
1393
|
+
const property = `${privateProperty ? "#" : ""}${statement.target.property}`;
|
|
1394
|
+
const value = this.emitMappedExpression(statement.value);
|
|
1395
|
+
return [
|
|
1396
|
+
`${indentation}{`,
|
|
1397
|
+
`${indentation} const ${objectName} = ${this.emitMappedExpression(statement.target.object)};`,
|
|
1398
|
+
`${indentation} ${objectName}.${property} = ${this.emitCompoundOperation(`${objectName}.${property}`, statement.operator, value)};`,
|
|
1399
|
+
`${indentation}}`,
|
|
1400
|
+
].join("\n");
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
{
|
|
1404
|
+
const target = this.emitMappedAssignmentTarget(statement.target);
|
|
1405
|
+
const value = this.emitMappedExpression(statement.value);
|
|
1406
|
+
if (statement.operator !== "=" && this.bitwiseAssignmentOperator(statement.operator)) {
|
|
1407
|
+
return `${indentation}${target} = ${this.emitCompoundOperation(target, statement.operator, value)};`;
|
|
1408
|
+
}
|
|
1409
|
+
return `${indentation}${target} ${statement.operator} ${value};`;
|
|
1410
|
+
}
|
|
1411
|
+
case "ExpressionStatement":
|
|
1412
|
+
return `${indentation}${this.emitMappedExpression(statement.expression, false)};`;
|
|
1413
|
+
case "AsyncStatement":
|
|
1414
|
+
// Detached execution never floats: the compiler-owned observer
|
|
1415
|
+
// adopts the Promise, normalizes rejection to Error, and reports it
|
|
1416
|
+
// through the host error channel (see docs/contributing/runtime-boundary.md,
|
|
1417
|
+
// B-DETACHED-ASYNC). The expression takes the same Promise
|
|
1418
|
+
// normalization every other Promise consumer applies, so a foreign
|
|
1419
|
+
// thenable or an `undefined` from an extern boundary fails as an owned
|
|
1420
|
+
// 'Expected an actual Promise' instead of a host-voiced
|
|
1421
|
+
// 'Promise.prototype.then called on incompatible receiver'.
|
|
1422
|
+
return `${indentation}__velarDetachedTask(${this.emitMappedExpression(statement.expression)});`;
|
|
1423
|
+
default:
|
|
1424
|
+
return "";
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
emitImport(statement, indentation) {
|
|
1428
|
+
const source = statement.source;
|
|
1429
|
+
const emittedSource = source.endsWith(".vel") ? `${source.slice(0, -4)}.js` : source;
|
|
1430
|
+
const first = statement.specifiers[0];
|
|
1431
|
+
if (first?.namespace) {
|
|
1432
|
+
const declared = statement.javascript && !statement.unsafe
|
|
1433
|
+
? this.externModuleExports.get(source)
|
|
1434
|
+
: undefined;
|
|
1435
|
+
if (!declared)
|
|
1436
|
+
return `${indentation}import * as ${first.local} from ${JSON.stringify(emittedSource)};`;
|
|
1437
|
+
this.needsExternExportHelper = true;
|
|
1438
|
+
return [
|
|
1439
|
+
`${indentation}import * as ${first.local} from ${JSON.stringify(emittedSource)};`,
|
|
1440
|
+
...[...declared].sort().map((name) => (`${indentation}__velarExternExport(${first.local}, ${JSON.stringify(name)}, ${JSON.stringify(source)});`)),
|
|
1441
|
+
].join("\n");
|
|
1442
|
+
}
|
|
1443
|
+
// W-22: names governed by an extern module declaration import through the
|
|
1444
|
+
// module namespace so a declared-but-missing export fails at this import
|
|
1445
|
+
// site with a velar-voiced error instead of linking to undefined (bundled
|
|
1446
|
+
// CommonJS interop) or a host-voiced link refusal. Only the genuinely
|
|
1447
|
+
// imported declared names are checked; specifiers the declaration does not
|
|
1448
|
+
// name keep the native import form.
|
|
1449
|
+
const declared = statement.javascript && !statement.unsafe
|
|
1450
|
+
? this.externModuleExports.get(source)
|
|
1451
|
+
: undefined;
|
|
1452
|
+
const checked = declared ? statement.specifiers.filter((specifier) => declared.has(specifier.imported)) : [];
|
|
1453
|
+
const native = declared ? statement.specifiers.filter((specifier) => !declared.has(specifier.imported)) : statement.specifiers;
|
|
1454
|
+
const lines = [];
|
|
1455
|
+
if (native.length === 1 && native[0].imported === "default") {
|
|
1456
|
+
lines.push(`${indentation}import ${native[0].local} from ${JSON.stringify(emittedSource)};`);
|
|
1457
|
+
}
|
|
1458
|
+
else if (native.length > 0 || checked.length === 0) {
|
|
1459
|
+
const names = native.map((specifier) => specifier.imported === specifier.local ? specifier.imported : `${specifier.imported} as ${specifier.local}`).join(", ");
|
|
1460
|
+
lines.push(`${indentation}import { ${names} } from ${JSON.stringify(emittedSource)};`);
|
|
1461
|
+
}
|
|
1462
|
+
if (checked.length > 0) {
|
|
1463
|
+
this.needsExternExportHelper = true;
|
|
1464
|
+
const namespaceName = `__velarExternModule${statement.span.start}`;
|
|
1465
|
+
lines.push(`${indentation}import * as ${namespaceName} from ${JSON.stringify(emittedSource)};`);
|
|
1466
|
+
for (const specifier of checked) {
|
|
1467
|
+
lines.push(`${indentation}const ${specifier.local} = __velarExternExport(${namespaceName}, ${JSON.stringify(specifier.imported)}, ${JSON.stringify(source)});`);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return lines.join("\n");
|
|
1471
|
+
}
|
|
1472
|
+
prepareEmbeddedJavaScript(program) {
|
|
1473
|
+
this.embeddedJavaScript.clear();
|
|
1474
|
+
let ordinal = 0;
|
|
1475
|
+
for (const statement of program.body) {
|
|
1476
|
+
if (statement.kind !== "EmbeddedJavaScriptDeclaration")
|
|
1477
|
+
continue;
|
|
1478
|
+
const specifier = embeddedJavaScriptSpecifier(this.sourcePath, ordinal);
|
|
1479
|
+
const occupiedJavaScriptNames = new Set(statement.bindings.map((binding) => binding.name));
|
|
1480
|
+
let factoryName = statement.contract ? `__velarEmbeddedFactory_${ordinal}` : null;
|
|
1481
|
+
while (factoryName && occupiedJavaScriptNames.has(factoryName))
|
|
1482
|
+
factoryName += "_";
|
|
1483
|
+
const localFactoryName = statement.contract ? `__velarEmbeddedFactoryBinding_${ordinal}` : null;
|
|
1484
|
+
const generated = statement.contract
|
|
1485
|
+
? emitCheckedEmbeddedJavaScript(statement, factoryName)
|
|
1486
|
+
: mappedSource(statement.source, statement.sourceSpan.start);
|
|
1487
|
+
this.embeddedJavaScript.set(statement, {
|
|
1488
|
+
statement,
|
|
1489
|
+
specifier,
|
|
1490
|
+
factoryName,
|
|
1491
|
+
localFactoryName,
|
|
1492
|
+
code: generated.code,
|
|
1493
|
+
mappings: generated.mappings,
|
|
1494
|
+
});
|
|
1495
|
+
ordinal += 1;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
emitEmbeddedJavaScript(statement, indentation) {
|
|
1499
|
+
const prepared = this.embeddedJavaScript.get(statement);
|
|
1500
|
+
if (!prepared)
|
|
1501
|
+
throw new Error("An embedded JavaScript declaration has no prepared sibling module");
|
|
1502
|
+
if (!statement.contract) {
|
|
1503
|
+
const imported = {
|
|
1504
|
+
kind: "ImportDeclaration",
|
|
1505
|
+
source: prepared.specifier,
|
|
1506
|
+
sourceSpan: statement.sourceSpan,
|
|
1507
|
+
javascript: true,
|
|
1508
|
+
unsafe: true,
|
|
1509
|
+
specifiers: statement.exports.map((item) => ({
|
|
1510
|
+
imported: item.name,
|
|
1511
|
+
local: item.name,
|
|
1512
|
+
namespace: false,
|
|
1513
|
+
span: item.nameSpan,
|
|
1514
|
+
})),
|
|
1515
|
+
span: statement.span,
|
|
1516
|
+
};
|
|
1517
|
+
return this.emitImport(imported, indentation);
|
|
1518
|
+
}
|
|
1519
|
+
const names = [
|
|
1520
|
+
...statement.contract.functions.map((item) => item.name),
|
|
1521
|
+
...statement.contract.constants.map((item) => item.name),
|
|
1522
|
+
...statement.contract.classes.map((item) => item.name),
|
|
1523
|
+
];
|
|
1524
|
+
const captureValues = statement.captures.map((capture) => this.emitMappedExpression({
|
|
1525
|
+
kind: "IdentifierExpression",
|
|
1526
|
+
name: capture.name,
|
|
1527
|
+
span: capture.nameSpan,
|
|
1528
|
+
})).join(", ");
|
|
1529
|
+
const call = `${prepared.localFactoryName}(${captureValues})`;
|
|
1530
|
+
return [
|
|
1531
|
+
`${indentation}import { ${prepared.factoryName} as ${prepared.localFactoryName} } from ${JSON.stringify(prepared.specifier)};`,
|
|
1532
|
+
names.length > 0
|
|
1533
|
+
? `${indentation}const { ${names.join(", ")} } = ${call};`
|
|
1534
|
+
: `${indentation}${call};`,
|
|
1535
|
+
].join("\n");
|
|
1536
|
+
}
|
|
1537
|
+
emitTypeDeclaration(statement, depth) {
|
|
1538
|
+
const parameters = statement.typeParameters?.map((parameter) => parameter.name) ?? null;
|
|
1539
|
+
if (!parameters)
|
|
1540
|
+
return this.emitRecordTypeDeclaration(statement, depth);
|
|
1541
|
+
this.genericTypeParameters = parameters;
|
|
1542
|
+
try {
|
|
1543
|
+
return this.emitRecordTypeDeclaration(statement, depth);
|
|
1544
|
+
}
|
|
1545
|
+
finally {
|
|
1546
|
+
this.genericTypeParameters = null;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
emitRecordTypeDeclaration(statement, depth) {
|
|
1550
|
+
const indentation = " ".repeat(depth);
|
|
1551
|
+
const generic = this.genericTypeParameters;
|
|
1552
|
+
const checkName = this.runtimeTypeCheckName(statement.name);
|
|
1553
|
+
const fields = statement.fields.map((field, index) => ({
|
|
1554
|
+
field,
|
|
1555
|
+
descriptor: `__velarField${index}`,
|
|
1556
|
+
type: this.resolveDeclarationType(field.type),
|
|
1557
|
+
}));
|
|
1558
|
+
const checks = fields.map(({ descriptor, type }) => {
|
|
1559
|
+
const present = `${descriptor}?.enumerable && "value" in ${descriptor} && ${this.emitTypeCheck(type, `${descriptor}.value`, "__state")}`;
|
|
1560
|
+
return type.kind === "optional" ? `(${descriptor} === undefined || (${present}))` : present;
|
|
1561
|
+
});
|
|
1562
|
+
const predicate = checks.length > 0 ? checks.join(" && ") : "true";
|
|
1563
|
+
const exportPrefix = statement.exported ? "export " : "";
|
|
1564
|
+
// COL-U5: parse failures name the failing field. The explain companion
|
|
1565
|
+
// re-runs the per-field checks only on the failure path, so is() and the
|
|
1566
|
+
// success path stay exactly as cheap as before.
|
|
1567
|
+
const explainName = `__velarTypeExplain_${statement.name}`;
|
|
1568
|
+
// D55 rule 121: a generic record's validator is the same validator with the
|
|
1569
|
+
// erased positions supplied from outside — the arguments carry a predicate,
|
|
1570
|
+
// a display text, and a key per type argument, so `parse` still names the
|
|
1571
|
+
// type the author wrote and the memo still answers with one Type object per
|
|
1572
|
+
// instantiation.
|
|
1573
|
+
const argumentsParameter = generic ? ", __velarArguments" : "";
|
|
1574
|
+
const displayName = generic ? "__velarArguments.name" : JSON.stringify(statement.name);
|
|
1575
|
+
const pathText = (suffix) => generic
|
|
1576
|
+
? (suffix === "" ? displayName : `${displayName} + ${JSON.stringify(suffix)}`)
|
|
1577
|
+
: JSON.stringify(`${statement.name}${suffix}`);
|
|
1578
|
+
const explainLines = [
|
|
1579
|
+
`${indentation}function ${explainName}(value${argumentsParameter}) {`,
|
|
1580
|
+
`${indentation} if (value === null || typeof value !== "object" || __velarValidationIsArray(value) || !__velarValidationIsPlainObject(value)) {`,
|
|
1581
|
+
`${indentation} return { path: ${pathText("")}, field: null, reason: "the value is not a record" };`,
|
|
1582
|
+
`${indentation} }`,
|
|
1583
|
+
...fields.flatMap(({ field, type }) => {
|
|
1584
|
+
const descriptor = "__velarExplainField";
|
|
1585
|
+
const typeText = this.typeTextExpression(type, field.type.syntax);
|
|
1586
|
+
const lines = [
|
|
1587
|
+
`${indentation} {`,
|
|
1588
|
+
`${indentation} const ${descriptor} = __velarValidationOwnDescriptor(value, ${JSON.stringify(field.name)});`,
|
|
1589
|
+
];
|
|
1590
|
+
if (type.kind === "optional") {
|
|
1591
|
+
lines.push(`${indentation} if (${descriptor} !== undefined && !(${descriptor}.enumerable && "value" in ${descriptor} && ${this.emitTypeCheck(type, `${descriptor}.value`, "__velarValidationState()")})) {`);
|
|
1592
|
+
}
|
|
1593
|
+
else {
|
|
1594
|
+
lines.push(`${indentation} if (${descriptor} === undefined) {`);
|
|
1595
|
+
lines.push(`${indentation} return { path: ${pathText(`.${field.name}`)}, field: ${JSON.stringify(field.name)}, reason: ${JSON.stringify(`field '${field.name}' is missing`)} };`);
|
|
1596
|
+
lines.push(`${indentation} }`);
|
|
1597
|
+
lines.push(`${indentation} if (!(${descriptor}.enumerable && "value" in ${descriptor} && ${this.emitTypeCheck(type, `${descriptor}.value`, "__velarValidationState()")})) {`);
|
|
1598
|
+
}
|
|
1599
|
+
lines.push(`${indentation} return { path: ${pathText(`.${field.name}`)}, field: ${JSON.stringify(field.name)}, reason: ${JSON.stringify(`field '${field.name}' does not match `)} + ${typeText} };`);
|
|
1600
|
+
lines.push(`${indentation} }`);
|
|
1601
|
+
lines.push(`${indentation} }`);
|
|
1602
|
+
return lines;
|
|
1603
|
+
}),
|
|
1604
|
+
`${indentation} return { path: ${pathText("")}, field: null, reason: null };`,
|
|
1605
|
+
`${indentation}}`,
|
|
1606
|
+
"",
|
|
1607
|
+
];
|
|
1608
|
+
const typeObject = [
|
|
1609
|
+
`${indentation} is(value, __state) {`,
|
|
1610
|
+
`${indentation} return ${checkName}(value, __state${generic ? ", __velarArguments" : ""});`,
|
|
1611
|
+
`${indentation} },`,
|
|
1612
|
+
`${indentation} parse(value) {`,
|
|
1613
|
+
`${indentation} if (!${checkName}(value, __velarValidationState()${generic ? ", __velarArguments" : ""})) {`,
|
|
1614
|
+
`${indentation} const __velarDetail = ${explainName}(value${generic ? ", __velarArguments" : ""});`,
|
|
1615
|
+
`${indentation} throw new __VelarValidationError(${generic ? `"Value does not match " + ${displayName}` : JSON.stringify(`Value does not match ${statement.name}`)} + (__velarDetail.reason ? " — " + __velarDetail.reason : "") + __velarValidationRejectionHint(value), __velarDetail);`,
|
|
1616
|
+
`${indentation} }`,
|
|
1617
|
+
`${indentation} return value;`,
|
|
1618
|
+
`${indentation} },`,
|
|
1619
|
+
];
|
|
1620
|
+
if (generic) {
|
|
1621
|
+
const instances = `__velarGenericInstances_${statement.name}`;
|
|
1622
|
+
return [
|
|
1623
|
+
...explainLines,
|
|
1624
|
+
...this.recordCheckFunctionLines(statement, fields, predicate, checkName, indentation, argumentsParameter),
|
|
1625
|
+
"",
|
|
1626
|
+
`${indentation}const ${instances} = [];`,
|
|
1627
|
+
// The instantiation memo: one frozen Type object per set of arguments,
|
|
1628
|
+
// found by a key the emitter builds from the arguments' own identities.
|
|
1629
|
+
// It is what makes `type Tree<T>: kids: List<Tree<T>>` terminate — the
|
|
1630
|
+
// body's reference to its own instantiation is a lookup, not a rebuild.
|
|
1631
|
+
`${indentation}${exportPrefix}const ${statement.name} = __velarValidationFreeze({`,
|
|
1632
|
+
`${indentation} of(__velarKeys, __velarTexts, __velarChecks) {`,
|
|
1633
|
+
`${indentation} let __velarKey = ${JSON.stringify(statement.name)};`,
|
|
1634
|
+
`${indentation} for (let __velarIndex = 0; __velarIndex < __velarKeys.length; __velarIndex += 1) __velarKey += "\\u0000" + __velarKeys[__velarIndex];`,
|
|
1635
|
+
`${indentation} for (let __velarIndex = 0; __velarIndex < ${instances}.length; __velarIndex += 1) {`,
|
|
1636
|
+
`${indentation} if (${instances}[__velarIndex].key === __velarKey) return ${instances}[__velarIndex].type;`,
|
|
1637
|
+
`${indentation} }`,
|
|
1638
|
+
`${indentation} let __velarName = ${JSON.stringify(`${statement.name}<`)};`,
|
|
1639
|
+
`${indentation} for (let __velarIndex = 0; __velarIndex < __velarTexts.length; __velarIndex += 1) __velarName += (__velarIndex === 0 ? "" : ", ") + __velarTexts[__velarIndex];`,
|
|
1640
|
+
`${indentation} __velarName += ">";`,
|
|
1641
|
+
`${indentation} const __velarArguments = { keys: __velarKeys, texts: __velarTexts, checks: __velarChecks, name: __velarName };`,
|
|
1642
|
+
`${indentation} const __velarType = __velarRegisterRuntimeType(__velarValidationFreeze({`,
|
|
1643
|
+
...typeObject.map((line) => `${indentation} ${line}`),
|
|
1644
|
+
`${indentation} }));`,
|
|
1645
|
+
`${indentation} ${instances}[${instances}.length] = { key: __velarKey, type: __velarType };`,
|
|
1646
|
+
`${indentation} return __velarType;`,
|
|
1647
|
+
`${indentation} },`,
|
|
1648
|
+
`${indentation}});`,
|
|
1649
|
+
].join("\n");
|
|
1650
|
+
}
|
|
1651
|
+
return [
|
|
1652
|
+
...explainLines,
|
|
1653
|
+
...this.recordCheckFunctionLines(statement, fields, predicate, checkName, indentation, ""),
|
|
1654
|
+
"",
|
|
1655
|
+
`${indentation}${exportPrefix}const ${statement.name} = __velarRegisterRuntimeType(__velarValidationFreeze({`,
|
|
1656
|
+
...typeObject,
|
|
1657
|
+
`${indentation}}));`,
|
|
1658
|
+
].join("\n");
|
|
1659
|
+
}
|
|
1660
|
+
/** The record predicate itself: identical for a plain record and a generic one but for the arguments it carries. */
|
|
1661
|
+
recordCheckFunctionLines(statement, fields, predicate, checkName, indentation, argumentsParameter) {
|
|
1662
|
+
// The per-value cycle guard is keyed by the *instantiation*, not by the
|
|
1663
|
+
// function: `Tree<string>` and `Tree<number>` share one predicate, and a
|
|
1664
|
+
// value reached under both in one traversal is two questions, not one.
|
|
1665
|
+
const guard = argumentsParameter ? "__velarArguments" : checkName;
|
|
1666
|
+
return [
|
|
1667
|
+
`${indentation}function ${checkName}(value, __state = __velarValidationState()${argumentsParameter}) {`,
|
|
1668
|
+
// D44 rule 70: a record contract accepts only plain data objects, so a
|
|
1669
|
+
// class instance can never satisfy it — otherwise the validated record
|
|
1670
|
+
// view would alias the live instance and write through its const fields.
|
|
1671
|
+
`${indentation} if (value === null || typeof value !== "object" || __velarValidationIsArray(value) || !__velarValidationIsPlainObject(value) || __state.depth >= 1000) return false;`,
|
|
1672
|
+
`${indentation} let __active = __velarValidationWeakMapGet(__state.active, value);`,
|
|
1673
|
+
`${indentation} if (__active && __velarValidationSetHas(__active, ${guard})) return false;`,
|
|
1674
|
+
`${indentation} if (!__active) {`,
|
|
1675
|
+
`${indentation} __active = __velarValidationSet();`,
|
|
1676
|
+
`${indentation} __velarValidationWeakMapSet(__state.active, value, __active);`,
|
|
1677
|
+
`${indentation} }`,
|
|
1678
|
+
`${indentation} __velarValidationSetAdd(__active, ${guard});`,
|
|
1679
|
+
`${indentation} __state.depth += 1;`,
|
|
1680
|
+
`${indentation} try {`,
|
|
1681
|
+
...fields.map(({ field, descriptor }) => `${indentation} const ${descriptor} = __velarValidationOwnDescriptor(value, ${JSON.stringify(field.name)});`),
|
|
1682
|
+
`${indentation} return !!(${predicate});`,
|
|
1683
|
+
`${indentation} } finally {`,
|
|
1684
|
+
`${indentation} __state.depth -= 1;`,
|
|
1685
|
+
`${indentation} __velarValidationSetDelete(__active, ${guard});`,
|
|
1686
|
+
`${indentation} if (__velarValidationSetSize(__active) === 0) __velarValidationWeakMapDelete(__state.active, value);`,
|
|
1687
|
+
`${indentation} }`,
|
|
1688
|
+
`${indentation}}`,
|
|
1689
|
+
];
|
|
1690
|
+
}
|
|
1691
|
+
emitTypeAliasDeclaration(statement, depth) {
|
|
1692
|
+
const indentation = " ".repeat(depth);
|
|
1693
|
+
// ENM-I4: identities follow aliases, so an alias whose target resolves to
|
|
1694
|
+
// an enum IS that enum object at runtime — members, is, parse, and
|
|
1695
|
+
// values() all answer through the one frozen object.
|
|
1696
|
+
const enumTarget = this.enumAliasTarget(statement.name);
|
|
1697
|
+
if (enumTarget !== null) {
|
|
1698
|
+
return `${indentation}${statement.exported ? "export " : ""}const ${statement.name} = ${enumTarget};`;
|
|
1699
|
+
}
|
|
1700
|
+
// D55 rule 123 on ENM-I4's precedent: naming an instantiation is *the*
|
|
1701
|
+
// idiom that gives a generic record a runtime Type object, so the name IS
|
|
1702
|
+
// that instantiation's Type object rather than a wrapper around it. One
|
|
1703
|
+
// object per instantiation program-wide, and `parse` answers with the
|
|
1704
|
+
// record's own per-field explanation instead of a bare refusal.
|
|
1705
|
+
const target = resolveTypeReference(statement.target);
|
|
1706
|
+
if (target.kind === "named" && target.application && this.genericTypeBinding(target.application.name)) {
|
|
1707
|
+
this.needsRuntimeTypeHelpers = true;
|
|
1708
|
+
return `${indentation}${statement.exported ? "export " : ""}const ${statement.name} = ${this.genericInstanceExpression(target.application)};`;
|
|
1709
|
+
}
|
|
1710
|
+
const checkName = this.runtimeTypeCheckName(statement.name);
|
|
1711
|
+
const predicate = this.emitTypeCheck(resolveTypeReference(statement.target), "value", "__state");
|
|
1712
|
+
const exportPrefix = statement.exported ? "export " : "";
|
|
1713
|
+
return [
|
|
1714
|
+
`${indentation}function ${checkName}(value, __state = __velarValidationState()) {`,
|
|
1715
|
+
`${indentation} return ${predicate};`,
|
|
1716
|
+
`${indentation}}`,
|
|
1717
|
+
"",
|
|
1718
|
+
`${indentation}${exportPrefix}const ${statement.name} = __velarRegisterRuntimeType(__velarValidationFreeze({`,
|
|
1719
|
+
`${indentation} is(value, __state) {`,
|
|
1720
|
+
`${indentation} return ${checkName}(value, __state);`,
|
|
1721
|
+
`${indentation} },`,
|
|
1722
|
+
`${indentation} parse(value) {`,
|
|
1723
|
+
`${indentation} if (!${checkName}(value)) {`,
|
|
1724
|
+
`${indentation} throw new __VelarValidationError(${JSON.stringify(`Value does not match ${statement.name}`)}, { path: ${JSON.stringify(statement.name)} });`,
|
|
1725
|
+
`${indentation} }`,
|
|
1726
|
+
`${indentation} return value;`,
|
|
1727
|
+
`${indentation} },`,
|
|
1728
|
+
`${indentation}}));`,
|
|
1729
|
+
].join("\n");
|
|
1730
|
+
}
|
|
1731
|
+
emitEnumDeclaration(statement, depth) {
|
|
1732
|
+
const indentation = " ".repeat(depth);
|
|
1733
|
+
const values = statement.members.map((member) => JSON.stringify(member.value));
|
|
1734
|
+
const members = statement.members.map((member) => `${indentation} ${member.name}: ${JSON.stringify(member.value)},`);
|
|
1735
|
+
const predicate = values.length === 1
|
|
1736
|
+
? `value === ${values[0]}`
|
|
1737
|
+
: values.map((value) => `value === ${value}`).join(" || ");
|
|
1738
|
+
return [
|
|
1739
|
+
`${indentation}${statement.exported ? "export " : ""}const ${statement.name} = __velarRegisterRuntimeType(__velarValidationFreeze({`,
|
|
1740
|
+
...members,
|
|
1741
|
+
`${indentation} is(value) {`,
|
|
1742
|
+
`${indentation} return ${predicate};`,
|
|
1743
|
+
`${indentation} },`,
|
|
1744
|
+
`${indentation} parse(value) {`,
|
|
1745
|
+
`${indentation} if (!${statement.name}.is(value)) {`,
|
|
1746
|
+
`${indentation} throw new __VelarValidationError(${JSON.stringify(`Value does not match ${statement.name}`)}, { path: ${JSON.stringify(statement.name)} });`,
|
|
1747
|
+
`${indentation} }`,
|
|
1748
|
+
`${indentation} return value;`,
|
|
1749
|
+
`${indentation} },`,
|
|
1750
|
+
// ENM-U1: the members in declaration order, a fresh mutable List per call.
|
|
1751
|
+
`${indentation} values() {`,
|
|
1752
|
+
`${indentation} return [${values.join(", ")}];`,
|
|
1753
|
+
`${indentation} },`,
|
|
1754
|
+
`${indentation}}));`,
|
|
1755
|
+
].join("\n");
|
|
1756
|
+
}
|
|
1757
|
+
emitTypeCheck(type, value, state = "undefined") {
|
|
1758
|
+
switch (type.kind) {
|
|
1759
|
+
case "unknown":
|
|
1760
|
+
case "any":
|
|
1761
|
+
return "true";
|
|
1762
|
+
case "null":
|
|
1763
|
+
return `${value} == null`;
|
|
1764
|
+
case "string":
|
|
1765
|
+
case "number":
|
|
1766
|
+
case "bool":
|
|
1767
|
+
return `typeof ${value} === ${JSON.stringify(type.kind === "bool" ? "boolean" : type.kind)}`;
|
|
1768
|
+
case "optional":
|
|
1769
|
+
return `(${value} == null || ${this.emitTypeCheck(type.inner, value, state)})`;
|
|
1770
|
+
case "list":
|
|
1771
|
+
return `__velarListTypeIs(${value}, (item) => ${this.emitTypeCheck(type.element, "item", state)})`;
|
|
1772
|
+
case "set":
|
|
1773
|
+
return `__velarSetTypeIs(${value}, (item) => ${this.emitTypeCheck(type.element, "item", state)})`;
|
|
1774
|
+
case "map":
|
|
1775
|
+
return `__velarMapTypeIs(${value}, (key, item) => ${this.emitTypeCheck(type.key, "key", state)} && ${this.emitTypeCheck(type.value, "item", state)})`;
|
|
1776
|
+
case "record":
|
|
1777
|
+
return `__velarRecordTypeIs(${value}, (item) => ${this.emitTypeCheck(type.value, "item", state)})`;
|
|
1778
|
+
case "promise":
|
|
1779
|
+
return `__velarValidationIsPromise(${value})`;
|
|
1780
|
+
case "named":
|
|
1781
|
+
// D55 rule 121: an instantiation's validator is the declaration's,
|
|
1782
|
+
// supplied with this application's argument predicates. The factory
|
|
1783
|
+
// memoizes, so the object is built once however many times it is asked
|
|
1784
|
+
// for — and a recursive record's reference to itself is a memo hit.
|
|
1785
|
+
if (type.application && this.genericTypeBinding(type.application.name)) {
|
|
1786
|
+
this.needsRuntimeTypeHelpers = true;
|
|
1787
|
+
return `${this.genericInstanceExpression(type.application)}.is(${value}, ${state})`;
|
|
1788
|
+
}
|
|
1789
|
+
if (type.name === "Duration")
|
|
1790
|
+
return `typeof ${value} === "string" && /^[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:ms|s)$/.test(${value})`;
|
|
1791
|
+
if (this.hints.enumNames.has(type.name))
|
|
1792
|
+
return `${type.name}.is(${value})`;
|
|
1793
|
+
if (this.hints.classNames.has(type.name)) {
|
|
1794
|
+
return `__velarValidationIsInstance(${value}, ${this.builtinErrorRuntimeName(type.name) ?? type.name})`;
|
|
1795
|
+
}
|
|
1796
|
+
// An alias of an enum is lowered as the enum object itself, so its
|
|
1797
|
+
// check delegates the same way a direct enum name does (ENM-I4).
|
|
1798
|
+
if (this.enumAliasTarget(type.name) !== null)
|
|
1799
|
+
return `${type.name}.is(${value})`;
|
|
1800
|
+
if (this.typeDeclarations.has(type.name))
|
|
1801
|
+
return `${this.runtimeTypeCheckName(type.name)}(${value}, ${state})`;
|
|
1802
|
+
// D60 rule 148: only a name that actually binds a runtime Type object
|
|
1803
|
+
// may be written into the output. See `runtimeTypeBinding`.
|
|
1804
|
+
return this.runtimeTypeBinding(type.name) ? `${type.name}.is(${value}, ${state})` : "false";
|
|
1805
|
+
case "class":
|
|
1806
|
+
return `__velarValidationIsInstance(${value}, ${this.builtinErrorRuntimeName(type.name) ?? type.name})`;
|
|
1807
|
+
case "enum":
|
|
1808
|
+
return `${type.name}.is(${value})`;
|
|
1809
|
+
case "enumMember":
|
|
1810
|
+
return `${value} === ${type.name}.${type.member}`;
|
|
1811
|
+
case "union":
|
|
1812
|
+
return `(${type.members.map((member) => this.emitTypeCheck(member, value, state)).join(" || ")})`;
|
|
1813
|
+
case "object":
|
|
1814
|
+
return `${value} !== null && typeof ${value} === "object"`;
|
|
1815
|
+
case "function":
|
|
1816
|
+
case "action":
|
|
1817
|
+
case "intrinsic":
|
|
1818
|
+
return `typeof ${value} === "function"`;
|
|
1819
|
+
// D55 rule 121: inside a generic record's own validator a type parameter
|
|
1820
|
+
// is not unknowable — the instantiation handed in the predicate for it.
|
|
1821
|
+
case "parameter":
|
|
1822
|
+
return this.genericTypeParameters?.length
|
|
1823
|
+
? `__velarArguments.checks[${type.index}](${value}, ${state === "undefined" ? "__velarValidationState()" : state})`
|
|
1824
|
+
: "false";
|
|
1825
|
+
case "typeObject":
|
|
1826
|
+
case "runtimeType":
|
|
1827
|
+
case "enumObject":
|
|
1828
|
+
case "classConstructor":
|
|
1829
|
+
case "extension":
|
|
1830
|
+
// Static Type<T> carriers are erased; the analyzer rejects them in any
|
|
1831
|
+
// recursively runtime-checked position before emission can happen.
|
|
1832
|
+
return "false";
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
emitIsCheck(type, value) {
|
|
1836
|
+
if (type.kind === "named" && type.name === "Duration")
|
|
1837
|
+
return `typeof ${value} === "string" && /^[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:ms|s)$/.test(${value})`;
|
|
1838
|
+
// D60 rule 148: a name with no runtime Type object behind it is not a
|
|
1839
|
+
// callable receiver, so the check falls through to the structural form
|
|
1840
|
+
// instead of naming a binding that does not exist.
|
|
1841
|
+
return type.kind === "named" && this.runtimeTypeBinding(type.name)
|
|
1842
|
+
? `${type.name}.is(${value})`
|
|
1843
|
+
: this.emitTypeCheck(type, value);
|
|
1844
|
+
}
|
|
1845
|
+
emitNarrowingCheck(type, value, state = "undefined") {
|
|
1846
|
+
switch (type.kind) {
|
|
1847
|
+
case "optional":
|
|
1848
|
+
return `(${value} == null || ${this.emitNarrowingCheck(type.inner, value, state)})`;
|
|
1849
|
+
case "list":
|
|
1850
|
+
return `__velarListTypeIs(${value}, (item) => ${this.emitNarrowingCheck(type.element, "item", state)})`;
|
|
1851
|
+
case "set":
|
|
1852
|
+
return `__velarSetTypeIs(${value}, (item) => ${this.emitNarrowingCheck(type.element, "item", state)})`;
|
|
1853
|
+
case "map":
|
|
1854
|
+
return `__velarMapTypeIs(${value}, (key, item) => ${this.emitNarrowingCheck(type.key, "key", state)} && ${this.emitNarrowingCheck(type.value, "item", state)})`;
|
|
1855
|
+
case "record":
|
|
1856
|
+
return `__velarRecordTypeIs(${value}, (item) => ${this.emitNarrowingCheck(type.value, "item", state)})`;
|
|
1857
|
+
case "union":
|
|
1858
|
+
return `(${type.members.map((member) => this.emitNarrowingCheck(member, value, state)).join(" || ")})`;
|
|
1859
|
+
case "named":
|
|
1860
|
+
// FLW-U1: an imported record type (or alias) is not in this module's
|
|
1861
|
+
// typeDeclarations, but its runtime Type object is an in-scope
|
|
1862
|
+
// binding, so the recheck routes through `Name.is(value)` exactly as
|
|
1863
|
+
// `is` tests already do. Only names with no runtime Type binding at
|
|
1864
|
+
// all — extension host types such as DOM interfaces — degrade to the
|
|
1865
|
+
// presence-only check.
|
|
1866
|
+
if (type.application && this.genericTypeBinding(type.application.name))
|
|
1867
|
+
return this.emitTypeCheck(type, value, state);
|
|
1868
|
+
if (!this.runtimeTypeBinding(type.name))
|
|
1869
|
+
return `${value} != null`;
|
|
1870
|
+
return this.emitTypeCheck(type, value, state);
|
|
1871
|
+
case "parameter":
|
|
1872
|
+
case "typeObject":
|
|
1873
|
+
case "runtimeType":
|
|
1874
|
+
case "enumObject":
|
|
1875
|
+
case "classConstructor":
|
|
1876
|
+
case "extension":
|
|
1877
|
+
return `${value} != null`;
|
|
1878
|
+
default:
|
|
1879
|
+
return this.emitTypeCheck(type, value, state);
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
runtimeTypeCheckName(name) {
|
|
1883
|
+
return `__velarTypeCheck_${name}`;
|
|
1884
|
+
}
|
|
1885
|
+
/**
|
|
1886
|
+
* D55 rule 121: the name a module writes for a generic record is a JavaScript
|
|
1887
|
+
* binding holding its instantiation factory — declared here, or imported from
|
|
1888
|
+
* the module that declares it. Nothing else may be written into `.of(...)`.
|
|
1889
|
+
*/
|
|
1890
|
+
genericTypeBinding(name) {
|
|
1891
|
+
if (this.hints.genericTypeNames.has(name))
|
|
1892
|
+
return true;
|
|
1893
|
+
const declaration = this.typeDeclarations.get(name);
|
|
1894
|
+
return declaration?.kind === "TypeDeclaration" && (declaration.typeParameters?.length ?? 0) > 0;
|
|
1895
|
+
}
|
|
1896
|
+
/** The instantiation a `named` application stands for, as a JavaScript expression. */
|
|
1897
|
+
genericInstanceExpression(application) {
|
|
1898
|
+
const keys = application.arguments.map((argument) => this.genericArgumentExpression(argument, "key"));
|
|
1899
|
+
const texts = application.arguments.map((argument) => this.genericArgumentExpression(argument, "text"));
|
|
1900
|
+
const checks = application.arguments.map((argument) => `(value, __state) => ${this.emitTypeCheck(argument, "value", "__state")}`);
|
|
1901
|
+
const expression = `${application.name}.of([${keys.join(", ")}], [${texts.join(", ")}], [${checks.join(", ")}])`;
|
|
1902
|
+
// Outside a generic body the arguments are closed, so the whole
|
|
1903
|
+
// instantiation is hoisted into one memoized function: a `function`
|
|
1904
|
+
// declaration, which hoists past the temporal dead zone a `const` would
|
|
1905
|
+
// create for `type Boxed = Box<string>` written above the declaration.
|
|
1906
|
+
if (this.genericTypeParameters?.length)
|
|
1907
|
+
return expression;
|
|
1908
|
+
const hoisted = this.hoistedGenericInstances.get(expression)
|
|
1909
|
+
?? `__velarTypeOf${this.hoistedGenericInstances.size}`;
|
|
1910
|
+
this.hoistedGenericInstances.set(expression, hoisted);
|
|
1911
|
+
return `${hoisted}()`;
|
|
1912
|
+
}
|
|
1913
|
+
/**
|
|
1914
|
+
* A type argument's memo key or display text. Both are plain strings when the
|
|
1915
|
+
* argument is closed; inside a generic body a mention of the enclosing
|
|
1916
|
+
* parameters reads them off the arguments the instantiation supplied, so
|
|
1917
|
+
* `type Wrapper<T>: inner: Box<List<T>>` keys and prints correctly at every
|
|
1918
|
+
* instantiation without the emitter having seen one.
|
|
1919
|
+
*/
|
|
1920
|
+
genericArgumentExpression(type, mode) {
|
|
1921
|
+
const parameters = this.genericTypeParameters;
|
|
1922
|
+
if (!parameters?.length || !typeContainsParameter(type)) {
|
|
1923
|
+
return JSON.stringify(mode === "key" ? semanticTypeIdentity(type) : describeType(type));
|
|
1924
|
+
}
|
|
1925
|
+
const nested = (value) => this.genericArgumentExpression(value, mode);
|
|
1926
|
+
const wrap = (prefix, parts, suffix) => [JSON.stringify(prefix), ...parts.flatMap((part, index) => index === 0 ? [part] : [JSON.stringify(mode === "key" ? "," : ", "), part]), JSON.stringify(suffix)].join(" + ");
|
|
1927
|
+
switch (type.kind) {
|
|
1928
|
+
case "parameter":
|
|
1929
|
+
return `__velarArguments.${mode === "key" ? "keys" : "texts"}[${type.index}]`;
|
|
1930
|
+
case "optional":
|
|
1931
|
+
return `${nested(type.inner)} + ${JSON.stringify("?")}`;
|
|
1932
|
+
case "list":
|
|
1933
|
+
return wrap("List<", [nested(type.element)], ">");
|
|
1934
|
+
case "set":
|
|
1935
|
+
return wrap("Set<", [nested(type.element)], ">");
|
|
1936
|
+
case "map":
|
|
1937
|
+
return wrap("Map<", [nested(type.key), nested(type.value)], ">");
|
|
1938
|
+
case "record":
|
|
1939
|
+
return wrap("Record<", [nested(type.value)], ">");
|
|
1940
|
+
case "promise":
|
|
1941
|
+
return wrap("Promise<", [nested(type.value)], ">");
|
|
1942
|
+
case "named":
|
|
1943
|
+
return type.application
|
|
1944
|
+
? wrap(`${type.application.name}<`, type.application.arguments.map(nested), ">")
|
|
1945
|
+
: JSON.stringify(mode === "key" ? semanticTypeIdentity(type) : describeType(type));
|
|
1946
|
+
case "union":
|
|
1947
|
+
return type.members.map(nested).join(` + ${JSON.stringify(mode === "key" ? "|" : " | ")} + `);
|
|
1948
|
+
default:
|
|
1949
|
+
return JSON.stringify(mode === "key" ? semanticTypeIdentity(type) : describeType(type));
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* The display text of a field's declared type, as a JavaScript expression.
|
|
1954
|
+
* A generic record's `parse` failure names the type the caller instantiated
|
|
1955
|
+
* — `field 'value' does not match string`, not `does not match T`.
|
|
1956
|
+
*/
|
|
1957
|
+
typeTextExpression(type, syntax) {
|
|
1958
|
+
return this.genericTypeParameters?.length
|
|
1959
|
+
? this.genericArgumentExpression(type, "text")
|
|
1960
|
+
: JSON.stringify(formatTypeSyntax(syntax));
|
|
1961
|
+
}
|
|
1962
|
+
/**
|
|
1963
|
+
* A declared type inside a generic record's body. The emitter has no analyzer
|
|
1964
|
+
* frame, so the declaration's own parameter names are turned into `parameter`
|
|
1965
|
+
* kinds here — the one place the emitter learns that `T` is erased rather
|
|
1966
|
+
* than unknown.
|
|
1967
|
+
*/
|
|
1968
|
+
resolveDeclarationType(reference) {
|
|
1969
|
+
const parameters = this.genericTypeParameters;
|
|
1970
|
+
const resolved = resolveTypeReference(reference);
|
|
1971
|
+
if (!parameters?.length)
|
|
1972
|
+
return resolved;
|
|
1973
|
+
const bindParameters = (type) => {
|
|
1974
|
+
if (type.kind === "named" && !type.application) {
|
|
1975
|
+
const index = parameters.indexOf(type.name);
|
|
1976
|
+
if (index >= 0)
|
|
1977
|
+
return { kind: "parameter", name: type.name, index };
|
|
1978
|
+
}
|
|
1979
|
+
return mapNestedTypes(type, bindParameters);
|
|
1980
|
+
};
|
|
1981
|
+
return bindParameters(resolved);
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* D60 rule 148: a `named` ValueType carries the type's *display* name, which
|
|
1985
|
+
* is a runtime binding only when this module really has a Type object under
|
|
1986
|
+
* it — a local `type` declaration, an imported one, an enum, or a class. An
|
|
1987
|
+
* unresolved generic formats to type text (`Component<(label: string) ->
|
|
1988
|
+
* WebNode>`) that is not even a JavaScript identifier, and an extension host
|
|
1989
|
+
* scalar (`Color`, `Length`, `WebNode`) has no binding at all. Writing either
|
|
1990
|
+
* into the output is how `velar check` passed and `velar build` then failed
|
|
1991
|
+
* to parse its own emission; the narrowing path (FLW-U1) already asks this
|
|
1992
|
+
* question, and every other check path now asks it too.
|
|
1993
|
+
*/
|
|
1994
|
+
runtimeTypeBinding(name) {
|
|
1995
|
+
return this.hints.enumNames.has(name)
|
|
1996
|
+
|| this.hints.classNames.has(name)
|
|
1997
|
+
|| this.typeDeclarations.has(name)
|
|
1998
|
+
|| this.hints.runtimeTypeObjectNames.has(name);
|
|
1999
|
+
}
|
|
2000
|
+
/** The runtime class behind a nameable builtin error type, marking the runtime it needs. */
|
|
2001
|
+
builtinErrorRuntimeName(name) {
|
|
2002
|
+
if (VELAR_HOST_ERROR_NAMES.includes(name)) {
|
|
2003
|
+
this.requiredHostErrorClasses.add(name);
|
|
2004
|
+
return `__Velar${name}`;
|
|
2005
|
+
}
|
|
2006
|
+
const runtime = builtinErrorRuntimeNames.get(name);
|
|
2007
|
+
if (!runtime)
|
|
2008
|
+
return null;
|
|
2009
|
+
if (name === "ValidationError")
|
|
2010
|
+
this.needsRuntimeTypeHelpers = true;
|
|
2011
|
+
else if (name === "NarrowingError")
|
|
2012
|
+
this.needsNarrowingErrorClass = true;
|
|
2013
|
+
else
|
|
2014
|
+
this.needsCollectionHelpers = true;
|
|
2015
|
+
return runtime;
|
|
2016
|
+
}
|
|
2017
|
+
/** The enum an alias (or alias chain) resolves to, or null when the name is not an alias of an enum. */
|
|
2018
|
+
enumAliasTarget(name, seen = []) {
|
|
2019
|
+
if (seen.includes(name))
|
|
2020
|
+
return null;
|
|
2021
|
+
const declaration = this.typeDeclarations.get(name);
|
|
2022
|
+
if (!declaration || declaration.kind !== "TypeAliasDeclaration")
|
|
2023
|
+
return null;
|
|
2024
|
+
const target = resolveTypeReference(declaration.target);
|
|
2025
|
+
if (target.kind !== "named")
|
|
2026
|
+
return null;
|
|
2027
|
+
if (this.hints.enumNames.has(target.name))
|
|
2028
|
+
return target.name;
|
|
2029
|
+
return this.enumAliasTarget(target.name, [...seen, name]);
|
|
2030
|
+
}
|
|
2031
|
+
emitClass(statement, depth) {
|
|
2032
|
+
const indentation = " ".repeat(depth);
|
|
2033
|
+
const parameters = statement.parameters.map((parameter) => this.emitParameter(parameter.name, parameter.defaultValue, parameter.rest)).join(", ");
|
|
2034
|
+
const constructorLines = [];
|
|
2035
|
+
const constructorBody = [...(statement.initialization?.body ?? [])];
|
|
2036
|
+
const explicitSuper = constructorBody[0]?.kind === "ExpressionStatement"
|
|
2037
|
+
&& constructorBody[0].expression.kind === "CallExpression"
|
|
2038
|
+
&& constructorBody[0].expression.callee.kind === "SuperExpression";
|
|
2039
|
+
if (statement.base) {
|
|
2040
|
+
constructorLines.push(explicitSuper
|
|
2041
|
+
? this.emitMappedStatement(constructorBody.shift(), depth + 2)
|
|
2042
|
+
: `${indentation} super();`);
|
|
2043
|
+
}
|
|
2044
|
+
// Error subclasses report under their declared name — the JavaScript
|
|
2045
|
+
// default leaves `.name` at "Error", which mislabels every report header
|
|
2046
|
+
// (audit 4's micro-ruling).
|
|
2047
|
+
if (statement.base && this.hints.errorSubclassNames.has(statement.name)) {
|
|
2048
|
+
constructorLines.push(`${indentation} this.name = ${JSON.stringify(statement.name)};`);
|
|
2049
|
+
}
|
|
2050
|
+
for (const parameter of statement.parameters) {
|
|
2051
|
+
if (parameter.binding) {
|
|
2052
|
+
constructorLines.push(`${indentation} this.${parameter.private ? "#" : ""}${parameter.name} = ${parameter.name};`);
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
for (const field of statement.fields) {
|
|
2056
|
+
if (!field.static && field.initializer)
|
|
2057
|
+
constructorLines.push(`${indentation} this.${field.private ? "#" : ""}${field.name} = ${this.emitMappedExpression(field.initializer)};`);
|
|
2058
|
+
}
|
|
2059
|
+
if (statement.initialization) {
|
|
2060
|
+
constructorLines.push(`${indentation} const self = this;`);
|
|
2061
|
+
constructorLines.push(...this.emitStatementLines(constructorBody, depth + 2));
|
|
2062
|
+
}
|
|
2063
|
+
const constructor = [
|
|
2064
|
+
`${indentation} constructor(${parameters}) {`,
|
|
2065
|
+
...constructorLines,
|
|
2066
|
+
`${indentation} }`,
|
|
2067
|
+
].join("\n");
|
|
2068
|
+
const methodBody = (method, methodDepth) => {
|
|
2069
|
+
const lines = method.abstract
|
|
2070
|
+
? [`${" ".repeat(methodDepth)}throw new Error(${JSON.stringify(`Abstract ${"accessor" in method ? "getter" : "method"} ${statement.name}.${method.name}${"accessor" in method ? "" : "()"} must be implemented`)});`]
|
|
2071
|
+
: [
|
|
2072
|
+
...(method.static ? [] : [`${" ".repeat(methodDepth)}const self = this;`]),
|
|
2073
|
+
...this.emitStatementLines(method.body, methodDepth),
|
|
2074
|
+
];
|
|
2075
|
+
if (!method.abstract && !this.blockAlwaysReturns(method.body))
|
|
2076
|
+
lines.push(`${" ".repeat(methodDepth)}return null;`);
|
|
2077
|
+
return lines;
|
|
2078
|
+
};
|
|
2079
|
+
// Methods — public, static, and private alike — live on the class body as
|
|
2080
|
+
// native (private) methods, so instances carry data fields only and one
|
|
2081
|
+
// method object serves every instance (charter section 18).
|
|
2082
|
+
const methods = statement.methods.map((method) => {
|
|
2083
|
+
const methodParameters = method.parameters.map((parameter) => this.emitParameter(parameter.name, parameter.defaultValue, parameter.rest)).join(", ");
|
|
2084
|
+
const lines = methodBody(method, depth + 2);
|
|
2085
|
+
const body = lines.join("\n");
|
|
2086
|
+
return `${indentation} ${method.static ? "static " : ""}${method.asynchronous ? "async " : ""}${method.private ? "#" : ""}${method.name}(${methodParameters}) {${body.length > 0 ? `\n${body}\n${indentation} ` : ""}}`;
|
|
2087
|
+
});
|
|
2088
|
+
const getters = statement.getters.map((getter) => {
|
|
2089
|
+
const lines = methodBody(getter, depth + 2);
|
|
2090
|
+
const body = lines.join("\n");
|
|
2091
|
+
return `${indentation} ${getter.static ? "static " : ""}get ${getter.private ? "#" : ""}${getter.name}() {${body.length > 0 ? `\n${body}\n${indentation} ` : ""}}`;
|
|
2092
|
+
});
|
|
2093
|
+
const privateFields = [
|
|
2094
|
+
...statement.parameters.filter((parameter) => parameter.private).map((parameter) => parameter.name),
|
|
2095
|
+
...statement.fields.filter((field) => field.private && !field.static).map((field) => field.name),
|
|
2096
|
+
].map((name) => `${indentation} #${name};`);
|
|
2097
|
+
const staticFields = statement.fields
|
|
2098
|
+
.filter((field) => field.static)
|
|
2099
|
+
.map((field) => `${indentation} static ${field.private ? "#" : ""}${field.name} = ${field.initializer ? this.emitMappedExpression(field.initializer) : "null"};`);
|
|
2100
|
+
// D43 item 69: `@dispose:` becomes one prototype member under a key no
|
|
2101
|
+
// source member name can spell, so the release contract cannot be called
|
|
2102
|
+
// from source and cannot collide with a member the author declares.
|
|
2103
|
+
const dispose = [];
|
|
2104
|
+
if (statement.dispose) {
|
|
2105
|
+
// D51 rule 102: a derived `@dispose:` adds to the base's, it does not
|
|
2106
|
+
// replace it. The compiler composes the contract because `@dispose` is
|
|
2107
|
+
// not callable from source (D43 item 69), so an author could not forward
|
|
2108
|
+
// it by hand even if every author remembered to.
|
|
2109
|
+
const chained = this.hints.classDisposeChains.get(spanIdentity(statement.span)) ?? null;
|
|
2110
|
+
const disposeDepth = depth + (chained ? 3 : 2);
|
|
2111
|
+
const indent = " ".repeat(disposeDepth);
|
|
2112
|
+
const asynchronous = chained === "async"
|
|
2113
|
+
|| blockContainsDirectAwait(statement.dispose.body, (value, contains) => this.extensionExpressionContainsDirectAwait(value, contains), (owned, containsExpression, containsBlock) => this.extensionStatementContainsDirectAwait(owned, containsExpression, containsBlock));
|
|
2114
|
+
const body = [
|
|
2115
|
+
`${indent}const self = this;`,
|
|
2116
|
+
...this.emitStatementLines(statement.dispose.body, disposeDepth),
|
|
2117
|
+
`${indent}return null;`,
|
|
2118
|
+
];
|
|
2119
|
+
const lines = chained ? this.chainedDisposeLines(body, chained, statement.span.start, depth + 2) : body;
|
|
2120
|
+
dispose.push(`${indentation} ${asynchronous ? "async " : ""}[${JSON.stringify(disposeMemberKey)}]() {\n${lines.join("\n")}\n${indentation} }`);
|
|
2121
|
+
}
|
|
2122
|
+
// D68 rule 177: `@iterate:` lands the same way, under its own unspellable
|
|
2123
|
+
// key. It is a plain prototype member, so a derived block simply shadows
|
|
2124
|
+
// the base's — overriding replaces, which is what "one answer" means.
|
|
2125
|
+
const iterate = [];
|
|
2126
|
+
if (statement.iterate) {
|
|
2127
|
+
const iterateDepth = depth + 2;
|
|
2128
|
+
const indent = " ".repeat(iterateDepth);
|
|
2129
|
+
const body = [
|
|
2130
|
+
`${indent}const self = this;`,
|
|
2131
|
+
...this.emitStatementLines(statement.iterate.body, iterateDepth),
|
|
2132
|
+
`${indent}return null;`,
|
|
2133
|
+
];
|
|
2134
|
+
iterate.push(`${indentation} [${JSON.stringify(iterateMemberKey)}]() {\n${body.join("\n")}\n${indentation} }`);
|
|
2135
|
+
}
|
|
2136
|
+
const extension = statement.base ? ` extends ${statement.base.name}` : "";
|
|
2137
|
+
return `${indentation}${statement.exported ? "export " : ""}class ${statement.name}${extension} {\n${[...privateFields, ...staticFields, constructor, ...getters, ...methods, ...dispose, ...iterate].join("\n\n")}\n${indentation}}`;
|
|
2138
|
+
}
|
|
2139
|
+
/**
|
|
2140
|
+
* D51 rule 102: derived first, base after — construction order reversed, the
|
|
2141
|
+
* same intuition LIFO release already has. The base runs on every exit from
|
|
2142
|
+
* the derived body, including a `return`, and when the derived part already
|
|
2143
|
+
* failed the base failure is reported to the host instead of replacing the
|
|
2144
|
+
* error in flight, exactly as rule 8 of D43 item 69 decides for `using`.
|
|
2145
|
+
*/
|
|
2146
|
+
chainedDisposeLines(body, inherited, suffix, depth) {
|
|
2147
|
+
const indentation = " ".repeat(depth);
|
|
2148
|
+
this.needsDisposalHelper = true;
|
|
2149
|
+
this.needsThrownValueHelper = true;
|
|
2150
|
+
const call = `${inherited === "async" ? "await " : ""}super[${JSON.stringify(disposeMemberKey)}]()`;
|
|
2151
|
+
const released = `__velarBaseReleased${suffix}`;
|
|
2152
|
+
const failure = `__velarDisposeChainFailure${suffix}`;
|
|
2153
|
+
return [
|
|
2154
|
+
`${indentation}let ${released} = false;`,
|
|
2155
|
+
`${indentation}try {`,
|
|
2156
|
+
...body,
|
|
2157
|
+
`${indentation}} catch (${failure}) {`,
|
|
2158
|
+
`${indentation} ${released} = true;`,
|
|
2159
|
+
`${indentation} try { ${call}; } catch (__velarBaseDisposeFailure${suffix}) { __velarDisposalReport(__velarBaseDisposeFailure${suffix}); }`,
|
|
2160
|
+
`${indentation} throw ${failure};`,
|
|
2161
|
+
`${indentation}} finally {`,
|
|
2162
|
+
`${indentation} if (!${released}) ${call};`,
|
|
2163
|
+
`${indentation}}`,
|
|
2164
|
+
];
|
|
2165
|
+
}
|
|
2166
|
+
emitParameter(name, defaultValue, rest = false) {
|
|
2167
|
+
if (rest)
|
|
2168
|
+
return `...${name}`;
|
|
2169
|
+
return defaultValue ? `${name} = ${this.emitMappedExpression(defaultValue)}` : name;
|
|
2170
|
+
}
|
|
2171
|
+
emitMappedExpression(expression, normalizeNull = true) {
|
|
2172
|
+
return this.emitMappedJavaScript(expression.span, () => {
|
|
2173
|
+
const key = spanIdentity(expression.span);
|
|
2174
|
+
const normalizePromise = normalizeNull
|
|
2175
|
+
&& this.suppressPromiseNormalization === 0
|
|
2176
|
+
&& this.hints.normalizedPromiseValues.has(key);
|
|
2177
|
+
if (normalizePromise)
|
|
2178
|
+
this.suppressPromiseNormalization += 1;
|
|
2179
|
+
let emitted;
|
|
2180
|
+
try {
|
|
2181
|
+
emitted = this.emitExpression(expression);
|
|
2182
|
+
}
|
|
2183
|
+
finally {
|
|
2184
|
+
if (normalizePromise)
|
|
2185
|
+
this.suppressPromiseNormalization -= 1;
|
|
2186
|
+
}
|
|
2187
|
+
const narrowing = this.hints.runtimeNarrowings.get(key);
|
|
2188
|
+
if (narrowing) {
|
|
2189
|
+
emitted = `(__velarValue => __velarNarrow(__velarValue, ${this.emitNarrowingCheck(narrowing.expected, "__velarValue")}, ${JSON.stringify(describeType(narrowing.expected))}, ${JSON.stringify(narrowing.description)}, ${expression.span.start}))(${emitted})`;
|
|
2190
|
+
}
|
|
2191
|
+
// D68 rule 177: the projection lives here rather than in each of the
|
|
2192
|
+
// eight consumers, because "iterating a class means iterating what
|
|
2193
|
+
// `@iterate:` answers" is one fact about the operand, and the consumers'
|
|
2194
|
+
// own lowerings then receive the List, Set, Map, or Record they already
|
|
2195
|
+
// knew how to handle.
|
|
2196
|
+
if (this.hints.iterationContracts.has(key))
|
|
2197
|
+
emitted = `(${emitted})[${JSON.stringify(iterateMemberKey)}]()`;
|
|
2198
|
+
if (!normalizeNull)
|
|
2199
|
+
return emitted;
|
|
2200
|
+
if (normalizePromise)
|
|
2201
|
+
return `__velarNormalizePromiseValue(${emitted})`;
|
|
2202
|
+
if (this.hints.normalizedNullResults.has(key))
|
|
2203
|
+
return `(${emitted}, null)`;
|
|
2204
|
+
if (this.hints.normalizedUndefinedExpressions.has(key))
|
|
2205
|
+
return `(${emitted} ?? null)`;
|
|
2206
|
+
return emitted;
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
emitMappedAssignmentTarget(expression) {
|
|
2210
|
+
return this.emitMappedJavaScript(expression.span, () => {
|
|
2211
|
+
if (expression.kind === "IdentifierExpression")
|
|
2212
|
+
return this.emitExpression(expression);
|
|
2213
|
+
const property = `${this.hints.privateMembers.has(spanIdentity(expression.span)) ? "#" : ""}${expression.property}`;
|
|
2214
|
+
return `${this.emitPostfixReceiver(expression.object)}.${property}`;
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2217
|
+
bitwiseAssignmentOperator(operator) {
|
|
2218
|
+
return operator === "&=" || operator === "|=" || operator === "^=" || operator === "<<=" || operator === ">>=" || operator === ">>>="
|
|
2219
|
+
? operator.slice(0, -1)
|
|
2220
|
+
: null;
|
|
2221
|
+
}
|
|
2222
|
+
emitCompoundOperation(read, operator, value) {
|
|
2223
|
+
const bitwise = this.bitwiseAssignmentOperator(operator);
|
|
2224
|
+
if (bitwise) {
|
|
2225
|
+
this.needsBitwiseHelpers = true;
|
|
2226
|
+
return `__velarBitwiseBinary(${read}, ${JSON.stringify(bitwise)}, ${value})`;
|
|
2227
|
+
}
|
|
2228
|
+
return `${read} ${operator.slice(0, -1)} ${value}`;
|
|
2229
|
+
}
|
|
2230
|
+
emitExpression(expression) {
|
|
2231
|
+
if (expression.kind === "ExtensionExpression:core:duration") {
|
|
2232
|
+
return JSON.stringify(expression.raw);
|
|
2233
|
+
}
|
|
2234
|
+
if (expression.kind === "UnaryExpression" && (expression.operator === "+" || expression.operator === "-")
|
|
2235
|
+
&& this.hints.extensionCalls.get(spanIdentity(expression.span)) === "core.duration-arithmetic") {
|
|
2236
|
+
return `__velarDurationUnary(${JSON.stringify(expression.operator)}, ${this.emitMappedExpression(expression.operand)})`;
|
|
2237
|
+
}
|
|
2238
|
+
if (expression.kind === "BinaryExpression"
|
|
2239
|
+
&& this.hints.extensionCalls.get(spanIdentity(expression.span)) === "core.duration-arithmetic") {
|
|
2240
|
+
return `__velarDurationMath(${JSON.stringify(expression.operator)}, ${this.emitMappedExpression(expression.left)}, ${this.emitMappedExpression(expression.right)})`;
|
|
2241
|
+
}
|
|
2242
|
+
switch (expression.kind) {
|
|
2243
|
+
case "LiteralExpression":
|
|
2244
|
+
return expression.value === null ? "null" : typeof expression.value === "string" ? JSON.stringify(expression.value) : String(expression.value);
|
|
2245
|
+
case "FStringExpression":
|
|
2246
|
+
return `\`${expression.parts.map((part) => part.kind === "text" ? this.escapeTemplateText(part.value) : `\${${this.emitMappedExpression(part.value)}}`).join("")}\``;
|
|
2247
|
+
case "IdentifierExpression":
|
|
2248
|
+
{
|
|
2249
|
+
const builtin = this.hints.builtinValueReferences.get(spanIdentity(expression.span));
|
|
2250
|
+
if (builtin === "Json")
|
|
2251
|
+
return "__velarJsonNamespace";
|
|
2252
|
+
if (builtin === "Promise")
|
|
2253
|
+
return "__velarPromiseNamespace";
|
|
2254
|
+
if (builtin === "Text")
|
|
2255
|
+
return "__velarTextNamespace";
|
|
2256
|
+
if (builtin === "Math")
|
|
2257
|
+
return "__velarMathNamespace";
|
|
2258
|
+
if (builtin === "range")
|
|
2259
|
+
return "__velarRange";
|
|
2260
|
+
}
|
|
2261
|
+
if (expression.name === "number") {
|
|
2262
|
+
this.needsNumberHelper = true;
|
|
2263
|
+
return "__velarNumber";
|
|
2264
|
+
}
|
|
2265
|
+
return expression.name === "str" ? "String"
|
|
2266
|
+
: expression.name === "print" ? "console.log"
|
|
2267
|
+
: this.builtinErrorRuntimeName(expression.name) ?? expression.name;
|
|
2268
|
+
case "SuperExpression":
|
|
2269
|
+
return "super";
|
|
2270
|
+
case "DynamicImportExpression": {
|
|
2271
|
+
const source = expression.source.endsWith(".vel") ? `${expression.source.slice(0, -4)}.js` : expression.source;
|
|
2272
|
+
return `import(${JSON.stringify(source)})`;
|
|
2273
|
+
}
|
|
2274
|
+
case "ListExpression":
|
|
2275
|
+
if (expression.elements.some((element) => element.kind === "SpreadExpression")) {
|
|
2276
|
+
this.needsCollectionHelpers = true;
|
|
2277
|
+
const asynchronous = expression.elements.some((element) => this.expressionContainsDirectAwait(element));
|
|
2278
|
+
const parts = expression.elements.map((element) => {
|
|
2279
|
+
const directAwait = this.expressionContainsDirectAwait(element);
|
|
2280
|
+
const value = element.kind === "SpreadExpression" ? element.value : element;
|
|
2281
|
+
const read = `${directAwait ? "async " : ""}() => (${this.emitMappedExpression(value)})`;
|
|
2282
|
+
return asynchronous ? `[${element.kind === "SpreadExpression"}, ${directAwait}, ${read}]` : `[${element.kind === "SpreadExpression"}, ${read}]`;
|
|
2283
|
+
});
|
|
2284
|
+
return `${asynchronous ? "await __velarCreateListAsync" : "__velarCreateList"}([${parts.join(", ")}])`;
|
|
2285
|
+
}
|
|
2286
|
+
return `[${expression.elements.map((element) => this.emitMappedExpression(element)).join(", ")}]`;
|
|
2287
|
+
case "ObjectExpression": {
|
|
2288
|
+
const needsControlledConstruction = expression.properties.some((property) => property.kind === "ObjectSpread"
|
|
2289
|
+
|| property.name === "__proto__");
|
|
2290
|
+
if (!needsControlledConstruction) {
|
|
2291
|
+
return `{ ${expression.properties.map((property) => property.kind === "ObjectProperty"
|
|
2292
|
+
? `${this.emitObjectKey(property.name)}: ${this.emitMappedExpression(property.value)}`
|
|
2293
|
+
: "").join(", ")} }`;
|
|
2294
|
+
}
|
|
2295
|
+
this.needsCollectionHelpers = true;
|
|
2296
|
+
this.needsRecordHelpers = true;
|
|
2297
|
+
const asynchronous = expression.properties.some((property) => this.expressionContainsDirectAwait(property.value));
|
|
2298
|
+
const parts = expression.properties.map((property) => {
|
|
2299
|
+
const directAwait = this.expressionContainsDirectAwait(property.value);
|
|
2300
|
+
const read = `${directAwait ? "async " : ""}() => (${this.emitMappedExpression(property.value)})`;
|
|
2301
|
+
const name = property.kind === "ObjectProperty" ? JSON.stringify(property.name) : "null";
|
|
2302
|
+
return asynchronous
|
|
2303
|
+
? `[${property.kind === "ObjectSpread"}, ${name}, ${directAwait}, ${read}]`
|
|
2304
|
+
: `[${property.kind === "ObjectSpread"}, ${name}, ${read}]`;
|
|
2305
|
+
});
|
|
2306
|
+
return `${asynchronous ? "await __velarCreateRecordAsync" : "__velarCreateRecord"}([${parts.join(", ")}])`;
|
|
2307
|
+
}
|
|
2308
|
+
case "SpreadExpression":
|
|
2309
|
+
this.needsCollectionHelpers = true;
|
|
2310
|
+
return `...__velarCopyList(${this.emitMappedExpression(expression.value)}, "Call spread")`;
|
|
2311
|
+
// D39 item 51: the attempt runs in its own frame so any failure inside
|
|
2312
|
+
// the whole chain becomes null, and nothing else in the surrounding
|
|
2313
|
+
// expression is skipped.
|
|
2314
|
+
case "TryExpression": {
|
|
2315
|
+
// D51 rule 103: `try` turns an *expected* failure into an optional.
|
|
2316
|
+
// AssertionError, NarrowingError, and IndexError are the language
|
|
2317
|
+
// saying "your program has a bug", so they pass straight through
|
|
2318
|
+
// instead of arriving as a `null` that reads like "not found". A
|
|
2319
|
+
// `catch` block still catches all three — that one is explicit.
|
|
2320
|
+
this.needsIntegrityFailureHelper = true;
|
|
2321
|
+
const asynchronous = this.expressionContainsDirectAwait(expression.value);
|
|
2322
|
+
const failure = `__velarTryFailure${expression.span.start}`;
|
|
2323
|
+
const attempt = `{ try { return ${this.emitMappedExpression(expression.value)}; } `
|
|
2324
|
+
+ `catch (${failure}) { if (__velarIsIntegrityFailure(${failure})) throw ${failure}; return null; } }`;
|
|
2325
|
+
return `${asynchronous ? "await " : ""}(${asynchronous ? "async " : ""}() => ${attempt})()`;
|
|
2326
|
+
}
|
|
2327
|
+
case "UnaryExpression":
|
|
2328
|
+
if (expression.operator === "await") {
|
|
2329
|
+
return `await ${this.emitMappedExpression(expression.operand)}`;
|
|
2330
|
+
}
|
|
2331
|
+
if (expression.operator === "~") {
|
|
2332
|
+
this.needsBitwiseHelpers = true;
|
|
2333
|
+
return `__velarBitwiseUnary(${this.emitMappedExpression(expression.operand)})`;
|
|
2334
|
+
}
|
|
2335
|
+
return expression.operator === "not"
|
|
2336
|
+
? `!(${this.emitCondition(expression.operand)})`
|
|
2337
|
+
: `${expression.operator}(${this.emitMappedExpression(expression.operand)})`;
|
|
2338
|
+
case "BinaryExpression": {
|
|
2339
|
+
if (expression.operator === "and" || expression.operator === "or") {
|
|
2340
|
+
const operator = expression.operator === "and" ? "&&" : "||";
|
|
2341
|
+
return `(${this.emitCondition(expression.left)} ${operator} ${this.emitCondition(expression.right)})`;
|
|
2342
|
+
}
|
|
2343
|
+
if (expression.operator === "in" || expression.operator === "not in") {
|
|
2344
|
+
this.needsCollectionHelpers = true;
|
|
2345
|
+
const membership = `__velarContains(${this.emitMappedExpression(expression.left)}, ${this.emitMappedExpression(expression.right)})`;
|
|
2346
|
+
return expression.operator === "not in" ? `!(${membership})` : membership;
|
|
2347
|
+
}
|
|
2348
|
+
if (["&", "|", "^", "<<", ">>", ">>>"].includes(expression.operator)) {
|
|
2349
|
+
this.needsBitwiseHelpers = true;
|
|
2350
|
+
return `__velarBitwiseBinary(${this.emitMappedExpression(expression.left)}, ${JSON.stringify(expression.operator)}, ${this.emitMappedExpression(expression.right)})`;
|
|
2351
|
+
}
|
|
2352
|
+
// D36 item 41: `==`/`!=` are SameValueZero. The analyzer proves which
|
|
2353
|
+
// comparisons can actually meet two NaN operands; everything else
|
|
2354
|
+
// elides the repair and stays plain strict equality.
|
|
2355
|
+
if ((expression.operator === "==" || expression.operator === "!=")
|
|
2356
|
+
&& this.hints.sameValueZeroEqualities.has(spanIdentity(expression.span))) {
|
|
2357
|
+
this.needsCollectionHelpers = true;
|
|
2358
|
+
const equality = `__velarSameValueZero(${this.emitBinaryOperand(expression.left)}, ${this.emitBinaryOperand(expression.right)})`;
|
|
2359
|
+
return expression.operator === "==" ? equality : `!${equality}`;
|
|
2360
|
+
}
|
|
2361
|
+
// TXT-D1: string orderings compare by code point everywhere; the
|
|
2362
|
+
// analyzer marks exactly the ordered comparisons whose operands are
|
|
2363
|
+
// strings, so numbers keep the plain operator.
|
|
2364
|
+
if (["<", "<=", ">", ">="].includes(expression.operator)
|
|
2365
|
+
&& this.hints.stringOrderings.has(spanIdentity(expression.span))) {
|
|
2366
|
+
this.needsPrimitiveHelpers = true;
|
|
2367
|
+
return `(__velarStringCompare(${this.emitBinaryOperand(expression.left)}, ${this.emitBinaryOperand(expression.right)}) ${expression.operator} 0)`;
|
|
2368
|
+
}
|
|
2369
|
+
// D41 item 61: a comparison between Comparable-bounded parameters
|
|
2370
|
+
// dispatches on the runtime category instead of guessing one.
|
|
2371
|
+
if (["<", "<=", ">", ">="].includes(expression.operator)
|
|
2372
|
+
&& this.hints.dynamicOrderings.has(spanIdentity(expression.span))) {
|
|
2373
|
+
this.needsPrimitiveHelpers = true;
|
|
2374
|
+
return `(__velarOrderCompare(${this.emitBinaryOperand(expression.left)}, ${this.emitBinaryOperand(expression.right)}) ${expression.operator} 0)`;
|
|
2375
|
+
}
|
|
2376
|
+
const operator = expression.operator === "==" ? "===" : expression.operator === "!=" ? "!==" : expression.operator;
|
|
2377
|
+
const left = expression.operator === "**" && expression.left.kind === "UnaryExpression"
|
|
2378
|
+
? `(${this.emitMappedExpression(expression.left)})`
|
|
2379
|
+
: this.emitBinaryOperand(expression.left);
|
|
2380
|
+
return `(${left} ${operator} ${this.emitBinaryOperand(expression.right)})`;
|
|
2381
|
+
}
|
|
2382
|
+
case "ComparisonChainExpression":
|
|
2383
|
+
return this.emitComparisonChain(expression);
|
|
2384
|
+
case "ConditionalExpression":
|
|
2385
|
+
return `(${this.emitCondition(expression.condition)} ? ${this.emitMappedExpression(expression.thenValue)} : ${this.emitMappedExpression(expression.elseValue)})`;
|
|
2386
|
+
case "IsExpression":
|
|
2387
|
+
{
|
|
2388
|
+
const value = `__velarIs${expression.span.start}`;
|
|
2389
|
+
const checked = resolveTypeReference(expression.type);
|
|
2390
|
+
const classCheck = this.hints.classChecks.has(spanIdentity(expression.span));
|
|
2391
|
+
const test = classCheck
|
|
2392
|
+
? `${value} instanceof ${this.typeRuntimeName(expression.type)}`
|
|
2393
|
+
: this.emitIsCheck(checked, value);
|
|
2394
|
+
const emittedValue = this.emitMappedExpression(expression.value);
|
|
2395
|
+
// Keep the common one-read checks direct. Union, optional, and
|
|
2396
|
+
// structural checks that reference the value more than once capture
|
|
2397
|
+
// it first so an arbitrary source expression still runs exactly once.
|
|
2398
|
+
const uses = test.split(value).length - 1;
|
|
2399
|
+
// GRM-D1: a nested `is` (or unary) operand spliced into the direct
|
|
2400
|
+
// check would rebind under the generated operator — `typeof typeof
|
|
2401
|
+
// x` — so those operands keep explicit parentheses.
|
|
2402
|
+
const operand = expression.value.kind === "IsExpression" || expression.value.kind === "UnaryExpression"
|
|
2403
|
+
? `(${emittedValue})`
|
|
2404
|
+
: emittedValue;
|
|
2405
|
+
const result = uses === 1
|
|
2406
|
+
? classCheck
|
|
2407
|
+
? `${operand} instanceof ${this.typeRuntimeName(expression.type)}`
|
|
2408
|
+
: this.emitIsCheck(checked, operand)
|
|
2409
|
+
: `(${value} => ${test})(${emittedValue})`;
|
|
2410
|
+
return expression.operator === "is not" ? `!(${result})` : result;
|
|
2411
|
+
}
|
|
2412
|
+
case "ArrowFunctionExpression": {
|
|
2413
|
+
const body = this.emitMappedExpression(expression.body);
|
|
2414
|
+
const resolvedBody = this.hints.asyncResolvedValues.has(spanIdentity(expression.body.span)) ? `__velarAsyncResolvedValue(${body})` : body;
|
|
2415
|
+
const emittedBody = expression.body.kind === "ObjectExpression" ? `(${resolvedBody})` : resolvedBody;
|
|
2416
|
+
return `${expression.asynchronous ? "async " : ""}${expression.parameters.length === 1 && !expression.parameters[0].rest && !expression.parameters[0].defaultValue
|
|
2417
|
+
? expression.parameters[0].name
|
|
2418
|
+
: `(${expression.parameters.map((parameter) => this.emitParameter(parameter.name, parameter.defaultValue, parameter.rest)).join(", ")})`} => ${emittedBody}`;
|
|
2419
|
+
}
|
|
2420
|
+
case "CallExpression": {
|
|
2421
|
+
if (expression.callee.kind === "MemberExpression") {
|
|
2422
|
+
const binaryHelper = this.binaryHelper(expression.callee);
|
|
2423
|
+
if (binaryHelper) {
|
|
2424
|
+
this.needsBinaryHelpers = true;
|
|
2425
|
+
const object = this.emitMappedExpression(expression.callee.object);
|
|
2426
|
+
const sourceArguments = expression.arguments.map((argument) => this.emitMappedExpression(argument));
|
|
2427
|
+
const namedOrder = this.hints.namedArgumentOrders.get(spanIdentity(expression.span));
|
|
2428
|
+
const arguments_ = namedOrder
|
|
2429
|
+
? namedOrder.map((source) => source === -1 ? "undefined" : `__velarNamedArguments[${source}]`)
|
|
2430
|
+
: sourceArguments;
|
|
2431
|
+
const emittedArguments = namedOrder
|
|
2432
|
+
? `...((__velarNamedArguments) => [${arguments_.join(", ")}])([${sourceArguments.join(", ")}])`
|
|
2433
|
+
: arguments_.join(", ");
|
|
2434
|
+
const suffix = arguments_.length > 0 ? `, ${emittedArguments}` : "";
|
|
2435
|
+
const invocation = `${binaryHelper}(__velarValue${suffix})`;
|
|
2436
|
+
return this.hints.optionalCallees.has(spanIdentity(expression.span))
|
|
2437
|
+
? `(__velarValue => __velarValue == null ? null : ${invocation})(${object})`
|
|
2438
|
+
: `${binaryHelper}(${object}${suffix})`;
|
|
2439
|
+
}
|
|
2440
|
+
const primitiveHelper = this.primitiveHelper(expression.callee);
|
|
2441
|
+
if (primitiveHelper) {
|
|
2442
|
+
this.needsPrimitiveHelpers = true;
|
|
2443
|
+
const object = this.emitMappedExpression(expression.callee.object);
|
|
2444
|
+
const sourceArguments = expression.arguments.map((argument) => this.emitMappedExpression(argument));
|
|
2445
|
+
const namedOrder = this.hints.namedArgumentOrders.get(spanIdentity(expression.span));
|
|
2446
|
+
const arguments_ = namedOrder
|
|
2447
|
+
? namedOrder.map((source) => source === -1 ? "undefined" : `__velarNamedArguments[${source}]`)
|
|
2448
|
+
: sourceArguments;
|
|
2449
|
+
const emittedArguments = namedOrder
|
|
2450
|
+
? `...((__velarNamedArguments) => [${arguments_.join(", ")}])([${sourceArguments.join(", ")}])`
|
|
2451
|
+
: arguments_.join(", ");
|
|
2452
|
+
const suffix = arguments_.length > 0 ? `, ${emittedArguments}` : "";
|
|
2453
|
+
const invocation = `${primitiveHelper}(__velarValue${suffix})`;
|
|
2454
|
+
return this.hints.optionalCallees.has(spanIdentity(expression.span))
|
|
2455
|
+
? `(__velarValue => __velarValue == null ? null : ${invocation})(${object})`
|
|
2456
|
+
: `${primitiveHelper}(${object}${suffix})`;
|
|
2457
|
+
}
|
|
2458
|
+
const helper = this.collectionHelper(expression.callee);
|
|
2459
|
+
if (helper) {
|
|
2460
|
+
this.needsCollectionHelpers = true;
|
|
2461
|
+
const object = this.emitMappedExpression(expression.callee.object);
|
|
2462
|
+
const sourceArguments = expression.arguments.map((argument) => this.emitMappedExpression(argument));
|
|
2463
|
+
const namedOrder = this.hints.namedArgumentOrders.get(spanIdentity(expression.span));
|
|
2464
|
+
const arguments_ = namedOrder
|
|
2465
|
+
? namedOrder.map((source) => source === -1 ? "undefined" : `__velarNamedArguments[${source}]`)
|
|
2466
|
+
: sourceArguments;
|
|
2467
|
+
const emitArguments = () => namedOrder
|
|
2468
|
+
? `...((__velarNamedArguments) => [${arguments_.join(", ")}])([${sourceArguments.join(", ")}])`
|
|
2469
|
+
: arguments_.join(", ");
|
|
2470
|
+
const suffix = arguments_.length > 0 ? `, ${emitArguments()}` : "";
|
|
2471
|
+
if (this.hints.optionalCallees.has(spanIdentity(expression.span))) {
|
|
2472
|
+
const invocation = `${helper}(__velarValue${suffix})`;
|
|
2473
|
+
return `(__velarOptionalCollection(${object}, __velarValue => ${invocation}) ?? null)`;
|
|
2474
|
+
}
|
|
2475
|
+
return `${helper}(${object}${suffix})`;
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
const hostBoundary = this.hints.javaScriptCallBoundaries.has(spanIdentity(expression.span));
|
|
2479
|
+
const sourceArguments = expression.arguments.map((argument) => {
|
|
2480
|
+
const emitted = this.emitMappedExpression(argument);
|
|
2481
|
+
return hostBoundary && argument.kind !== "SpreadExpression" ? `__velarHostRaw(${emitted})` : emitted;
|
|
2482
|
+
});
|
|
2483
|
+
const namedOrder = this.hints.namedArgumentOrders.get(spanIdentity(expression.span));
|
|
2484
|
+
const arguments_ = namedOrder
|
|
2485
|
+
? namedOrder.map((source) => source === -1 ? "undefined" : `__velarNamedArguments[${source}]`)
|
|
2486
|
+
: sourceArguments;
|
|
2487
|
+
const emitArguments = () => namedOrder
|
|
2488
|
+
? `...((__velarNamedArguments) => [${arguments_.join(", ")}])([${sourceArguments.join(", ")}])`
|
|
2489
|
+
: arguments_.join(", ");
|
|
2490
|
+
if (this.hints.optionalCallees.has(spanIdentity(expression.span))) {
|
|
2491
|
+
const call = expression.callee.kind === "MemberExpression"
|
|
2492
|
+
? `${this.emitPostfixReceiver(expression.callee.object)}${expression.callee.optional ? "?." : "."}${expression.callee.property}?.(${emitArguments()})`
|
|
2493
|
+
: `${this.emitPostfixReceiver(expression.callee)}?.(${emitArguments()})`;
|
|
2494
|
+
return `(${call} ?? null)`;
|
|
2495
|
+
}
|
|
2496
|
+
if (expression.callee.kind === "MemberExpression" && expression.callee.optional) {
|
|
2497
|
+
const call = `${this.emitPostfixReceiver(expression.callee.object)}?.${expression.callee.property}(${emitArguments()})`;
|
|
2498
|
+
return `(${call} ?? null)`;
|
|
2499
|
+
}
|
|
2500
|
+
let callee;
|
|
2501
|
+
if (expression.callee.kind === "IdentifierExpression" && (expression.callee.name === "Map" || expression.callee.name === "Set")) {
|
|
2502
|
+
this.needsCollectionHelpers = true;
|
|
2503
|
+
callee = expression.callee.name === "Map" ? "__velarCreateMap" : "__velarCreateSet";
|
|
2504
|
+
}
|
|
2505
|
+
else if (expression.callee.kind === "IdentifierExpression" && this.hints.equalsCalls.has(spanIdentity(expression.span))) {
|
|
2506
|
+
this.needsCollectionHelpers = true;
|
|
2507
|
+
callee = "__velarEquals";
|
|
2508
|
+
}
|
|
2509
|
+
else {
|
|
2510
|
+
if (this.hints.constructorCalls.has(spanIdentity(expression.span))) {
|
|
2511
|
+
// A callee that is not a plain name path may be wrapped (for
|
|
2512
|
+
// example by a narrowing recheck IIFE), and `new (arrow)(x)(args)`
|
|
2513
|
+
// binds `(x)` as the construction arguments — the wrapper, not
|
|
2514
|
+
// the class, gets constructed. Parentheses restore the callee
|
|
2515
|
+
// boundary; plain name paths skip them to keep output readable.
|
|
2516
|
+
// Source-map markers are invisible in final output and ignored.
|
|
2517
|
+
const constructed = this.emitMappedExpression(expression.callee);
|
|
2518
|
+
callee = /^[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z0-9_$]+)*$/u.test(constructed.replaceAll(javaScriptNodeMarker, ""))
|
|
2519
|
+
? `new ${constructed}`
|
|
2520
|
+
: `new (${constructed})`;
|
|
2521
|
+
}
|
|
2522
|
+
else {
|
|
2523
|
+
callee = this.emitPostfixReceiver(expression.callee);
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
const formRead = this.hints.formReads.get(spanIdentity(expression.span));
|
|
2527
|
+
if (formRead)
|
|
2528
|
+
arguments_.push(JSON.stringify(formRead));
|
|
2529
|
+
const call = `${callee}(${emitArguments()})`;
|
|
2530
|
+
const result = this.hints.optionalCalls.has(spanIdentity(expression.span)) ? `(${call} ?? null)` : call;
|
|
2531
|
+
// BRG-U10: a synchronous non-Error throw from an extern call during
|
|
2532
|
+
// module initialization would reach the host raw (no catch, no
|
|
2533
|
+
// rejection path); rethrowing through the owned normalization keeps
|
|
2534
|
+
// the last bridge failure shape on the Error channel. Calls whose
|
|
2535
|
+
// arguments await are already rejection-owned and stay unwrapped.
|
|
2536
|
+
if (hostBoundary
|
|
2537
|
+
&& this.hints.moduleTopLevelHostCalls.has(spanIdentity(expression.span))
|
|
2538
|
+
&& !this.expressionContainsDirectAwait(expression)) {
|
|
2539
|
+
this.needsThrownValueHelper = true;
|
|
2540
|
+
return `(() => { try { return ${result}; } catch (__velarThrown) { throw __velarNormalizeError(__velarThrown); } })()`;
|
|
2541
|
+
}
|
|
2542
|
+
return result;
|
|
2543
|
+
}
|
|
2544
|
+
case "MemberExpression": {
|
|
2545
|
+
const binaryHelper = this.binaryHelper(expression);
|
|
2546
|
+
if (binaryHelper) {
|
|
2547
|
+
this.needsBinaryHelpers = true;
|
|
2548
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2549
|
+
const bound = `(...__velarArguments) => ${binaryHelper}(__velarValue, ...__velarArguments)`;
|
|
2550
|
+
return expression.optional
|
|
2551
|
+
? `(__velarValue => __velarValue == null ? null : ${bound})(${object})`
|
|
2552
|
+
: `(__velarValue => ${bound})(${object})`;
|
|
2553
|
+
}
|
|
2554
|
+
if (this.hints.binarySizes.has(expression.span.end)) {
|
|
2555
|
+
this.needsBinaryHelpers = true;
|
|
2556
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2557
|
+
return expression.optional
|
|
2558
|
+
? `(__velarValue => __velarValue == null ? null : __velarBinaryRuntime.__velarSize(__velarValue))(${object})`
|
|
2559
|
+
: `__velarBinaryRuntime.__velarSize(${object})`;
|
|
2560
|
+
}
|
|
2561
|
+
const primitiveHelper = this.primitiveHelper(expression);
|
|
2562
|
+
if (primitiveHelper) {
|
|
2563
|
+
this.needsPrimitiveHelpers = true;
|
|
2564
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2565
|
+
const bound = `(...__velarArguments) => ${primitiveHelper}(__velarValue, ...__velarArguments)`;
|
|
2566
|
+
return expression.optional
|
|
2567
|
+
? `(__velarValue => __velarValue == null ? null : ${bound})(${object})`
|
|
2568
|
+
: `(__velarValue => ${bound})(${object})`;
|
|
2569
|
+
}
|
|
2570
|
+
if (this.hints.stringSizes.has(expression.span.end)) {
|
|
2571
|
+
this.needsPrimitiveHelpers = true;
|
|
2572
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2573
|
+
return expression.optional
|
|
2574
|
+
? `(__velarValue => __velarValue == null ? null : __velarStringSize(__velarValue))(${object})`
|
|
2575
|
+
: `__velarStringSize(${object})`;
|
|
2576
|
+
}
|
|
2577
|
+
const collectionHelper = this.collectionHelper(expression);
|
|
2578
|
+
if (collectionHelper) {
|
|
2579
|
+
this.needsCollectionHelpers = true;
|
|
2580
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2581
|
+
const bound = `(...__velarArguments) => ${collectionHelper}(__velarValue, ...__velarArguments)`;
|
|
2582
|
+
return expression.optional
|
|
2583
|
+
? `(__velarValue => __velarValue == null ? null : ${bound})(${object})`
|
|
2584
|
+
: `(__velarValue => ${bound})(${object})`;
|
|
2585
|
+
}
|
|
2586
|
+
if (this.hints.collectionSizes.has(expression.span.end)) {
|
|
2587
|
+
this.needsCollectionHelpers = true;
|
|
2588
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2589
|
+
return expression.optional
|
|
2590
|
+
? `(__velarOptionalCollection(${object}, __velarCollectionSize) ?? null)`
|
|
2591
|
+
: `__velarCollectionSize(${object})`;
|
|
2592
|
+
}
|
|
2593
|
+
const staticFieldOwnerDepth = this.hints.staticFieldReads.get(spanIdentity(expression.span));
|
|
2594
|
+
if (staticFieldOwnerDepth !== undefined) {
|
|
2595
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2596
|
+
const read = `__velarReadStaticField(__velarValue, ${JSON.stringify(expression.property)}, ${staticFieldOwnerDepth})`;
|
|
2597
|
+
return expression.optional
|
|
2598
|
+
? `(__velarValue => __velarValue == null ? null : ${read})(${object})`
|
|
2599
|
+
: `__velarReadStaticField(${object}, ${JSON.stringify(expression.property)}, ${staticFieldOwnerDepth})`;
|
|
2600
|
+
}
|
|
2601
|
+
if (this.hints.errorCodeReads.has(spanIdentity(expression.span))) {
|
|
2602
|
+
this.needsErrorCodeHelper = true;
|
|
2603
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2604
|
+
return expression.optional
|
|
2605
|
+
? `($velarValue => $velarValue == null ? null : __velarErrorCode($velarValue))(${object})`
|
|
2606
|
+
: `__velarErrorCode(${object})`;
|
|
2607
|
+
}
|
|
2608
|
+
if (this.hints.instanceFieldReads.has(spanIdentity(expression.span))) {
|
|
2609
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2610
|
+
const read = `__velarReadInstanceField(__velarValue, ${JSON.stringify(expression.property)})`;
|
|
2611
|
+
return expression.optional
|
|
2612
|
+
? `(__velarValue => __velarValue == null ? null : ${read})(${object})`
|
|
2613
|
+
: `__velarReadInstanceField(${object}, ${JSON.stringify(expression.property)})`;
|
|
2614
|
+
}
|
|
2615
|
+
if (this.hints.privateInstanceFieldReads.has(spanIdentity(expression.span))) {
|
|
2616
|
+
if (expression.optional) {
|
|
2617
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2618
|
+
const read = `__velarReadPrivateField(__velarValue.#${expression.property}, ${JSON.stringify(expression.property)})`;
|
|
2619
|
+
return `(__velarValue => __velarValue == null ? null : ${read})(${object})`;
|
|
2620
|
+
}
|
|
2621
|
+
return `__velarReadPrivateField(${this.emitPostfixReceiver(expression.object)}.#${expression.property}, ${JSON.stringify(expression.property)})`;
|
|
2622
|
+
}
|
|
2623
|
+
if (this.hints.classMethodReferences.has(spanIdentity(expression.span))) {
|
|
2624
|
+
// Methods live on the prototype, so a method read as a value
|
|
2625
|
+
// evaluates its receiver once and binds at the reference site —
|
|
2626
|
+
// the collection-method rule of charter section 8. `super` cannot
|
|
2627
|
+
// be captured by a temporary; it binds `this` directly.
|
|
2628
|
+
const property = `${this.hints.privateMembers.has(spanIdentity(expression.span)) ? "#" : ""}${expression.property}`;
|
|
2629
|
+
if (expression.object.kind === "SuperExpression") {
|
|
2630
|
+
return `super.${property}.bind(this)`;
|
|
2631
|
+
}
|
|
2632
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2633
|
+
const bound = `__velarValue.${property}.bind(__velarValue)`;
|
|
2634
|
+
return expression.optional
|
|
2635
|
+
? `(__velarValue => __velarValue == null ? null : ${bound})(${object})`
|
|
2636
|
+
: `(__velarValue => ${bound})(${object})`;
|
|
2637
|
+
}
|
|
2638
|
+
const publicProperty = expression.property;
|
|
2639
|
+
const property = `${this.hints.privateMembers.has(spanIdentity(expression.span)) ? "#" : ""}${publicProperty}`;
|
|
2640
|
+
const access = `${this.emitPostfixReceiver(expression.object)}${expression.optional ? "?." : "."}${property}`;
|
|
2641
|
+
return this.hints.optionalMembers.has(spanIdentity(expression.span)) ? `(${access} ?? null)` : access;
|
|
2642
|
+
}
|
|
2643
|
+
case "IndexExpression":
|
|
2644
|
+
{
|
|
2645
|
+
const binaryKind = this.hints.binaryIndexes.get(spanIdentity(expression.span));
|
|
2646
|
+
if (binaryKind) {
|
|
2647
|
+
this.needsBinaryHelpers = true;
|
|
2648
|
+
const helper = this.binaryIndexHelper(binaryKind);
|
|
2649
|
+
const object = this.emitMappedExpression(expression.object);
|
|
2650
|
+
if (this.hints.optionalIndexes.has(spanIdentity(expression.span))) {
|
|
2651
|
+
return `(__velarValue => __velarValue == null ? null : __velarBinaryRuntime.${helper}(__velarValue, ${this.emitMappedExpression(expression.index)}))(${object})`;
|
|
2652
|
+
}
|
|
2653
|
+
return `__velarBinaryRuntime.${helper}(${object}, ${this.emitMappedExpression(expression.index)})`;
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
this.needsIndexHelpers = true;
|
|
2657
|
+
this.needsCollectionHelpers = true;
|
|
2658
|
+
return this.hints.optionalIndexes.has(spanIdentity(expression.span))
|
|
2659
|
+
? `__velarOptionalIndex(${this.emitMappedExpression(expression.object)}, () => ${this.emitMappedExpression(expression.index)})`
|
|
2660
|
+
: `__velarIndex(${this.emitMappedExpression(expression.object)}, ${this.emitMappedExpression(expression.index)})`;
|
|
2661
|
+
default:
|
|
2662
|
+
return "null";
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
// A 'bool?' condition asks whether the value is true, so it lowers to an
|
|
2666
|
+
// explicit '=== true'. Both 'false' and an absent value then take the same
|
|
2667
|
+
// else path instead of riding on JavaScript truthiness.
|
|
2668
|
+
emitCondition(expression) {
|
|
2669
|
+
const value = this.emitMappedExpression(expression);
|
|
2670
|
+
return this.hints.truthConditions.has(spanIdentity(expression.span)) ? `(${value} === true)` : value;
|
|
2671
|
+
}
|
|
2672
|
+
emitBinaryOperand(expression) {
|
|
2673
|
+
const emitted = this.emitMappedExpression(expression);
|
|
2674
|
+
return expression.kind === "ArrowFunctionExpression" ? `(${emitted})` : emitted;
|
|
2675
|
+
}
|
|
2676
|
+
emitPostfixReceiver(expression) {
|
|
2677
|
+
const emitted = this.emitMappedExpression(expression);
|
|
2678
|
+
if (expression.kind === "ArrowFunctionExpression"
|
|
2679
|
+
|| expression.kind === "UnaryExpression"
|
|
2680
|
+
|| expression.kind === "IsExpression"
|
|
2681
|
+
|| (expression.kind === "LiteralExpression" && typeof expression.value === "number")) {
|
|
2682
|
+
return `(${emitted})`;
|
|
2683
|
+
}
|
|
2684
|
+
return emitted;
|
|
2685
|
+
}
|
|
2686
|
+
emitComparisonChain(expression) {
|
|
2687
|
+
const prefix = `__velarCompare${expression.span.start}`;
|
|
2688
|
+
const body = [`const ${prefix}_0 = ${this.emitMappedExpression(expression.operands[0])};`];
|
|
2689
|
+
for (let index = 1; index < expression.operands.length; index += 1) {
|
|
2690
|
+
body.push(`const ${prefix}_${index} = ${this.emitMappedExpression(expression.operands[index])};`);
|
|
2691
|
+
const sourceOperator = expression.operators[index - 1];
|
|
2692
|
+
const linkSpan = spanIdentity({
|
|
2693
|
+
start: expression.operands[index - 1].span.start,
|
|
2694
|
+
end: expression.operands[index].span.end,
|
|
2695
|
+
});
|
|
2696
|
+
if ((sourceOperator === "==" || sourceOperator === "!=") && this.hints.sameValueZeroEqualities.has(linkSpan)) {
|
|
2697
|
+
// Chain operands are already captured once, so the SameValueZero
|
|
2698
|
+
// repair inlines as its short-circuit shape (D36 item 41).
|
|
2699
|
+
const left = `${prefix}_${index - 1}`;
|
|
2700
|
+
const right = `${prefix}_${index}`;
|
|
2701
|
+
const equality = `${left} === ${right} || (${left} !== ${left} && ${right} !== ${right})`;
|
|
2702
|
+
body.push(sourceOperator === "=="
|
|
2703
|
+
? `if (!(${equality})) return false;`
|
|
2704
|
+
: `if (${equality}) return false;`);
|
|
2705
|
+
continue;
|
|
2706
|
+
}
|
|
2707
|
+
// TXT-D1: string chain links compare by code point too.
|
|
2708
|
+
if (["<", "<=", ">", ">="].includes(sourceOperator) && this.hints.stringOrderings.has(linkSpan)) {
|
|
2709
|
+
this.needsPrimitiveHelpers = true;
|
|
2710
|
+
body.push(`if (!(__velarStringCompare(${prefix}_${index - 1}, ${prefix}_${index}) ${sourceOperator} 0)) return false;`);
|
|
2711
|
+
continue;
|
|
2712
|
+
}
|
|
2713
|
+
if (["<", "<=", ">", ">="].includes(sourceOperator) && this.hints.dynamicOrderings.has(linkSpan)) {
|
|
2714
|
+
this.needsPrimitiveHelpers = true;
|
|
2715
|
+
body.push(`if (!(__velarOrderCompare(${prefix}_${index - 1}, ${prefix}_${index}) ${sourceOperator} 0)) return false;`);
|
|
2716
|
+
continue;
|
|
2717
|
+
}
|
|
2718
|
+
const operator = sourceOperator === "==" ? "===" : sourceOperator === "!=" ? "!==" : sourceOperator;
|
|
2719
|
+
body.push(`if (!(${prefix}_${index - 1} ${operator} ${prefix}_${index})) return false;`);
|
|
2720
|
+
}
|
|
2721
|
+
const asynchronous = expression.operands.some((operand) => this.expressionContainsDirectAwait(operand));
|
|
2722
|
+
return `${asynchronous ? "await " : ""}(${asynchronous ? "async " : ""}() => { ${body.join(" ")} return true; })()`;
|
|
2723
|
+
}
|
|
2724
|
+
expressionContainsDirectAwait(expression) {
|
|
2725
|
+
return containsDirectAwait(expression, (value, contains) => this.extensionExpressionContainsDirectAwait(value, contains));
|
|
2726
|
+
}
|
|
2727
|
+
typeRuntimeName(reference) {
|
|
2728
|
+
const type = resolveTypeReference(reference);
|
|
2729
|
+
if (type.kind === "named")
|
|
2730
|
+
return this.builtinErrorRuntimeName(type.name) ?? type.name;
|
|
2731
|
+
return formatTypeReference(reference);
|
|
2732
|
+
}
|
|
2733
|
+
collectionHelper(expression) {
|
|
2734
|
+
switch (this.hints.collectionCalls.get(expression.span.end)) {
|
|
2735
|
+
case "get": return "__velarCollectionGet";
|
|
2736
|
+
case "slice": return "__velarCollectionSlice";
|
|
2737
|
+
case "listAppend": return "__velarListAppend";
|
|
2738
|
+
case "listExtend": return "__velarListExtend";
|
|
2739
|
+
case "listInsert": return "__velarListInsert";
|
|
2740
|
+
case "listRemove": return "__velarListRemove";
|
|
2741
|
+
case "listPop": return "__velarListPop";
|
|
2742
|
+
case "listCopy": return "__velarListCopy";
|
|
2743
|
+
case "listCount": return "__velarListCount";
|
|
2744
|
+
case "listFind": return "__velarListFind";
|
|
2745
|
+
case "listIndex": return "__velarListIndex";
|
|
2746
|
+
case "listSome": return "__velarListSome";
|
|
2747
|
+
case "listEvery": return "__velarListEvery";
|
|
2748
|
+
case "listMap": return "__velarListMap";
|
|
2749
|
+
case "listFilter": return "__velarListFilter";
|
|
2750
|
+
case "listFlatMap": return "__velarListFlatMap";
|
|
2751
|
+
case "listReduce": return "__velarListReduce";
|
|
2752
|
+
case "listJoin": return "__velarListJoin";
|
|
2753
|
+
case "listSorted": return "__velarListSorted";
|
|
2754
|
+
case "listReversed": return "__velarListReversed";
|
|
2755
|
+
case "listSum": return "__velarListSum";
|
|
2756
|
+
case "listMin": return "__velarListMin";
|
|
2757
|
+
case "listMax": return "__velarListMax";
|
|
2758
|
+
case "setAdd": return "__velarSetAdd";
|
|
2759
|
+
case "setUpdate": return "__velarSetUpdate";
|
|
2760
|
+
case "setCopy": return "__velarSetCopy";
|
|
2761
|
+
case "setUnion": return "__velarSetUnion";
|
|
2762
|
+
case "setIntersection": return "__velarSetIntersection";
|
|
2763
|
+
case "setDifference": return "__velarSetDifference";
|
|
2764
|
+
case "mapSet": return "__velarMapSet";
|
|
2765
|
+
case "mapUpdate": return "__velarMapUpdate";
|
|
2766
|
+
case "mapCopy": return "__velarMapCopy";
|
|
2767
|
+
case "recordSet": return "__velarRecordSet";
|
|
2768
|
+
case "recordCopy": return "__velarRecordCopy";
|
|
2769
|
+
case "has": return "__velarCollectionHas";
|
|
2770
|
+
case "remove": return "__velarCollectionRemove";
|
|
2771
|
+
case "clear": return "__velarCollectionClear";
|
|
2772
|
+
case "keys": return "__velarCollectionKeys";
|
|
2773
|
+
case "values": return "__velarCollectionValues";
|
|
2774
|
+
case "entries": return "__velarCollectionEntries";
|
|
2775
|
+
default: return null;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
binaryHelper(expression) {
|
|
2779
|
+
switch (this.hints.binaryCalls.get(expression.span.end)) {
|
|
2780
|
+
case "bufferCopy": return "__velarBinaryRuntime.__velarBufferCopy";
|
|
2781
|
+
case "bufferSlice": return "__velarBinaryRuntime.__velarBufferSlice";
|
|
2782
|
+
case "bufferToBytes": return "__velarBinaryRuntime.__velarBufferToBytes";
|
|
2783
|
+
default: return null;
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
binaryIndexHelper(kind) {
|
|
2787
|
+
switch (kind) {
|
|
2788
|
+
case "bytes": return "__velarIndex";
|
|
2789
|
+
case "uint8": return "__velarUInt8Index";
|
|
2790
|
+
case "uint16": return "__velarUInt16Index";
|
|
2791
|
+
case "uint32": return "__velarUInt32Index";
|
|
2792
|
+
case "float32": return "__velarFloat32Index";
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
binarySetIndexHelper(kind) {
|
|
2796
|
+
switch (kind) {
|
|
2797
|
+
case "uint8": return "__velarUInt8SetIndex";
|
|
2798
|
+
case "uint16": return "__velarUInt16SetIndex";
|
|
2799
|
+
case "uint32": return "__velarUInt32SetIndex";
|
|
2800
|
+
case "float32": return "__velarFloat32SetIndex";
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
primitiveHelper(expression) {
|
|
2804
|
+
switch (this.hints.primitiveCalls.get(expression.span.end)) {
|
|
2805
|
+
case "stringTrim": return "__velarStringTrim";
|
|
2806
|
+
case "stringUpper": return "__velarStringUpper";
|
|
2807
|
+
case "stringLower": return "__velarStringLower";
|
|
2808
|
+
case "stringSlice": return "__velarStringSlice";
|
|
2809
|
+
case "stringChar": return "__velarStringChar";
|
|
2810
|
+
case "stringHas": return "__velarStringHas";
|
|
2811
|
+
case "stringIndex": return "__velarStringIndex";
|
|
2812
|
+
case "stringCount": return "__velarStringCount";
|
|
2813
|
+
case "stringStartsWith": return "__velarStringStartsWith";
|
|
2814
|
+
case "stringEndsWith": return "__velarStringEndsWith";
|
|
2815
|
+
case "stringSplit": return "__velarStringSplit";
|
|
2816
|
+
case "stringReplace": return "__velarStringReplace";
|
|
2817
|
+
case "stringReplaceAll": return "__velarStringReplaceAll";
|
|
2818
|
+
case "stringPadStart": return "__velarStringPadStart";
|
|
2819
|
+
case "stringPadEnd": return "__velarStringPadEnd";
|
|
2820
|
+
case "stringRepeat": return "__velarStringRepeat";
|
|
2821
|
+
case "stringIsBlank": return "__velarStringIsBlank";
|
|
2822
|
+
case "numberAbs": return "__velarNumberAbs";
|
|
2823
|
+
case "numberRound": return "__velarNumberRound";
|
|
2824
|
+
case "numberFloor": return "__velarNumberFloor";
|
|
2825
|
+
case "numberCeil": return "__velarNumberCeil";
|
|
2826
|
+
case "numberToFixed": return "__velarNumberToFixed";
|
|
2827
|
+
case "numberIsInteger": return "__velarNumberIsInteger";
|
|
2828
|
+
case "numberIsNaN": return "__velarNumberIsNaN";
|
|
2829
|
+
case "numberIsFinite": return "__velarNumberIsFinite";
|
|
2830
|
+
default: return null;
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
emitObjectKey(name) {
|
|
2834
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2835
|
+
}
|
|
2836
|
+
emitMatchPatternAttempt(pattern, valueName, indentation) {
|
|
2837
|
+
const lines = [];
|
|
2838
|
+
const bindings = new Map();
|
|
2839
|
+
let nextTemporary = 0;
|
|
2840
|
+
const temporary = (label) => `__velarPattern${pattern.span.start}${label}${nextTemporary++}`;
|
|
2841
|
+
const bind = (name, value) => {
|
|
2842
|
+
if (name !== "_" && !bindings.has(name))
|
|
2843
|
+
bindings.set(name, value);
|
|
2844
|
+
};
|
|
2845
|
+
const rejectUnless = (condition) => {
|
|
2846
|
+
lines.push(`${indentation}if (!(${condition})) return null;`);
|
|
2847
|
+
};
|
|
2848
|
+
const emit = (current, value) => {
|
|
2849
|
+
switch (current.kind) {
|
|
2850
|
+
case "MatchWildcardPattern":
|
|
2851
|
+
break;
|
|
2852
|
+
case "MatchCapturePattern":
|
|
2853
|
+
bind(current.binding.name, value);
|
|
2854
|
+
break;
|
|
2855
|
+
case "MatchAsPattern":
|
|
2856
|
+
emit(current.pattern, value);
|
|
2857
|
+
bind(current.binding.name, value);
|
|
2858
|
+
break;
|
|
2859
|
+
case "MatchValuePattern":
|
|
2860
|
+
// ENM-D2: a candidate whose value can be NaN compares by
|
|
2861
|
+
// SameValueZero so `case box.nan:` agrees with `==` (charter
|
|
2862
|
+
// section 8); every other candidate keeps plain `===`.
|
|
2863
|
+
rejectUnless(current.values.map((candidate) => {
|
|
2864
|
+
if (this.hints.sameValueZeroMatchValues.has(spanIdentity(candidate.span))) {
|
|
2865
|
+
this.needsCollectionHelpers = true;
|
|
2866
|
+
return `__velarSameValueZero(${value}, ${this.emitMappedExpression(candidate)})`;
|
|
2867
|
+
}
|
|
2868
|
+
return `${value} === ${this.emitMappedExpression(candidate)}`;
|
|
2869
|
+
}).join(" || ") || "false");
|
|
2870
|
+
break;
|
|
2871
|
+
case "MatchTypePattern":
|
|
2872
|
+
rejectUnless(this.emitTypeCheck(resolveTypeReference(current.type), value));
|
|
2873
|
+
break;
|
|
2874
|
+
case "MatchListPattern": {
|
|
2875
|
+
this.needsCollectionHelpers = true;
|
|
2876
|
+
this.needsDirectCollectionInfrastructure = true;
|
|
2877
|
+
const items = temporary("List");
|
|
2878
|
+
const length = current.elements.length;
|
|
2879
|
+
lines.push(`${indentation}__velarReactiveCollectionTrack(${value});`);
|
|
2880
|
+
rejectUnless(`__velarCollectionListIsArray(${value})`);
|
|
2881
|
+
const lengthDescriptor = temporary("Length");
|
|
2882
|
+
const listLength = temporary("Size");
|
|
2883
|
+
lines.push(`${indentation}const ${lengthDescriptor} = __velarCollectionListGetOwnPropertyDescriptor(${value}, "length");`);
|
|
2884
|
+
rejectUnless(`${lengthDescriptor} && ${lengthDescriptor}.writable && !${lengthDescriptor}.enumerable && !${lengthDescriptor}.configurable && "value" in ${lengthDescriptor}`);
|
|
2885
|
+
lines.push(`${indentation}const ${listLength} = ${lengthDescriptor}.value;`);
|
|
2886
|
+
rejectUnless(`${listLength} <= __velarMaxCollectionItems && ${listLength} ${current.rest ? ">=" : "==="} ${length} && __velarCollectionListOwnSymbols(${value}).length === 0 && __velarCollectionListOwnNames(${value}).length === ${listLength} + 1`);
|
|
2887
|
+
lines.push(`${indentation}const ${items} = new __velarCollectionNativeArray(${listLength});`);
|
|
2888
|
+
const cursor = temporary("Index");
|
|
2889
|
+
const descriptor = temporary("Item");
|
|
2890
|
+
lines.push(`${indentation}for (let ${cursor} = 0; ${cursor} < ${listLength}; ${cursor} += 1) {`);
|
|
2891
|
+
lines.push(`${indentation} const ${descriptor} = __velarCollectionListGetOwnPropertyDescriptor(${value}, ${cursor});`);
|
|
2892
|
+
lines.push(`${indentation} if (!${descriptor}?.enumerable || !${descriptor}.configurable || !${descriptor}.writable || !("value" in ${descriptor})) return null;`);
|
|
2893
|
+
lines.push(`${indentation} __velarCollectionListDefineProperty(${items}, ${cursor}, { value: __velarReactiveCollectionRead(${value}, ${cursor}, ${descriptor}.value), writable: true, enumerable: true, configurable: true });`);
|
|
2894
|
+
lines.push(`${indentation}}`);
|
|
2895
|
+
current.elements.forEach((child, index) => emit(child, `${items}[${index}]`));
|
|
2896
|
+
if (current.rest) {
|
|
2897
|
+
const rest = temporary("Rest");
|
|
2898
|
+
const restCursor = temporary("RestIndex");
|
|
2899
|
+
lines.push(`${indentation}const ${rest} = new __velarCollectionNativeArray(${listLength} - ${length});`);
|
|
2900
|
+
lines.push(`${indentation}for (let ${restCursor} = ${length}; ${restCursor} < ${listLength}; ${restCursor} += 1) {`);
|
|
2901
|
+
lines.push(`${indentation} __velarCollectionListDefineProperty(${rest}, ${restCursor} - ${length}, { value: ${items}[${restCursor}], writable: true, enumerable: true, configurable: true });`);
|
|
2902
|
+
lines.push(`${indentation}}`);
|
|
2903
|
+
bind(current.rest.name, rest);
|
|
2904
|
+
}
|
|
2905
|
+
break;
|
|
2906
|
+
}
|
|
2907
|
+
case "MatchObjectPattern": {
|
|
2908
|
+
this.needsCollectionHelpers = true;
|
|
2909
|
+
this.needsDirectCollectionInfrastructure = true;
|
|
2910
|
+
rejectUnless(`${value} !== null && typeof ${value} === "object" && !__velarCollectionListIsArray(${value})`);
|
|
2911
|
+
for (const entry of current.entries) {
|
|
2912
|
+
const descriptor = temporary("Field");
|
|
2913
|
+
const fieldValue = temporary("Value");
|
|
2914
|
+
lines.push(`${indentation}__velarReactiveCollectionTrack(${value}, ${JSON.stringify(entry.property)});`);
|
|
2915
|
+
lines.push(`${indentation}const ${descriptor} = __velarCollectionRecordGetOwnPropertyDescriptor(${value}, ${JSON.stringify(entry.property)});`);
|
|
2916
|
+
rejectUnless(`${descriptor}?.enumerable && "value" in ${descriptor}`);
|
|
2917
|
+
lines.push(`${indentation}const ${fieldValue} = __velarReactiveCollectionRead(${value}, ${JSON.stringify(entry.property)}, ${descriptor}.value);`);
|
|
2918
|
+
emit(entry.pattern, fieldValue);
|
|
2919
|
+
}
|
|
2920
|
+
if (current.rest) {
|
|
2921
|
+
const rest = temporary("Rest");
|
|
2922
|
+
const key = temporary("Key");
|
|
2923
|
+
const descriptor = temporary("RestField");
|
|
2924
|
+
const selected = current.entries.map((entry) => `${key} === ${JSON.stringify(entry.property)}`).join(" || ") || "false";
|
|
2925
|
+
rejectUnless(`__velarCollectionRecordOwnSymbols(${value}).length === 0`);
|
|
2926
|
+
lines.push(`${indentation}__velarReactiveCollectionTrack(${value});`);
|
|
2927
|
+
lines.push(`${indentation}const ${rest} = {};`);
|
|
2928
|
+
const fields = temporary("Fields");
|
|
2929
|
+
const fieldIndex = temporary("FieldIndex");
|
|
2930
|
+
lines.push(`${indentation}const ${fields} = __velarCollectionRecordOwnNames(${value});`);
|
|
2931
|
+
rejectUnless(`${fields}.length <= __velarMaxCollectionItems`);
|
|
2932
|
+
lines.push(`${indentation}for (let ${fieldIndex} = 0; ${fieldIndex} < ${fields}.length; ${fieldIndex} += 1) {`);
|
|
2933
|
+
lines.push(`${indentation} const ${key} = ${fields}[${fieldIndex}];`);
|
|
2934
|
+
lines.push(`${indentation} if (${selected}) continue;`);
|
|
2935
|
+
lines.push(`${indentation} const ${descriptor} = __velarCollectionRecordGetOwnPropertyDescriptor(${value}, ${key});`);
|
|
2936
|
+
lines.push(`${indentation} if (!${descriptor}?.enumerable) continue;`);
|
|
2937
|
+
lines.push(`${indentation} if (!("value" in ${descriptor})) return null;`);
|
|
2938
|
+
lines.push(`${indentation} __velarCollectionRecordDefineProperty(${rest}, ${key}, { value: __velarReactiveCollectionRead(${value}, ${key}, ${descriptor}.value), writable: true, enumerable: true, configurable: true });`);
|
|
2939
|
+
lines.push(`${indentation}}`);
|
|
2940
|
+
bind(current.rest.name, rest);
|
|
2941
|
+
}
|
|
2942
|
+
break;
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
};
|
|
2946
|
+
emit(pattern, valueName);
|
|
2947
|
+
return { lines, bindings: [...bindings].map(([name, value]) => ({ name, value })) };
|
|
2948
|
+
}
|
|
2949
|
+
emitBindingPatternStatements(pattern, value, binding, exported, depth, label) {
|
|
2950
|
+
const lines = [];
|
|
2951
|
+
const indentation = " ".repeat(depth);
|
|
2952
|
+
let temporary = 0;
|
|
2953
|
+
const nextTemporary = (kind, current) => `__velarBinding${kind}${current.span.start}_${temporary++}`;
|
|
2954
|
+
const declare = (name, expression) => {
|
|
2955
|
+
lines.push(`${indentation}${exported ? "export " : ""}${binding} ${name} = ${expression};`);
|
|
2956
|
+
};
|
|
2957
|
+
const emit = (current, currentValue) => {
|
|
2958
|
+
if (current.kind === "NameBindingPattern") {
|
|
2959
|
+
declare(current.name, currentValue);
|
|
2960
|
+
return;
|
|
2961
|
+
}
|
|
2962
|
+
if (current.kind === "ObjectBindingPattern") {
|
|
2963
|
+
this.needsCollectionHelpers = true;
|
|
2964
|
+
this.needsObjectBindingHelpers = true;
|
|
2965
|
+
const object = nextTemporary("Object", current);
|
|
2966
|
+
lines.push(`${indentation}const ${object} = __velarRequireBindingObject(${currentValue}, ${JSON.stringify(label)});`);
|
|
2967
|
+
for (const entry of current.entries) {
|
|
2968
|
+
const read = `__velarReadBindingField(${object}, ${JSON.stringify(entry.property)}, ${this.hints.optionalBindingEntries.has(entry.span.start)}, ${JSON.stringify(label)})`;
|
|
2969
|
+
if (entry.pattern.kind === "NameBindingPattern") {
|
|
2970
|
+
declare(entry.pattern.name, read);
|
|
2971
|
+
}
|
|
2972
|
+
else {
|
|
2973
|
+
const field = nextTemporary("Field", entry.pattern);
|
|
2974
|
+
lines.push(`${indentation}const ${field} = ${read};`);
|
|
2975
|
+
emit(entry.pattern, field);
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
if (current.rest) {
|
|
2979
|
+
declare(current.rest.name, `__velarBindingObjectRest(${object}, ${JSON.stringify(current.entries.map((entry) => entry.property))}, ${JSON.stringify(label)})`);
|
|
2980
|
+
}
|
|
2981
|
+
return;
|
|
2982
|
+
}
|
|
2983
|
+
this.needsCollectionHelpers = true;
|
|
2984
|
+
this.needsListBindingHelpers = true;
|
|
2985
|
+
const list = nextTemporary("List", current);
|
|
2986
|
+
lines.push(`${indentation}const ${list} = __velarRequireBindingList(${currentValue}, ${current.elements.length}, ${current.rest !== null}, ${JSON.stringify(label)});`);
|
|
2987
|
+
current.elements.forEach((element, index) => {
|
|
2988
|
+
if (!element)
|
|
2989
|
+
return;
|
|
2990
|
+
const read = `__velarReadBindingListItem(${list}, ${index})`;
|
|
2991
|
+
if (element.kind === "NameBindingPattern") {
|
|
2992
|
+
declare(element.name, read);
|
|
2993
|
+
}
|
|
2994
|
+
else {
|
|
2995
|
+
const item = nextTemporary("Item", element);
|
|
2996
|
+
lines.push(`${indentation}const ${item} = ${read};`);
|
|
2997
|
+
emit(element, item);
|
|
2998
|
+
}
|
|
2999
|
+
});
|
|
3000
|
+
if (current.rest) {
|
|
3001
|
+
declare(current.rest.name, `__velarBindingListRest(${list}, ${current.elements.length})`);
|
|
3002
|
+
}
|
|
3003
|
+
};
|
|
3004
|
+
emit(pattern, value);
|
|
3005
|
+
return lines;
|
|
3006
|
+
}
|
|
3007
|
+
escapeTemplateText(value) {
|
|
3008
|
+
return value.replaceAll("\\", "\\\\").replaceAll("\r", "\\r").replaceAll("`", "\\`").replaceAll("${", "\\${");
|
|
3009
|
+
}
|
|
3010
|
+
blockAlwaysReturns(statements) {
|
|
3011
|
+
for (const statement of statements) {
|
|
3012
|
+
if (statement.kind === "ReturnStatement" || statement.kind === "ThrowStatement")
|
|
3013
|
+
return true;
|
|
3014
|
+
if (statement.kind === "IfStatement" && statement.elseBody
|
|
3015
|
+
&& this.blockAlwaysReturns(statement.thenBody) && this.blockAlwaysReturns(statement.elseBody))
|
|
3016
|
+
return true;
|
|
3017
|
+
if (statement.kind === "MatchStatement" && this.hints.exhaustiveMatches.has(statement.span.start)
|
|
3018
|
+
&& statement.cases.every((branch) => this.blockAlwaysReturns(branch.body)))
|
|
3019
|
+
return true;
|
|
3020
|
+
if (statement.kind === "TryStatement") {
|
|
3021
|
+
if (statement.finallyBody && this.blockAlwaysReturns(statement.finallyBody))
|
|
3022
|
+
return true;
|
|
3023
|
+
if (statement.catchBody && this.blockAlwaysReturns(statement.tryBody) && this.blockAlwaysReturns(statement.catchBody))
|
|
3024
|
+
return true;
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
return false;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
function javaScriptIdentifiers(sources) {
|
|
3031
|
+
const identifiers = new Set();
|
|
3032
|
+
const identifierStart = (character) => /[A-Za-z_$]/u.test(character);
|
|
3033
|
+
const identifierPart = (character) => /[A-Za-z0-9_$]/u.test(character);
|
|
3034
|
+
for (const source of sources) {
|
|
3035
|
+
let index = 0;
|
|
3036
|
+
const skipQuoted = (quote) => {
|
|
3037
|
+
index += 1;
|
|
3038
|
+
while (index < source.length) {
|
|
3039
|
+
if (source[index] === "\\")
|
|
3040
|
+
index += 2;
|
|
3041
|
+
else if (source[index] === quote) {
|
|
3042
|
+
index += 1;
|
|
3043
|
+
return;
|
|
3044
|
+
}
|
|
3045
|
+
else
|
|
3046
|
+
index += 1;
|
|
3047
|
+
}
|
|
3048
|
+
};
|
|
3049
|
+
const skipLineComment = () => {
|
|
3050
|
+
index += 2;
|
|
3051
|
+
while (index < source.length && source[index] !== "\n" && source[index] !== "\r")
|
|
3052
|
+
index += 1;
|
|
3053
|
+
};
|
|
3054
|
+
const skipBlockComment = () => {
|
|
3055
|
+
index += 2;
|
|
3056
|
+
while (index < source.length) {
|
|
3057
|
+
if (source[index] === "*" && source[index + 1] === "/") {
|
|
3058
|
+
index += 2;
|
|
3059
|
+
return;
|
|
3060
|
+
}
|
|
3061
|
+
index += 1;
|
|
3062
|
+
}
|
|
3063
|
+
};
|
|
3064
|
+
let scanCode;
|
|
3065
|
+
const skipTemplate = () => {
|
|
3066
|
+
index += 1;
|
|
3067
|
+
while (index < source.length) {
|
|
3068
|
+
if (source[index] === "\\")
|
|
3069
|
+
index += 2;
|
|
3070
|
+
else if (source[index] === "`") {
|
|
3071
|
+
index += 1;
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3074
|
+
else if (source[index] === "$" && source[index + 1] === "{") {
|
|
3075
|
+
index += 2;
|
|
3076
|
+
scanCode(true);
|
|
3077
|
+
}
|
|
3078
|
+
else
|
|
3079
|
+
index += 1;
|
|
3080
|
+
}
|
|
3081
|
+
};
|
|
3082
|
+
scanCode = (templateExpression) => {
|
|
3083
|
+
let braceDepth = 0;
|
|
3084
|
+
while (index < source.length) {
|
|
3085
|
+
const character = source[index];
|
|
3086
|
+
if (character === "'" || character === '"') {
|
|
3087
|
+
skipQuoted(character);
|
|
3088
|
+
continue;
|
|
3089
|
+
}
|
|
3090
|
+
if (character === "`") {
|
|
3091
|
+
skipTemplate();
|
|
3092
|
+
continue;
|
|
3093
|
+
}
|
|
3094
|
+
if (character === "/" && source[index + 1] === "/") {
|
|
3095
|
+
skipLineComment();
|
|
3096
|
+
continue;
|
|
3097
|
+
}
|
|
3098
|
+
if (character === "/" && source[index + 1] === "*") {
|
|
3099
|
+
skipBlockComment();
|
|
3100
|
+
continue;
|
|
3101
|
+
}
|
|
3102
|
+
if (character === "{") {
|
|
3103
|
+
braceDepth += 1;
|
|
3104
|
+
index += 1;
|
|
3105
|
+
continue;
|
|
3106
|
+
}
|
|
3107
|
+
if (character === "}" && templateExpression) {
|
|
3108
|
+
if (braceDepth === 0) {
|
|
3109
|
+
index += 1;
|
|
3110
|
+
return;
|
|
3111
|
+
}
|
|
3112
|
+
braceDepth -= 1;
|
|
3113
|
+
index += 1;
|
|
3114
|
+
continue;
|
|
3115
|
+
}
|
|
3116
|
+
if (identifierStart(character)) {
|
|
3117
|
+
const start = index;
|
|
3118
|
+
index += 1;
|
|
3119
|
+
while (index < source.length && identifierPart(source[index]))
|
|
3120
|
+
index += 1;
|
|
3121
|
+
identifiers.add(source.slice(start, index));
|
|
3122
|
+
continue;
|
|
3123
|
+
}
|
|
3124
|
+
index += 1;
|
|
3125
|
+
}
|
|
3126
|
+
};
|
|
3127
|
+
scanCode(false);
|
|
3128
|
+
}
|
|
3129
|
+
return identifiers;
|
|
3130
|
+
}
|
|
3131
|
+
const base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
3132
|
+
function encodeVlq(value) {
|
|
3133
|
+
let remaining = value < 0 ? ((-value) << 1) | 1 : value << 1;
|
|
3134
|
+
let output = "";
|
|
3135
|
+
do {
|
|
3136
|
+
let digit = remaining & 31;
|
|
3137
|
+
remaining >>>= 5;
|
|
3138
|
+
if (remaining > 0)
|
|
3139
|
+
digit |= 32;
|
|
3140
|
+
output += base64[digit];
|
|
3141
|
+
} while (remaining > 0);
|
|
3142
|
+
return output;
|
|
3143
|
+
}
|
|
3144
|
+
function generatedLineAt(lineStarts, offset) {
|
|
3145
|
+
let low = 0;
|
|
3146
|
+
let high = lineStarts.length - 1;
|
|
3147
|
+
while (low <= high) {
|
|
3148
|
+
const middle = Math.floor((low + high) / 2);
|
|
3149
|
+
if (lineStarts[middle] <= offset)
|
|
3150
|
+
low = middle + 1;
|
|
3151
|
+
else
|
|
3152
|
+
high = middle - 1;
|
|
3153
|
+
}
|
|
3154
|
+
return Math.max(0, high);
|
|
3155
|
+
}
|
|
3156
|
+
function sourceMapFor(code, generatedMappings, source) {
|
|
3157
|
+
const lineStarts = [0];
|
|
3158
|
+
for (let index = 0; index < code.length; index += 1) {
|
|
3159
|
+
if (code[index] === "\n")
|
|
3160
|
+
lineStarts.push(index + 1);
|
|
3161
|
+
}
|
|
3162
|
+
const byLine = new Map();
|
|
3163
|
+
for (const mapping of generatedMappings) {
|
|
3164
|
+
const line = generatedLineAt(lineStarts, mapping.offset);
|
|
3165
|
+
const entries = byLine.get(line) ?? [];
|
|
3166
|
+
entries.push({ column: mapping.offset - lineStarts[line], span: mapping.sourceSpan });
|
|
3167
|
+
byLine.set(line, entries);
|
|
3168
|
+
}
|
|
3169
|
+
let previousSource = 0;
|
|
3170
|
+
let previousLine = 0;
|
|
3171
|
+
let previousColumn = 0;
|
|
3172
|
+
const mappings = lineStarts.map((_, line) => {
|
|
3173
|
+
let previousGeneratedColumn = 0;
|
|
3174
|
+
return (byLine.get(line) ?? []).sort((left, right) => left.column - right.column).map((mapped) => {
|
|
3175
|
+
const location = source.location(mapped.span.start);
|
|
3176
|
+
const originalLine = location.line - 1;
|
|
3177
|
+
const originalColumn = location.column - 1;
|
|
3178
|
+
const segment = [
|
|
3179
|
+
encodeVlq(mapped.column - previousGeneratedColumn),
|
|
3180
|
+
encodeVlq(-previousSource),
|
|
3181
|
+
encodeVlq(originalLine - previousLine),
|
|
3182
|
+
encodeVlq(originalColumn - previousColumn),
|
|
3183
|
+
].join("");
|
|
3184
|
+
previousGeneratedColumn = mapped.column;
|
|
3185
|
+
previousSource = 0;
|
|
3186
|
+
previousLine = originalLine;
|
|
3187
|
+
previousColumn = originalColumn;
|
|
3188
|
+
return segment;
|
|
3189
|
+
}).join(",");
|
|
3190
|
+
}).join(";");
|
|
3191
|
+
return JSON.stringify({
|
|
3192
|
+
version: 3,
|
|
3193
|
+
// Source-map sources are URLs, not host path strings. A Windows drive path
|
|
3194
|
+
// starts with what URL parsers treat as a scheme (`C:`), so Node cannot
|
|
3195
|
+
// map the generated frame back to VelarScript unless the drive path is a
|
|
3196
|
+
// real file URL. POSIX and project-relative paths keep their existing form.
|
|
3197
|
+
sources: [/^[A-Za-z]:[\\/]/u.test(source.path)
|
|
3198
|
+
? `file:///${source.path.replaceAll("\\", "/")}`
|
|
3199
|
+
: source.path.replaceAll("\\", "/")],
|
|
3200
|
+
sourcesContent: [source.text],
|
|
3201
|
+
names: [],
|
|
3202
|
+
mappings,
|
|
3203
|
+
});
|
|
3204
|
+
}
|
|
3205
|
+
function mappedSource(source, sourceStart) {
|
|
3206
|
+
const mappings = source.length > 0
|
|
3207
|
+
? [{ offset: 0, sourceSpan: { start: sourceStart, end: sourceStart + 1 } }]
|
|
3208
|
+
: [];
|
|
3209
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
3210
|
+
if (source[index] !== "\n" || index + 1 >= source.length)
|
|
3211
|
+
continue;
|
|
3212
|
+
mappings.push({
|
|
3213
|
+
offset: index + 1,
|
|
3214
|
+
sourceSpan: { start: sourceStart + index + 1, end: sourceStart + index + 2 },
|
|
3215
|
+
});
|
|
3216
|
+
}
|
|
3217
|
+
return { code: source, mappings };
|
|
3218
|
+
}
|
|
3219
|
+
function emitCheckedEmbeddedJavaScript(statement, factoryName) {
|
|
3220
|
+
const relative = (value) => ({
|
|
3221
|
+
start: value.start - statement.sourceSpan.start,
|
|
3222
|
+
end: value.end - statement.sourceSpan.start,
|
|
3223
|
+
});
|
|
3224
|
+
const blank = (value) => value.replace(/[^\r\n]/gu, " ");
|
|
3225
|
+
const body = [...statement.factoryEdits]
|
|
3226
|
+
.sort((left, right) => right.span.start - left.span.start)
|
|
3227
|
+
.reduce((current, edit) => {
|
|
3228
|
+
const target = relative(edit.span);
|
|
3229
|
+
const replacement = edit.replacement + blank(current.slice(target.start + edit.replacement.length, target.end));
|
|
3230
|
+
return `${current.slice(0, target.start)}${replacement}${current.slice(target.end)}`;
|
|
3231
|
+
}, statement.source);
|
|
3232
|
+
let code = "";
|
|
3233
|
+
const mappings = [];
|
|
3234
|
+
const appendMapped = (value, absoluteStart) => {
|
|
3235
|
+
const mapped = mappedSource(value, absoluteStart);
|
|
3236
|
+
const offset = code.length;
|
|
3237
|
+
code += value;
|
|
3238
|
+
mappings.push(...mapped.mappings.map((mapping) => ({ ...mapping, offset: offset + mapping.offset })));
|
|
3239
|
+
};
|
|
3240
|
+
for (const imported of statement.imports) {
|
|
3241
|
+
const target = relative(imported.span);
|
|
3242
|
+
appendMapped(statement.source.slice(target.start, target.end), imported.span.start);
|
|
3243
|
+
if (!code.endsWith("\n") && !code.endsWith("\r"))
|
|
3244
|
+
code += "\n";
|
|
3245
|
+
}
|
|
3246
|
+
code += `export function ${factoryName}(${statement.captures.map((capture) => capture.name).join(", ")}) {\n`;
|
|
3247
|
+
appendMapped(body, statement.sourceSpan.start);
|
|
3248
|
+
if (code.length > 0 && !code.endsWith("\n") && !code.endsWith("\r"))
|
|
3249
|
+
code += "\n";
|
|
3250
|
+
const entries = statement.exports.map((item) => `${JSON.stringify(item.name)}: ${item.local}`).join(", ");
|
|
3251
|
+
code += `return { ${entries} };\n}\n`;
|
|
3252
|
+
return { code, mappings };
|
|
3253
|
+
}
|
|
3254
|
+
//# sourceMappingURL=emitter.js.map
|