@tsonic/emitter 0.0.67 → 0.0.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/array.test.js +67 -3
- package/dist/array.test.js.map +1 -1
- package/dist/core/format/backend-ast/index.d.ts +1 -1
- package/dist/core/format/backend-ast/index.d.ts.map +1 -1
- package/dist/core/format/backend-ast/index.js.map +1 -1
- package/dist/core/format/backend-ast/printer.d.ts.map +1 -1
- package/dist/core/format/backend-ast/printer.js +5 -1
- package/dist/core/format/backend-ast/printer.js.map +1 -1
- package/dist/core/format/backend-ast/types.d.ts +6 -1
- package/dist/core/format/backend-ast/types.d.ts.map +1 -1
- package/dist/core/format/module-emitter/orchestrator.d.ts.map +1 -1
- package/dist/core/format/module-emitter/orchestrator.js +7 -0
- package/dist/core/format/module-emitter/orchestrator.js.map +1 -1
- package/dist/core/module-emitter.test.js +125 -0
- package/dist/core/module-emitter.test.js.map +1 -1
- package/dist/core/semantic/imports.d.ts.map +1 -1
- package/dist/core/semantic/imports.js +62 -4
- package/dist/core/semantic/imports.js.map +1 -1
- package/dist/core/semantic/imports.test.js +186 -0
- package/dist/core/semantic/imports.test.js.map +1 -1
- package/dist/core/semantic/module-map.d.ts.map +1 -1
- package/dist/core/semantic/module-map.js +6 -0
- package/dist/core/semantic/module-map.js.map +1 -1
- package/dist/core/semantic/module-map.test.js +52 -1
- package/dist/core/semantic/module-map.test.js.map +1 -1
- package/dist/core/semantic/mutable-storage.d.ts +10 -0
- package/dist/core/semantic/mutable-storage.d.ts.map +1 -0
- package/dist/core/semantic/mutable-storage.js +479 -0
- package/dist/core/semantic/mutable-storage.js.map +1 -0
- package/dist/core/semantic/type-resolution.d.ts.map +1 -1
- package/dist/core/semantic/type-resolution.js +14 -5
- package/dist/core/semantic/type-resolution.js.map +1 -1
- package/dist/core/semantic/type-resolution.test.js +2 -0
- package/dist/core/semantic/type-resolution.test.js.map +1 -1
- package/dist/core/semantic/unsafe.d.ts.map +1 -1
- package/dist/core/semantic/unsafe.js +4 -0
- package/dist/core/semantic/unsafe.js.map +1 -1
- package/dist/emitter-types/core.d.ts +10 -0
- package/dist/emitter-types/core.d.ts.map +1 -1
- package/dist/expression-emitter.d.ts.map +1 -1
- package/dist/expression-emitter.js +73 -3
- package/dist/expression-emitter.js.map +1 -1
- package/dist/expressions/access.d.ts +2 -2
- package/dist/expressions/access.d.ts.map +1 -1
- package/dist/expressions/access.js +16 -2
- package/dist/expressions/access.js.map +1 -1
- package/dist/expressions/calls/call-emitter.d.ts.map +1 -1
- package/dist/expressions/calls/call-emitter.js +1350 -122
- package/dist/expressions/calls/call-emitter.js.map +1 -1
- package/dist/expressions/collections.d.ts +3 -0
- package/dist/expressions/collections.d.ts.map +1 -1
- package/dist/expressions/collections.js +126 -43
- package/dist/expressions/collections.js.map +1 -1
- package/dist/expressions/functions.d.ts.map +1 -1
- package/dist/expressions/functions.js +126 -50
- package/dist/expressions/functions.js.map +1 -1
- package/dist/expressions/index.test.js +447 -0
- package/dist/expressions/index.test.js.map +1 -1
- package/dist/expressions/operators/assignment-emitter.d.ts.map +1 -1
- package/dist/expressions/operators/assignment-emitter.js +5 -1
- package/dist/expressions/operators/assignment-emitter.js.map +1 -1
- package/dist/expressions/parentheses.d.ts.map +1 -1
- package/dist/expressions/parentheses.js +4 -0
- package/dist/expressions/parentheses.js.map +1 -1
- package/dist/integration.test.js +343 -0
- package/dist/integration.test.js.map +1 -1
- package/dist/json-aot-generic.test.js +114 -0
- package/dist/json-aot-generic.test.js.map +1 -1
- package/dist/patterns.d.ts.map +1 -1
- package/dist/patterns.js +194 -0
- package/dist/patterns.js.map +1 -1
- package/dist/patterns.test.js +79 -0
- package/dist/patterns.test.js.map +1 -1
- package/dist/statements/classes/members/properties.d.ts.map +1 -1
- package/dist/statements/classes/members/properties.js +10 -3
- package/dist/statements/classes/members/properties.js.map +1 -1
- package/dist/statements/classes/members/static-readonly-properties.test.js +74 -1
- package/dist/statements/classes/members/static-readonly-properties.test.js.map +1 -1
- package/dist/statements/classes/properties.d.ts.map +1 -1
- package/dist/statements/classes/properties.js +4 -2
- package/dist/statements/classes/properties.js.map +1 -1
- package/dist/statements/control/conditionals/if-emitter.d.ts.map +1 -1
- package/dist/statements/control/conditionals/if-emitter.js +137 -0
- package/dist/statements/control/conditionals/if-emitter.js.map +1 -1
- package/dist/statements/control/loops.d.ts.map +1 -1
- package/dist/statements/control/loops.js +65 -3
- package/dist/statements/control/loops.js.map +1 -1
- package/dist/statements/declarations/classes.d.ts.map +1 -1
- package/dist/statements/declarations/classes.js +4 -1
- package/dist/statements/declarations/classes.js.map +1 -1
- package/dist/statements/declarations/interfaces-mutable-storage.test.d.ts +2 -0
- package/dist/statements/declarations/interfaces-mutable-storage.test.d.ts.map +1 -0
- package/dist/statements/declarations/interfaces-mutable-storage.test.js +84 -0
- package/dist/statements/declarations/interfaces-mutable-storage.test.js.map +1 -0
- package/dist/statements/declarations/interfaces.d.ts.map +1 -1
- package/dist/statements/declarations/interfaces.js +4 -1
- package/dist/statements/declarations/interfaces.js.map +1 -1
- package/dist/statements/declarations/variables.d.ts.map +1 -1
- package/dist/statements/declarations/variables.js +89 -2
- package/dist/statements/declarations/variables.js.map +1 -1
- package/dist/types/functions.d.ts.map +1 -1
- package/dist/types/functions.js +3 -1
- package/dist/types/functions.js.map +1 -1
- package/dist/types/references.d.ts.map +1 -1
- package/dist/types/references.js +19 -16
- package/dist/types/references.js.map +1 -1
- package/dist/types/references.test.js +17 -0
- package/dist/types/references.test.js.map +1 -1
- package/dist/types/unions.d.ts.map +1 -1
- package/dist/types/unions.js +41 -16
- package/dist/types/unions.js.map +1 -1
- package/package.json +2 -2
|
@@ -8,8 +8,10 @@ import { emitMemberAccess } from "../access.js";
|
|
|
8
8
|
import { isLValue, getPassingModifierFromCast, isJsonSerializerCall, isGlobalJsonCall, isInstanceMemberAccess, shouldEmitFluentExtensionCall, getTypeNamespace, registerJsonAotType, needsIntCast, isPromiseChainMethod, isAsyncWrapperType, } from "./call-analysis.js";
|
|
9
9
|
import { extractCalleeNameFromAst } from "../../core/format/backend-ast/utils.js";
|
|
10
10
|
import { resolveImportPath } from "../../core/semantic/index.js";
|
|
11
|
+
import { containsTypeParameter } from "../../core/semantic/type-resolution.js";
|
|
12
|
+
import { allocateLocalName } from "../../core/format/local-names.js";
|
|
11
13
|
/**
|
|
12
|
-
* Wrap an expression AST with an optional argument modifier (ref/out/in
|
|
14
|
+
* Wrap an expression AST with an optional argument modifier (ref/out/in).
|
|
13
15
|
*/
|
|
14
16
|
const wrapArgModifier = (modifier, expr) => modifier
|
|
15
17
|
? { kind: "argumentModifierExpression", modifier, expression: expr }
|
|
@@ -25,6 +27,221 @@ const wrapIntCast = (needsCast, expr) => needsCast
|
|
|
25
27
|
}
|
|
26
28
|
: expr;
|
|
27
29
|
const stripClrGenericArity = (typeName) => typeName.replace(/`\d+$/, "");
|
|
30
|
+
const nativeArrayMutationMembers = new Set([
|
|
31
|
+
"push",
|
|
32
|
+
"pop",
|
|
33
|
+
"shift",
|
|
34
|
+
"unshift",
|
|
35
|
+
"splice",
|
|
36
|
+
"sort",
|
|
37
|
+
"reverse",
|
|
38
|
+
"fill",
|
|
39
|
+
"copyWithin",
|
|
40
|
+
]);
|
|
41
|
+
const returnsMutatedArrayMember = (memberName) => memberName === "sort" ||
|
|
42
|
+
memberName === "reverse" ||
|
|
43
|
+
memberName === "fill" ||
|
|
44
|
+
memberName === "copyWithin";
|
|
45
|
+
const createVarLocal = (name, initializer) => ({
|
|
46
|
+
kind: "localDeclarationStatement",
|
|
47
|
+
modifiers: [],
|
|
48
|
+
type: { kind: "varType" },
|
|
49
|
+
declarators: [{ name, initializer }],
|
|
50
|
+
});
|
|
51
|
+
const captureAssignableArrayTarget = (expr, context) => {
|
|
52
|
+
const [receiverAst, receiverContext] = emitExpressionAst(expr, context);
|
|
53
|
+
if (receiverAst.kind === "identifierExpression") {
|
|
54
|
+
return {
|
|
55
|
+
readExpression: receiverAst,
|
|
56
|
+
writeExpression: receiverAst,
|
|
57
|
+
setupStatements: [],
|
|
58
|
+
context: receiverContext,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (receiverAst.kind === "memberAccessExpression") {
|
|
62
|
+
const objectTemp = allocateLocalName("__tsonic_arrayTarget", receiverContext);
|
|
63
|
+
const objectIdentifier = {
|
|
64
|
+
kind: "identifierExpression",
|
|
65
|
+
identifier: objectTemp.emittedName,
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
readExpression: {
|
|
69
|
+
kind: "memberAccessExpression",
|
|
70
|
+
expression: objectIdentifier,
|
|
71
|
+
memberName: receiverAst.memberName,
|
|
72
|
+
},
|
|
73
|
+
writeExpression: {
|
|
74
|
+
kind: "memberAccessExpression",
|
|
75
|
+
expression: objectIdentifier,
|
|
76
|
+
memberName: receiverAst.memberName,
|
|
77
|
+
},
|
|
78
|
+
setupStatements: [
|
|
79
|
+
createVarLocal(objectTemp.emittedName, receiverAst.expression),
|
|
80
|
+
],
|
|
81
|
+
context: objectTemp.context,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (receiverAst.kind === "elementAccessExpression" &&
|
|
85
|
+
receiverAst.arguments.length === 1) {
|
|
86
|
+
const objectTemp = allocateLocalName("__tsonic_arrayTarget", receiverContext);
|
|
87
|
+
const indexTemp = allocateLocalName("__tsonic_arrayIndex", objectTemp.context);
|
|
88
|
+
const objectIdentifier = {
|
|
89
|
+
kind: "identifierExpression",
|
|
90
|
+
identifier: objectTemp.emittedName,
|
|
91
|
+
};
|
|
92
|
+
const indexIdentifier = {
|
|
93
|
+
kind: "identifierExpression",
|
|
94
|
+
identifier: indexTemp.emittedName,
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
readExpression: {
|
|
98
|
+
kind: "elementAccessExpression",
|
|
99
|
+
expression: objectIdentifier,
|
|
100
|
+
arguments: [indexIdentifier],
|
|
101
|
+
},
|
|
102
|
+
writeExpression: {
|
|
103
|
+
kind: "elementAccessExpression",
|
|
104
|
+
expression: objectIdentifier,
|
|
105
|
+
arguments: [indexIdentifier],
|
|
106
|
+
},
|
|
107
|
+
setupStatements: [
|
|
108
|
+
createVarLocal(objectTemp.emittedName, receiverAst.expression),
|
|
109
|
+
createVarLocal(indexTemp.emittedName, receiverAst.arguments[0]),
|
|
110
|
+
],
|
|
111
|
+
context: indexTemp.context,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
};
|
|
116
|
+
const emitArrayMutationInteropCall = (expr, context) => {
|
|
117
|
+
if (expr.isOptional)
|
|
118
|
+
return undefined;
|
|
119
|
+
if (expr.callee.kind !== "memberAccess")
|
|
120
|
+
return undefined;
|
|
121
|
+
if (expr.callee.isComputed)
|
|
122
|
+
return undefined;
|
|
123
|
+
if (typeof expr.callee.property !== "string")
|
|
124
|
+
return undefined;
|
|
125
|
+
if (!nativeArrayMutationMembers.has(expr.callee.property))
|
|
126
|
+
return undefined;
|
|
127
|
+
if (!isLValue(expr.callee.object))
|
|
128
|
+
return undefined;
|
|
129
|
+
const binding = expr.callee.memberBinding;
|
|
130
|
+
if (!binding || binding.isExtensionMethod)
|
|
131
|
+
return undefined;
|
|
132
|
+
const receiverType = expr.callee.object.inferredType;
|
|
133
|
+
if (!receiverType || receiverType.kind !== "arrayType")
|
|
134
|
+
return undefined;
|
|
135
|
+
const captured = captureAssignableArrayTarget(expr.callee.object, context);
|
|
136
|
+
if (!captured)
|
|
137
|
+
return undefined;
|
|
138
|
+
let currentContext = captured.context;
|
|
139
|
+
const [elementTypeAst, elementTypeContext] = emitTypeAst(receiverType.elementType, currentContext);
|
|
140
|
+
currentContext = elementTypeContext;
|
|
141
|
+
const wrapperTemp = allocateLocalName("__tsonic_arrayWrapper", currentContext);
|
|
142
|
+
currentContext = wrapperTemp.context;
|
|
143
|
+
const resultTemp = allocateLocalName("__tsonic_arrayResult", currentContext);
|
|
144
|
+
currentContext = resultTemp.context;
|
|
145
|
+
const wrapperIdentifier = {
|
|
146
|
+
kind: "identifierExpression",
|
|
147
|
+
identifier: wrapperTemp.emittedName,
|
|
148
|
+
};
|
|
149
|
+
const resultIdentifier = {
|
|
150
|
+
kind: "identifierExpression",
|
|
151
|
+
identifier: resultTemp.emittedName,
|
|
152
|
+
};
|
|
153
|
+
const [argAsts, argContext] = emitCallArguments(expr.arguments, expr, currentContext);
|
|
154
|
+
currentContext = argContext;
|
|
155
|
+
const mutationCall = {
|
|
156
|
+
kind: "invocationExpression",
|
|
157
|
+
expression: {
|
|
158
|
+
kind: "memberAccessExpression",
|
|
159
|
+
expression: wrapperIdentifier,
|
|
160
|
+
memberName: binding.member,
|
|
161
|
+
},
|
|
162
|
+
arguments: argAsts,
|
|
163
|
+
};
|
|
164
|
+
const mutatedArrayAst = {
|
|
165
|
+
kind: "invocationExpression",
|
|
166
|
+
expression: {
|
|
167
|
+
kind: "memberAccessExpression",
|
|
168
|
+
expression: wrapperIdentifier,
|
|
169
|
+
memberName: "toArray",
|
|
170
|
+
},
|
|
171
|
+
arguments: [],
|
|
172
|
+
};
|
|
173
|
+
let returnExpression = resultIdentifier;
|
|
174
|
+
if (expr.callee.property === "splice") {
|
|
175
|
+
returnExpression = {
|
|
176
|
+
kind: "invocationExpression",
|
|
177
|
+
expression: {
|
|
178
|
+
kind: "memberAccessExpression",
|
|
179
|
+
expression: resultIdentifier,
|
|
180
|
+
memberName: "toArray",
|
|
181
|
+
},
|
|
182
|
+
arguments: [],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
else if (returnsMutatedArrayMember(expr.callee.property)) {
|
|
186
|
+
returnExpression = mutatedArrayAst;
|
|
187
|
+
}
|
|
188
|
+
const returnType = expr.inferredType ?? receiverType;
|
|
189
|
+
const [returnTypeAst, returnTypeContext] = emitTypeAst(returnType, currentContext);
|
|
190
|
+
currentContext = returnTypeContext;
|
|
191
|
+
const lambdaAst = {
|
|
192
|
+
kind: "lambdaExpression",
|
|
193
|
+
isAsync: false,
|
|
194
|
+
parameters: [],
|
|
195
|
+
body: {
|
|
196
|
+
kind: "blockStatement",
|
|
197
|
+
statements: [
|
|
198
|
+
...captured.setupStatements,
|
|
199
|
+
createVarLocal(wrapperTemp.emittedName, {
|
|
200
|
+
kind: "objectCreationExpression",
|
|
201
|
+
type: {
|
|
202
|
+
kind: "identifierType",
|
|
203
|
+
name: "global::Tsonic.JSRuntime.JSArray",
|
|
204
|
+
typeArguments: [elementTypeAst],
|
|
205
|
+
},
|
|
206
|
+
arguments: [captured.readExpression],
|
|
207
|
+
}),
|
|
208
|
+
createVarLocal(resultTemp.emittedName, mutationCall),
|
|
209
|
+
{
|
|
210
|
+
kind: "expressionStatement",
|
|
211
|
+
expression: {
|
|
212
|
+
kind: "assignmentExpression",
|
|
213
|
+
operatorToken: "=",
|
|
214
|
+
left: captured.writeExpression,
|
|
215
|
+
right: mutatedArrayAst,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
kind: "returnStatement",
|
|
220
|
+
expression: returnExpression,
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
const delegateCastAst = {
|
|
226
|
+
kind: "castExpression",
|
|
227
|
+
type: buildDelegateType([], returnTypeAst),
|
|
228
|
+
expression: {
|
|
229
|
+
kind: "parenthesizedExpression",
|
|
230
|
+
expression: lambdaAst,
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
return [
|
|
234
|
+
wrapIntCast(needsIntCast(expr, expr.callee.property), {
|
|
235
|
+
kind: "invocationExpression",
|
|
236
|
+
expression: {
|
|
237
|
+
kind: "parenthesizedExpression",
|
|
238
|
+
expression: delegateCastAst,
|
|
239
|
+
},
|
|
240
|
+
arguments: [],
|
|
241
|
+
}),
|
|
242
|
+
currentContext,
|
|
243
|
+
];
|
|
244
|
+
};
|
|
28
245
|
const emitArrayWrapperInteropCall = (expr, context) => {
|
|
29
246
|
if (expr.isOptional)
|
|
30
247
|
return undefined;
|
|
@@ -142,11 +359,74 @@ const callbackParameterCount = (callbackExpr) => {
|
|
|
142
359
|
}
|
|
143
360
|
return 1;
|
|
144
361
|
};
|
|
362
|
+
const collectBlockReturnTypes = (block) => {
|
|
363
|
+
const collectFromStatement = (statement) => {
|
|
364
|
+
switch (statement.kind) {
|
|
365
|
+
case "returnStatement":
|
|
366
|
+
return statement.expression?.inferredType
|
|
367
|
+
? [statement.expression.inferredType]
|
|
368
|
+
: [];
|
|
369
|
+
case "blockStatement":
|
|
370
|
+
return statement.statements.flatMap(collectFromStatement);
|
|
371
|
+
case "ifStatement":
|
|
372
|
+
return [
|
|
373
|
+
...collectFromStatement(statement.thenStatement),
|
|
374
|
+
...(statement.elseStatement
|
|
375
|
+
? collectFromStatement(statement.elseStatement)
|
|
376
|
+
: []),
|
|
377
|
+
];
|
|
378
|
+
case "whileStatement":
|
|
379
|
+
case "forStatement":
|
|
380
|
+
case "forOfStatement":
|
|
381
|
+
case "forInStatement":
|
|
382
|
+
return collectFromStatement(statement.body);
|
|
383
|
+
case "switchStatement":
|
|
384
|
+
return statement.cases.flatMap((switchCase) => switchCase.statements.flatMap(collectFromStatement));
|
|
385
|
+
case "tryStatement":
|
|
386
|
+
return [
|
|
387
|
+
...statement.tryBlock.statements.flatMap(collectFromStatement),
|
|
388
|
+
...(statement.catchClause
|
|
389
|
+
? statement.catchClause.body.statements.flatMap(collectFromStatement)
|
|
390
|
+
: []),
|
|
391
|
+
...(statement.finallyBlock
|
|
392
|
+
? statement.finallyBlock.statements.flatMap(collectFromStatement)
|
|
393
|
+
: []),
|
|
394
|
+
];
|
|
395
|
+
case "functionDeclaration":
|
|
396
|
+
case "classDeclaration":
|
|
397
|
+
case "interfaceDeclaration":
|
|
398
|
+
case "enumDeclaration":
|
|
399
|
+
case "typeAliasDeclaration":
|
|
400
|
+
return [];
|
|
401
|
+
default:
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
return block.statements.flatMap(collectFromStatement);
|
|
406
|
+
};
|
|
407
|
+
const getCallbackDelegateReturnType = (callbackExpr) => {
|
|
408
|
+
if ((callbackExpr.kind === "arrowFunction" ||
|
|
409
|
+
callbackExpr.kind === "functionExpression") &&
|
|
410
|
+
callbackExpr.body.kind === "blockStatement") {
|
|
411
|
+
const returnTypes = collectBlockReturnTypes(callbackExpr.body);
|
|
412
|
+
const concreteReturnTypes = returnTypes.filter((type) => !isVoidOrUnknownIrType(type));
|
|
413
|
+
if (concreteReturnTypes.length === 0) {
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
const deduped = concreteReturnTypes.filter((type, index, all) => all.findIndex((candidate) => stableIrTypeKey(candidate) === stableIrTypeKey(type)) === index);
|
|
417
|
+
if (deduped.length === 1) {
|
|
418
|
+
return deduped[0];
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
kind: "unionType",
|
|
422
|
+
types: deduped,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
return getCallbackReturnType(callbackExpr);
|
|
426
|
+
};
|
|
145
427
|
const callbackReturnsAsyncWrapper = (callbackExpr) => {
|
|
146
|
-
const
|
|
147
|
-
return
|
|
148
|
-
? isAsyncWrapperType(callbackType.returnType)
|
|
149
|
-
: false;
|
|
428
|
+
const delegateReturnType = getCallbackDelegateReturnType(callbackExpr);
|
|
429
|
+
return delegateReturnType ? isAsyncWrapperType(delegateReturnType) : false;
|
|
150
430
|
};
|
|
151
431
|
const buildInvocation = (expression, args) => ({
|
|
152
432
|
kind: "invocationExpression",
|
|
@@ -178,32 +458,763 @@ const buildDelegateType = (parameterTypes, returnType) => {
|
|
|
178
458
|
typeArguments: parameterTypes,
|
|
179
459
|
};
|
|
180
460
|
}
|
|
181
|
-
return {
|
|
182
|
-
kind: "identifierType",
|
|
183
|
-
name: "global::System.Func",
|
|
184
|
-
typeArguments: [...parameterTypes, returnType],
|
|
185
|
-
};
|
|
461
|
+
return {
|
|
462
|
+
kind: "identifierType",
|
|
463
|
+
name: "global::System.Func",
|
|
464
|
+
typeArguments: [...parameterTypes, returnType],
|
|
465
|
+
};
|
|
466
|
+
};
|
|
467
|
+
const isVoidOrUnknownIrType = (type) => type === undefined ||
|
|
468
|
+
type.kind === "voidType" ||
|
|
469
|
+
type.kind === "unknownType" ||
|
|
470
|
+
(type.kind === "primitiveType" && type.name === "undefined");
|
|
471
|
+
const getCallbackReturnType = (callbackExpr) => {
|
|
472
|
+
if (callbackExpr.kind === "arrowFunction" &&
|
|
473
|
+
callbackExpr.body.kind !== "blockStatement" &&
|
|
474
|
+
!isVoidOrUnknownIrType(callbackExpr.body.inferredType)) {
|
|
475
|
+
return callbackExpr.body.inferredType;
|
|
476
|
+
}
|
|
477
|
+
const declared = callbackExpr.inferredType?.kind === "functionType"
|
|
478
|
+
? callbackExpr.inferredType.returnType
|
|
479
|
+
: undefined;
|
|
480
|
+
if (!isVoidOrUnknownIrType(declared)) {
|
|
481
|
+
return declared;
|
|
482
|
+
}
|
|
483
|
+
if (callbackExpr.kind === "arrowFunction" &&
|
|
484
|
+
callbackExpr.body.kind !== "blockStatement") {
|
|
485
|
+
return callbackExpr.body.inferredType;
|
|
486
|
+
}
|
|
487
|
+
return undefined;
|
|
488
|
+
};
|
|
489
|
+
const getFunctionValueSignature = (expr) => {
|
|
490
|
+
const calleeType = expr.callee.inferredType;
|
|
491
|
+
if (!calleeType || calleeType.kind !== "functionType")
|
|
492
|
+
return undefined;
|
|
493
|
+
if (expr.callee.kind === "identifier" && expr.callee.resolvedClrType) {
|
|
494
|
+
return undefined;
|
|
495
|
+
}
|
|
496
|
+
if (expr.callee.kind === "memberAccess" && expr.callee.memberBinding) {
|
|
497
|
+
return undefined;
|
|
498
|
+
}
|
|
499
|
+
return calleeType;
|
|
500
|
+
};
|
|
501
|
+
const emitFunctionValueCallArguments = (args, signature, expr, context) => {
|
|
502
|
+
let currentContext = context;
|
|
503
|
+
const argAsts = [];
|
|
504
|
+
const parameters = signature.parameters;
|
|
505
|
+
const extractTupleRestCandidates = (type) => {
|
|
506
|
+
if (!type)
|
|
507
|
+
return undefined;
|
|
508
|
+
if (type.kind === "tupleType") {
|
|
509
|
+
return [type.elementTypes];
|
|
510
|
+
}
|
|
511
|
+
if (type.kind !== "unionType") {
|
|
512
|
+
return undefined;
|
|
513
|
+
}
|
|
514
|
+
const candidates = [];
|
|
515
|
+
for (const member of type.types) {
|
|
516
|
+
if (!member || member.kind !== "tupleType") {
|
|
517
|
+
return undefined;
|
|
518
|
+
}
|
|
519
|
+
candidates.push(member.elementTypes);
|
|
520
|
+
}
|
|
521
|
+
return candidates;
|
|
522
|
+
};
|
|
523
|
+
const tryEmitTupleRestArguments = (startIndex, parameterType) => {
|
|
524
|
+
const remainingArgs = args.slice(startIndex);
|
|
525
|
+
if (remainingArgs.some((arg) => arg?.kind === "spread")) {
|
|
526
|
+
return undefined;
|
|
527
|
+
}
|
|
528
|
+
const tupleCandidates = extractTupleRestCandidates(parameterType);
|
|
529
|
+
if (!tupleCandidates || tupleCandidates.length === 0) {
|
|
530
|
+
return undefined;
|
|
531
|
+
}
|
|
532
|
+
const matchingCandidates = tupleCandidates.filter((candidate) => candidate.length === remainingArgs.length);
|
|
533
|
+
if (matchingCandidates.length !== 1) {
|
|
534
|
+
return undefined;
|
|
535
|
+
}
|
|
536
|
+
const tupleElements = matchingCandidates[0] ?? [];
|
|
537
|
+
const emittedArgs = [];
|
|
538
|
+
let tupleContext = currentContext;
|
|
539
|
+
for (let index = 0; index < remainingArgs.length; index++) {
|
|
540
|
+
const arg = remainingArgs[index];
|
|
541
|
+
const expectedType = tupleElements[index];
|
|
542
|
+
if (!arg)
|
|
543
|
+
continue;
|
|
544
|
+
const [argAst, argContext] = emitExpressionAst(arg, tupleContext, expectedType);
|
|
545
|
+
emittedArgs.push(argAst);
|
|
546
|
+
tupleContext = argContext;
|
|
547
|
+
}
|
|
548
|
+
return [emittedArgs, tupleContext];
|
|
549
|
+
};
|
|
550
|
+
for (let i = 0; i < parameters.length; i++) {
|
|
551
|
+
const parameter = parameters[i];
|
|
552
|
+
if (!parameter)
|
|
553
|
+
continue;
|
|
554
|
+
if (parameter.isRest) {
|
|
555
|
+
const tupleRestResult = tryEmitTupleRestArguments(i, parameter.type);
|
|
556
|
+
if (tupleRestResult) {
|
|
557
|
+
const [tupleArgs, tupleContext] = tupleRestResult;
|
|
558
|
+
argAsts.push(...tupleArgs);
|
|
559
|
+
currentContext = tupleContext;
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
const spreadArg = args[i];
|
|
563
|
+
if (args.length === i + 1 && spreadArg && spreadArg.kind === "spread") {
|
|
564
|
+
const [spreadAst, spreadCtx] = emitExpressionAst(spreadArg.expression, currentContext);
|
|
565
|
+
argAsts.push(spreadAst);
|
|
566
|
+
currentContext = spreadCtx;
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
const restElementType = parameter.type?.kind === "arrayType"
|
|
570
|
+
? parameter.type.elementType
|
|
571
|
+
: undefined;
|
|
572
|
+
let elementTypeAst = {
|
|
573
|
+
kind: "identifierType",
|
|
574
|
+
name: "object",
|
|
575
|
+
};
|
|
576
|
+
if (restElementType) {
|
|
577
|
+
const [emittedType, typeCtx] = emitTypeAst(restElementType, currentContext);
|
|
578
|
+
elementTypeAst = emittedType;
|
|
579
|
+
currentContext = typeCtx;
|
|
580
|
+
}
|
|
581
|
+
const restItems = [];
|
|
582
|
+
for (let j = i; j < args.length; j++) {
|
|
583
|
+
const arg = args[j];
|
|
584
|
+
if (!arg)
|
|
585
|
+
continue;
|
|
586
|
+
if (arg.kind === "spread") {
|
|
587
|
+
const [spreadAst, spreadCtx] = emitExpressionAst(arg.expression, currentContext);
|
|
588
|
+
argAsts.push(spreadAst);
|
|
589
|
+
currentContext = spreadCtx;
|
|
590
|
+
return [argAsts, currentContext];
|
|
591
|
+
}
|
|
592
|
+
const [argAst, argCtx] = emitExpressionAst(arg, currentContext, restElementType);
|
|
593
|
+
restItems.push(argAst);
|
|
594
|
+
currentContext = argCtx;
|
|
595
|
+
}
|
|
596
|
+
argAsts.push({
|
|
597
|
+
kind: "arrayCreationExpression",
|
|
598
|
+
elementType: elementTypeAst,
|
|
599
|
+
initializer: restItems,
|
|
600
|
+
});
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
const arg = args[i];
|
|
604
|
+
if (arg) {
|
|
605
|
+
const [argAst, argCtx] = emitExpressionAst(arg, currentContext, parameter.type);
|
|
606
|
+
const modifier = expr.argumentPassing?.[i] &&
|
|
607
|
+
expr.argumentPassing[i] !== "value" &&
|
|
608
|
+
isLValue(arg)
|
|
609
|
+
? expr.argumentPassing[i]
|
|
610
|
+
: undefined;
|
|
611
|
+
argAsts.push(wrapArgModifier(modifier, argAst));
|
|
612
|
+
currentContext = argCtx;
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (parameter.isOptional || parameter.initializer) {
|
|
616
|
+
let defaultType;
|
|
617
|
+
if (parameter.type) {
|
|
618
|
+
const [emittedType, typeCtx] = emitTypeAst(parameter.type, currentContext);
|
|
619
|
+
currentContext = typeCtx;
|
|
620
|
+
defaultType =
|
|
621
|
+
parameter.isOptional || parameter.initializer
|
|
622
|
+
? emittedType.kind === "nullableType"
|
|
623
|
+
? emittedType
|
|
624
|
+
: { kind: "nullableType", underlyingType: emittedType }
|
|
625
|
+
: emittedType;
|
|
626
|
+
}
|
|
627
|
+
argAsts.push({ kind: "defaultExpression", type: defaultType });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return [argAsts, currentContext];
|
|
631
|
+
};
|
|
632
|
+
const isArrayLikeIrType = (type) => {
|
|
633
|
+
if (!type)
|
|
634
|
+
return false;
|
|
635
|
+
if (type.kind === "arrayType" || type.kind === "tupleType") {
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
if (type.kind === "unionType") {
|
|
639
|
+
return type.types.every((member) => isArrayLikeIrType(member));
|
|
640
|
+
}
|
|
641
|
+
if (type.kind !== "referenceType") {
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
const simpleName = type.name.split(".").pop() ?? type.name;
|
|
645
|
+
return (simpleName === "Array" ||
|
|
646
|
+
simpleName === "ReadonlyArray" ||
|
|
647
|
+
simpleName === "JSArray" ||
|
|
648
|
+
simpleName === "Iterable" ||
|
|
649
|
+
simpleName === "IterableIterator" ||
|
|
650
|
+
simpleName === "IEnumerable" ||
|
|
651
|
+
simpleName === "IReadOnlyList" ||
|
|
652
|
+
simpleName === "List");
|
|
653
|
+
};
|
|
654
|
+
const detectRestParameterInfo = (args, parameterTypes) => {
|
|
655
|
+
if (parameterTypes.length === 0) {
|
|
656
|
+
return undefined;
|
|
657
|
+
}
|
|
658
|
+
const index = parameterTypes.length - 1;
|
|
659
|
+
const lastParamType = parameterTypes[index];
|
|
660
|
+
if (!lastParamType || lastParamType.kind !== "arrayType") {
|
|
661
|
+
return undefined;
|
|
662
|
+
}
|
|
663
|
+
if (args.length > parameterTypes.length) {
|
|
664
|
+
return {
|
|
665
|
+
index,
|
|
666
|
+
arrayType: lastParamType,
|
|
667
|
+
elementType: lastParamType.elementType,
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
const restArgs = args.slice(index);
|
|
671
|
+
if (restArgs.some((arg) => arg?.kind === "spread")) {
|
|
672
|
+
return {
|
|
673
|
+
index,
|
|
674
|
+
arrayType: lastParamType,
|
|
675
|
+
elementType: lastParamType.elementType,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
const lastArg = args[index];
|
|
679
|
+
if (!lastArg) {
|
|
680
|
+
return undefined;
|
|
681
|
+
}
|
|
682
|
+
const lastArgType = lastArg.kind === "spread"
|
|
683
|
+
? lastArg.expression.inferredType
|
|
684
|
+
: lastArg.inferredType;
|
|
685
|
+
if (!isArrayLikeIrType(lastArgType)) {
|
|
686
|
+
return {
|
|
687
|
+
index,
|
|
688
|
+
arrayType: lastParamType,
|
|
689
|
+
elementType: lastParamType.elementType,
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
return undefined;
|
|
693
|
+
};
|
|
694
|
+
const emitFlattenedRestArguments = (restArgs, restElementType, context) => {
|
|
695
|
+
let currentContext = context;
|
|
696
|
+
const [elementTypeAst, typeContext] = emitTypeAst(restElementType, currentContext);
|
|
697
|
+
currentContext = typeContext;
|
|
698
|
+
const segments = [];
|
|
699
|
+
let inlineElements = [];
|
|
700
|
+
const flushInlineElements = () => {
|
|
701
|
+
if (inlineElements.length === 0)
|
|
702
|
+
return;
|
|
703
|
+
segments.push({
|
|
704
|
+
kind: "arrayCreationExpression",
|
|
705
|
+
elementType: elementTypeAst,
|
|
706
|
+
initializer: inlineElements,
|
|
707
|
+
});
|
|
708
|
+
inlineElements = [];
|
|
709
|
+
};
|
|
710
|
+
for (const arg of restArgs) {
|
|
711
|
+
if (!arg)
|
|
712
|
+
continue;
|
|
713
|
+
if (arg.kind === "spread") {
|
|
714
|
+
flushInlineElements();
|
|
715
|
+
const [spreadAst, spreadContext] = emitExpressionAst(arg.expression, currentContext);
|
|
716
|
+
segments.push(spreadAst);
|
|
717
|
+
currentContext = spreadContext;
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
const [argAst, argContext] = emitExpressionAst(arg, currentContext, restElementType);
|
|
721
|
+
inlineElements.push(argAst);
|
|
722
|
+
currentContext = argContext;
|
|
723
|
+
}
|
|
724
|
+
flushInlineElements();
|
|
725
|
+
if (segments.length === 0) {
|
|
726
|
+
return [
|
|
727
|
+
[
|
|
728
|
+
{
|
|
729
|
+
kind: "invocationExpression",
|
|
730
|
+
expression: {
|
|
731
|
+
kind: "identifierExpression",
|
|
732
|
+
identifier: "global::System.Array.Empty",
|
|
733
|
+
},
|
|
734
|
+
typeArguments: [elementTypeAst],
|
|
735
|
+
arguments: [],
|
|
736
|
+
},
|
|
737
|
+
],
|
|
738
|
+
currentContext,
|
|
739
|
+
];
|
|
740
|
+
}
|
|
741
|
+
let concatAst = segments[0];
|
|
742
|
+
for (let index = 1; index < segments.length; index++) {
|
|
743
|
+
concatAst = {
|
|
744
|
+
kind: "invocationExpression",
|
|
745
|
+
expression: {
|
|
746
|
+
kind: "identifierExpression",
|
|
747
|
+
identifier: "global::System.Linq.Enumerable.Concat",
|
|
748
|
+
},
|
|
749
|
+
arguments: [concatAst, segments[index]],
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
return [
|
|
753
|
+
[
|
|
754
|
+
{
|
|
755
|
+
kind: "invocationExpression",
|
|
756
|
+
expression: {
|
|
757
|
+
kind: "identifierExpression",
|
|
758
|
+
identifier: "global::System.Linq.Enumerable.ToArray",
|
|
759
|
+
},
|
|
760
|
+
arguments: [concatAst],
|
|
761
|
+
},
|
|
762
|
+
],
|
|
763
|
+
currentContext,
|
|
764
|
+
];
|
|
765
|
+
};
|
|
766
|
+
const stableIrTypeKey = (type) => JSON.stringify(type);
|
|
767
|
+
const unwrapAsyncWrapperIrType = (type) => {
|
|
768
|
+
if (type.kind !== "referenceType")
|
|
769
|
+
return undefined;
|
|
770
|
+
if (!type.typeArguments || type.typeArguments.length !== 1)
|
|
771
|
+
return undefined;
|
|
772
|
+
const inner = type.typeArguments[0];
|
|
773
|
+
if (!inner)
|
|
774
|
+
return undefined;
|
|
775
|
+
const simpleName = type.name.split(".").pop() ?? type.name;
|
|
776
|
+
const clrName = type.resolvedClrType ?? type.name;
|
|
777
|
+
const isAsyncWrapper = simpleName === "Promise" ||
|
|
778
|
+
simpleName === "PromiseLike" ||
|
|
779
|
+
simpleName === "Task_1" ||
|
|
780
|
+
simpleName === "Task`1" ||
|
|
781
|
+
simpleName === "ValueTask_1" ||
|
|
782
|
+
simpleName === "ValueTask`1" ||
|
|
783
|
+
clrName === "System.Threading.Tasks.Task" ||
|
|
784
|
+
clrName === "System.Threading.Tasks.ValueTask" ||
|
|
785
|
+
clrName.startsWith("System.Threading.Tasks.Task`1") ||
|
|
786
|
+
clrName.startsWith("System.Threading.Tasks.ValueTask`1");
|
|
787
|
+
return isAsyncWrapper ? inner : undefined;
|
|
788
|
+
};
|
|
789
|
+
const isAsyncWrapperIrTypeLike = (type) => {
|
|
790
|
+
if (type.kind !== "referenceType")
|
|
791
|
+
return false;
|
|
792
|
+
const simpleName = type.name.split(".").pop() ?? type.name;
|
|
793
|
+
const clrName = type.resolvedClrType ?? type.name;
|
|
794
|
+
return (simpleName === "Promise" ||
|
|
795
|
+
simpleName === "PromiseLike" ||
|
|
796
|
+
simpleName === "Task" ||
|
|
797
|
+
simpleName === "Task_1" ||
|
|
798
|
+
simpleName === "Task`1" ||
|
|
799
|
+
simpleName === "ValueTask" ||
|
|
800
|
+
simpleName === "ValueTask_1" ||
|
|
801
|
+
simpleName === "ValueTask`1" ||
|
|
802
|
+
clrName === "System.Threading.Tasks.Task" ||
|
|
803
|
+
clrName === "System.Threading.Tasks.ValueTask" ||
|
|
804
|
+
clrName.startsWith("System.Threading.Tasks.Task`1") ||
|
|
805
|
+
clrName.startsWith("System.Threading.Tasks.ValueTask`1"));
|
|
806
|
+
};
|
|
807
|
+
const containsPromiseChainArtifact = (type) => {
|
|
808
|
+
if (!type)
|
|
809
|
+
return false;
|
|
810
|
+
if (isAsyncWrapperIrTypeLike(type)) {
|
|
811
|
+
return true;
|
|
812
|
+
}
|
|
813
|
+
if (type.kind === "unionType" || type.kind === "intersectionType") {
|
|
814
|
+
return type.types.some((member) => !!member && containsPromiseChainArtifact(member));
|
|
815
|
+
}
|
|
816
|
+
return false;
|
|
817
|
+
};
|
|
818
|
+
const normalizePromiseChainResultIrType = (type) => {
|
|
819
|
+
if (!type)
|
|
820
|
+
return undefined;
|
|
821
|
+
const asyncInner = unwrapAsyncWrapperIrType(type);
|
|
822
|
+
if (asyncInner) {
|
|
823
|
+
return normalizePromiseChainResultIrType(asyncInner);
|
|
824
|
+
}
|
|
825
|
+
if (type.kind === "unionType") {
|
|
826
|
+
const normalizedTypes = [];
|
|
827
|
+
const seen = new Set();
|
|
828
|
+
for (const member of type.types) {
|
|
829
|
+
if (!member)
|
|
830
|
+
continue;
|
|
831
|
+
const normalized = normalizePromiseChainResultIrType(member);
|
|
832
|
+
if (!normalized)
|
|
833
|
+
continue;
|
|
834
|
+
const key = stableIrTypeKey(normalized);
|
|
835
|
+
if (seen.has(key))
|
|
836
|
+
continue;
|
|
837
|
+
seen.add(key);
|
|
838
|
+
normalizedTypes.push(normalized);
|
|
839
|
+
}
|
|
840
|
+
if (normalizedTypes.length === 0)
|
|
841
|
+
return undefined;
|
|
842
|
+
if (normalizedTypes.length === 1)
|
|
843
|
+
return normalizedTypes[0];
|
|
844
|
+
return {
|
|
845
|
+
kind: "unionType",
|
|
846
|
+
types: normalizedTypes,
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
return type;
|
|
850
|
+
};
|
|
851
|
+
const mergePromiseChainResultIrTypes = (...types) => {
|
|
852
|
+
const merged = [];
|
|
853
|
+
const seen = new Set();
|
|
854
|
+
for (const type of types) {
|
|
855
|
+
const normalized = normalizePromiseChainResultIrType(type);
|
|
856
|
+
if (!normalized)
|
|
857
|
+
continue;
|
|
858
|
+
if (normalized.kind === "unionType") {
|
|
859
|
+
for (const member of normalized.types) {
|
|
860
|
+
if (!member)
|
|
861
|
+
continue;
|
|
862
|
+
const key = stableIrTypeKey(member);
|
|
863
|
+
if (seen.has(key))
|
|
864
|
+
continue;
|
|
865
|
+
seen.add(key);
|
|
866
|
+
merged.push(member);
|
|
867
|
+
}
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
const key = stableIrTypeKey(normalized);
|
|
871
|
+
if (seen.has(key))
|
|
872
|
+
continue;
|
|
873
|
+
seen.add(key);
|
|
874
|
+
merged.push(normalized);
|
|
875
|
+
}
|
|
876
|
+
if (merged.length === 0)
|
|
877
|
+
return undefined;
|
|
878
|
+
if (merged.length === 1)
|
|
879
|
+
return merged[0];
|
|
880
|
+
return {
|
|
881
|
+
kind: "unionType",
|
|
882
|
+
types: merged,
|
|
883
|
+
};
|
|
884
|
+
};
|
|
885
|
+
const buildTaskTypeAst = (resultType) => resultType
|
|
886
|
+
? {
|
|
887
|
+
kind: "identifierType",
|
|
888
|
+
name: "global::System.Threading.Tasks.Task",
|
|
889
|
+
typeArguments: [resultType],
|
|
890
|
+
}
|
|
891
|
+
: {
|
|
892
|
+
kind: "identifierType",
|
|
893
|
+
name: "global::System.Threading.Tasks.Task",
|
|
894
|
+
};
|
|
895
|
+
const buildTaskRunInvocation = (outputTaskType, body, isAsync) => {
|
|
896
|
+
const resultType = getTaskResultType(outputTaskType);
|
|
897
|
+
return {
|
|
898
|
+
kind: "invocationExpression",
|
|
899
|
+
expression: {
|
|
900
|
+
kind: "memberAccessExpression",
|
|
901
|
+
expression: {
|
|
902
|
+
kind: "identifierExpression",
|
|
903
|
+
identifier: "global::System.Threading.Tasks.Task",
|
|
904
|
+
},
|
|
905
|
+
memberName: "Run",
|
|
906
|
+
},
|
|
907
|
+
arguments: [
|
|
908
|
+
{
|
|
909
|
+
kind: "lambdaExpression",
|
|
910
|
+
isAsync,
|
|
911
|
+
parameters: [],
|
|
912
|
+
body,
|
|
913
|
+
},
|
|
914
|
+
],
|
|
915
|
+
typeArguments: resultType ? [resultType] : undefined,
|
|
916
|
+
};
|
|
917
|
+
};
|
|
918
|
+
const buildCompletedTaskAst = () => ({
|
|
919
|
+
kind: "memberAccessExpression",
|
|
920
|
+
expression: {
|
|
921
|
+
kind: "identifierExpression",
|
|
922
|
+
identifier: "global::System.Threading.Tasks.Task",
|
|
923
|
+
},
|
|
924
|
+
memberName: "CompletedTask",
|
|
925
|
+
});
|
|
926
|
+
const buildPromiseRejectedExceptionAst = (reasonAst) => {
|
|
927
|
+
const reasonExpr = reasonAst ??
|
|
928
|
+
{
|
|
929
|
+
kind: "literalExpression",
|
|
930
|
+
text: "null",
|
|
931
|
+
};
|
|
932
|
+
return {
|
|
933
|
+
kind: "binaryExpression",
|
|
934
|
+
operatorToken: "??",
|
|
935
|
+
left: {
|
|
936
|
+
kind: "asExpression",
|
|
937
|
+
expression: reasonExpr,
|
|
938
|
+
type: {
|
|
939
|
+
kind: "identifierType",
|
|
940
|
+
name: "global::System.Exception",
|
|
941
|
+
},
|
|
942
|
+
},
|
|
943
|
+
right: {
|
|
944
|
+
kind: "objectCreationExpression",
|
|
945
|
+
type: {
|
|
946
|
+
kind: "identifierType",
|
|
947
|
+
name: "global::System.Exception",
|
|
948
|
+
},
|
|
949
|
+
arguments: [
|
|
950
|
+
{
|
|
951
|
+
kind: "binaryExpression",
|
|
952
|
+
operatorToken: "??",
|
|
953
|
+
left: {
|
|
954
|
+
kind: "invocationExpression",
|
|
955
|
+
expression: {
|
|
956
|
+
kind: "conditionalMemberAccessExpression",
|
|
957
|
+
expression: reasonExpr,
|
|
958
|
+
memberName: "ToString",
|
|
959
|
+
},
|
|
960
|
+
arguments: [],
|
|
961
|
+
},
|
|
962
|
+
right: {
|
|
963
|
+
kind: "literalExpression",
|
|
964
|
+
text: '"Promise rejected"',
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
],
|
|
968
|
+
},
|
|
969
|
+
};
|
|
970
|
+
};
|
|
971
|
+
const getPromiseStaticMethod = (expr) => {
|
|
972
|
+
if (expr.callee.kind !== "memberAccess")
|
|
973
|
+
return undefined;
|
|
974
|
+
if (expr.callee.isComputed)
|
|
975
|
+
return undefined;
|
|
976
|
+
if (typeof expr.callee.property !== "string")
|
|
977
|
+
return undefined;
|
|
978
|
+
if (expr.callee.object.kind !== "identifier")
|
|
979
|
+
return undefined;
|
|
980
|
+
const objectName = expr.callee.object.originalName ?? expr.callee.object.name;
|
|
981
|
+
const simpleObjectName = objectName.split(".").pop() ?? objectName;
|
|
982
|
+
if (simpleObjectName !== "Promise")
|
|
983
|
+
return undefined;
|
|
984
|
+
switch (expr.callee.property) {
|
|
985
|
+
case "resolve":
|
|
986
|
+
case "reject":
|
|
987
|
+
case "all":
|
|
988
|
+
case "race":
|
|
989
|
+
return expr.callee.property;
|
|
990
|
+
default:
|
|
991
|
+
return undefined;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
const getSequenceElementIrType = (type) => {
|
|
995
|
+
if (!type)
|
|
996
|
+
return undefined;
|
|
997
|
+
if (type.kind === "arrayType")
|
|
998
|
+
return type.elementType;
|
|
999
|
+
if (type.kind === "tupleType") {
|
|
1000
|
+
if (type.elementTypes.length === 0)
|
|
1001
|
+
return undefined;
|
|
1002
|
+
if (type.elementTypes.length === 1)
|
|
1003
|
+
return type.elementTypes[0];
|
|
1004
|
+
return { kind: "unionType", types: type.elementTypes };
|
|
1005
|
+
}
|
|
1006
|
+
if (type.kind === "referenceType" &&
|
|
1007
|
+
type.typeArguments &&
|
|
1008
|
+
type.typeArguments.length > 0) {
|
|
1009
|
+
const simpleName = type.name.split(".").pop() ?? type.name;
|
|
1010
|
+
switch (simpleName) {
|
|
1011
|
+
case "Array":
|
|
1012
|
+
case "ReadonlyArray":
|
|
1013
|
+
case "Iterable":
|
|
1014
|
+
case "IterableIterator":
|
|
1015
|
+
case "IEnumerable":
|
|
1016
|
+
case "IReadOnlyList":
|
|
1017
|
+
case "List":
|
|
1018
|
+
case "Set":
|
|
1019
|
+
case "ReadonlySet":
|
|
1020
|
+
case "JSArray":
|
|
1021
|
+
return type.typeArguments[0];
|
|
1022
|
+
default:
|
|
1023
|
+
return undefined;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return undefined;
|
|
1027
|
+
};
|
|
1028
|
+
const isValueTaskLikeIrType = (type) => {
|
|
1029
|
+
if (!type || type.kind !== "referenceType")
|
|
1030
|
+
return false;
|
|
1031
|
+
const simpleName = type.name.split(".").pop() ?? type.name;
|
|
1032
|
+
const clrName = type.resolvedClrType ?? type.name;
|
|
1033
|
+
return (simpleName === "ValueTask" ||
|
|
1034
|
+
simpleName === "ValueTask_1" ||
|
|
1035
|
+
simpleName === "ValueTask`1" ||
|
|
1036
|
+
clrName === "System.Threading.Tasks.ValueTask" ||
|
|
1037
|
+
clrName.startsWith("System.Threading.Tasks.ValueTask`1"));
|
|
1038
|
+
};
|
|
1039
|
+
const emitPromiseNormalizedTaskAst = (valueAst, valueType, resultTypeAst, context) => {
|
|
1040
|
+
let currentContext = context;
|
|
1041
|
+
const asyncInner = valueType
|
|
1042
|
+
? unwrapAsyncWrapperIrType(valueType)
|
|
1043
|
+
: undefined;
|
|
1044
|
+
if (asyncInner) {
|
|
1045
|
+
if (isValueTaskLikeIrType(valueType)) {
|
|
1046
|
+
return [
|
|
1047
|
+
{
|
|
1048
|
+
kind: "invocationExpression",
|
|
1049
|
+
expression: {
|
|
1050
|
+
kind: "memberAccessExpression",
|
|
1051
|
+
expression: valueAst,
|
|
1052
|
+
memberName: "AsTask",
|
|
1053
|
+
},
|
|
1054
|
+
arguments: [],
|
|
1055
|
+
},
|
|
1056
|
+
currentContext,
|
|
1057
|
+
];
|
|
1058
|
+
}
|
|
1059
|
+
return [valueAst, currentContext];
|
|
1060
|
+
}
|
|
1061
|
+
if (valueType?.kind === "unionType") {
|
|
1062
|
+
const arms = [];
|
|
1063
|
+
for (let index = 0; index < valueType.types.length; index++) {
|
|
1064
|
+
const memberType = valueType.types[index];
|
|
1065
|
+
if (!memberType)
|
|
1066
|
+
continue;
|
|
1067
|
+
const [memberTypeAst, memberTypeContext] = emitTypeAst(memberType, currentContext);
|
|
1068
|
+
currentContext = memberTypeContext;
|
|
1069
|
+
const memberName = `__tsonic_promise_value_${index}`;
|
|
1070
|
+
const [normalizedArm, normalizedContext] = emitPromiseNormalizedTaskAst({
|
|
1071
|
+
kind: "identifierExpression",
|
|
1072
|
+
identifier: memberName,
|
|
1073
|
+
}, memberType, resultTypeAst, currentContext);
|
|
1074
|
+
currentContext = normalizedContext;
|
|
1075
|
+
arms.push({
|
|
1076
|
+
kind: "lambdaExpression",
|
|
1077
|
+
isAsync: false,
|
|
1078
|
+
parameters: [{ name: memberName, type: memberTypeAst }],
|
|
1079
|
+
body: normalizedArm,
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
return [
|
|
1083
|
+
{
|
|
1084
|
+
kind: "invocationExpression",
|
|
1085
|
+
expression: {
|
|
1086
|
+
kind: "memberAccessExpression",
|
|
1087
|
+
expression: valueAst,
|
|
1088
|
+
memberName: "Match",
|
|
1089
|
+
},
|
|
1090
|
+
arguments: arms,
|
|
1091
|
+
},
|
|
1092
|
+
currentContext,
|
|
1093
|
+
];
|
|
1094
|
+
}
|
|
1095
|
+
if (!resultTypeAst) {
|
|
1096
|
+
return [buildCompletedTaskAst(), currentContext];
|
|
1097
|
+
}
|
|
1098
|
+
return [
|
|
1099
|
+
{
|
|
1100
|
+
kind: "invocationExpression",
|
|
1101
|
+
expression: {
|
|
1102
|
+
kind: "memberAccessExpression",
|
|
1103
|
+
expression: {
|
|
1104
|
+
kind: "identifierExpression",
|
|
1105
|
+
identifier: "global::System.Threading.Tasks.Task",
|
|
1106
|
+
},
|
|
1107
|
+
memberName: "FromResult",
|
|
1108
|
+
},
|
|
1109
|
+
typeArguments: [resultTypeAst],
|
|
1110
|
+
arguments: [valueAst],
|
|
1111
|
+
},
|
|
1112
|
+
currentContext,
|
|
1113
|
+
];
|
|
186
1114
|
};
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
:
|
|
195
|
-
|
|
196
|
-
|
|
1115
|
+
const emitPromiseStaticCall = (expr, context) => {
|
|
1116
|
+
const method = getPromiseStaticMethod(expr);
|
|
1117
|
+
if (!method)
|
|
1118
|
+
return null;
|
|
1119
|
+
let currentContext = context;
|
|
1120
|
+
const [outputTaskType, outputTaskContext] = emitTypeAst(expr.inferredType ?? {
|
|
1121
|
+
kind: "referenceType",
|
|
1122
|
+
name: "Promise",
|
|
1123
|
+
typeArguments: [{ kind: "referenceType", name: "object" }],
|
|
1124
|
+
}, currentContext);
|
|
1125
|
+
currentContext = outputTaskContext;
|
|
1126
|
+
const outputResultType = getTaskResultType(outputTaskType);
|
|
1127
|
+
if (method === "resolve") {
|
|
1128
|
+
const argument = expr.arguments[0];
|
|
1129
|
+
if (!argument) {
|
|
1130
|
+
return [buildCompletedTaskAst(), currentContext];
|
|
1131
|
+
}
|
|
1132
|
+
const [valueAst, valueContext] = emitExpressionAst(argument, currentContext, argument.inferredType);
|
|
1133
|
+
currentContext = valueContext;
|
|
1134
|
+
return emitPromiseNormalizedTaskAst(valueAst, argument.inferredType, outputResultType, currentContext);
|
|
197
1135
|
}
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
1136
|
+
if (method === "reject") {
|
|
1137
|
+
const reason = expr.arguments[0];
|
|
1138
|
+
let reasonAst;
|
|
1139
|
+
if (reason) {
|
|
1140
|
+
[reasonAst, currentContext] = emitExpressionAst(reason, currentContext, reason.inferredType);
|
|
1141
|
+
}
|
|
1142
|
+
const exceptionAst = buildPromiseRejectedExceptionAst(reasonAst);
|
|
1143
|
+
return [
|
|
1144
|
+
{
|
|
1145
|
+
kind: "invocationExpression",
|
|
1146
|
+
expression: {
|
|
1147
|
+
kind: "memberAccessExpression",
|
|
1148
|
+
expression: {
|
|
1149
|
+
kind: "identifierExpression",
|
|
1150
|
+
identifier: "global::System.Threading.Tasks.Task",
|
|
1151
|
+
},
|
|
1152
|
+
memberName: "FromException",
|
|
1153
|
+
},
|
|
1154
|
+
typeArguments: outputResultType ? [outputResultType] : undefined,
|
|
1155
|
+
arguments: [exceptionAst],
|
|
1156
|
+
},
|
|
1157
|
+
currentContext,
|
|
1158
|
+
];
|
|
201
1159
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
1160
|
+
const valuesArg = expr.arguments[0];
|
|
1161
|
+
if (!valuesArg)
|
|
1162
|
+
return null;
|
|
1163
|
+
const [valuesAst, valuesContext] = emitExpressionAst(valuesArg, currentContext, valuesArg.inferredType);
|
|
1164
|
+
currentContext = valuesContext;
|
|
1165
|
+
const inputElementType = getSequenceElementIrType(valuesArg.inferredType);
|
|
1166
|
+
const resultElementTypeAst = outputResultType?.kind === "arrayType"
|
|
1167
|
+
? outputResultType.elementType
|
|
1168
|
+
: outputResultType;
|
|
1169
|
+
let normalizedValuesAst = valuesAst;
|
|
1170
|
+
if (inputElementType) {
|
|
1171
|
+
const [inputElementTypeAst, inputElementContext] = emitTypeAst(inputElementType, currentContext);
|
|
1172
|
+
currentContext = inputElementContext;
|
|
1173
|
+
const [normalizedTaskAst, normalizedTaskContext] = emitPromiseNormalizedTaskAst({
|
|
1174
|
+
kind: "identifierExpression",
|
|
1175
|
+
identifier: "__tsonic_promise_item",
|
|
1176
|
+
}, inputElementType, resultElementTypeAst, currentContext);
|
|
1177
|
+
currentContext = normalizedTaskContext;
|
|
1178
|
+
normalizedValuesAst = {
|
|
1179
|
+
kind: "invocationExpression",
|
|
1180
|
+
expression: {
|
|
1181
|
+
kind: "identifierExpression",
|
|
1182
|
+
identifier: "global::System.Linq.Enumerable.Select",
|
|
1183
|
+
},
|
|
1184
|
+
arguments: [
|
|
1185
|
+
valuesAst,
|
|
1186
|
+
{
|
|
1187
|
+
kind: "lambdaExpression",
|
|
1188
|
+
isAsync: false,
|
|
1189
|
+
parameters: [
|
|
1190
|
+
{
|
|
1191
|
+
name: "__tsonic_promise_item",
|
|
1192
|
+
type: inputElementTypeAst,
|
|
1193
|
+
},
|
|
1194
|
+
],
|
|
1195
|
+
body: normalizedTaskAst,
|
|
1196
|
+
},
|
|
1197
|
+
],
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
if (method === "all") {
|
|
1201
|
+
return [
|
|
1202
|
+
{
|
|
1203
|
+
kind: "invocationExpression",
|
|
1204
|
+
expression: {
|
|
1205
|
+
kind: "memberAccessExpression",
|
|
1206
|
+
expression: {
|
|
1207
|
+
kind: "identifierExpression",
|
|
1208
|
+
identifier: "global::System.Threading.Tasks.Task",
|
|
1209
|
+
},
|
|
1210
|
+
memberName: "WhenAll",
|
|
1211
|
+
},
|
|
1212
|
+
arguments: [normalizedValuesAst],
|
|
1213
|
+
},
|
|
1214
|
+
currentContext,
|
|
1215
|
+
];
|
|
1216
|
+
}
|
|
1217
|
+
const whenAnyAst = {
|
|
207
1218
|
kind: "invocationExpression",
|
|
208
1219
|
expression: {
|
|
209
1220
|
kind: "memberAccessExpression",
|
|
@@ -211,18 +1222,48 @@ const buildPromiseChainTaskRun = (outputTaskType, body) => {
|
|
|
211
1222
|
kind: "identifierExpression",
|
|
212
1223
|
identifier: "global::System.Threading.Tasks.Task",
|
|
213
1224
|
},
|
|
214
|
-
memberName: "
|
|
1225
|
+
memberName: "WhenAny",
|
|
215
1226
|
},
|
|
216
|
-
arguments: [
|
|
217
|
-
{
|
|
218
|
-
kind: "lambdaExpression",
|
|
219
|
-
isAsync: true,
|
|
220
|
-
parameters: [],
|
|
221
|
-
body,
|
|
222
|
-
},
|
|
223
|
-
],
|
|
224
|
-
typeArguments: resultType ? [resultType] : undefined,
|
|
1227
|
+
arguments: [normalizedValuesAst],
|
|
225
1228
|
};
|
|
1229
|
+
if (!outputResultType) {
|
|
1230
|
+
return [
|
|
1231
|
+
buildTaskRunInvocation(outputTaskType, {
|
|
1232
|
+
kind: "blockStatement",
|
|
1233
|
+
statements: [
|
|
1234
|
+
{
|
|
1235
|
+
kind: "expressionStatement",
|
|
1236
|
+
expression: {
|
|
1237
|
+
kind: "awaitExpression",
|
|
1238
|
+
expression: {
|
|
1239
|
+
kind: "awaitExpression",
|
|
1240
|
+
expression: whenAnyAst,
|
|
1241
|
+
},
|
|
1242
|
+
},
|
|
1243
|
+
},
|
|
1244
|
+
],
|
|
1245
|
+
}, true),
|
|
1246
|
+
currentContext,
|
|
1247
|
+
];
|
|
1248
|
+
}
|
|
1249
|
+
return [
|
|
1250
|
+
buildTaskRunInvocation(outputTaskType, {
|
|
1251
|
+
kind: "blockStatement",
|
|
1252
|
+
statements: [
|
|
1253
|
+
{
|
|
1254
|
+
kind: "returnStatement",
|
|
1255
|
+
expression: {
|
|
1256
|
+
kind: "awaitExpression",
|
|
1257
|
+
expression: {
|
|
1258
|
+
kind: "awaitExpression",
|
|
1259
|
+
expression: whenAnyAst,
|
|
1260
|
+
},
|
|
1261
|
+
},
|
|
1262
|
+
},
|
|
1263
|
+
],
|
|
1264
|
+
}, true),
|
|
1265
|
+
currentContext,
|
|
1266
|
+
];
|
|
226
1267
|
};
|
|
227
1268
|
const getDynamicImportSpecifier = (expr) => {
|
|
228
1269
|
const [arg] = expr.arguments;
|
|
@@ -232,93 +1273,145 @@ const getDynamicImportSpecifier = (expr) => {
|
|
|
232
1273
|
? arg.value
|
|
233
1274
|
: undefined;
|
|
234
1275
|
};
|
|
235
|
-
const
|
|
236
|
-
if (expr.callee.kind !== "identifier" || expr.callee.name !== "import") {
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
const specifier = getDynamicImportSpecifier(expr);
|
|
240
|
-
if (!specifier)
|
|
241
|
-
return null;
|
|
242
|
-
const completedTaskExpr = {
|
|
243
|
-
kind: "memberAccessExpression",
|
|
244
|
-
expression: {
|
|
245
|
-
kind: "identifierExpression",
|
|
246
|
-
identifier: "global::System.Threading.Tasks.Task",
|
|
247
|
-
},
|
|
248
|
-
memberName: "CompletedTask",
|
|
249
|
-
};
|
|
1276
|
+
const resolveDynamicImportTargetModule = (specifier, context) => {
|
|
250
1277
|
const currentFilePath = context.options.currentModuleFilePath;
|
|
251
1278
|
const moduleMap = context.options.moduleMap;
|
|
252
1279
|
if (!currentFilePath || !moduleMap) {
|
|
253
|
-
return
|
|
1280
|
+
return undefined;
|
|
254
1281
|
}
|
|
255
1282
|
const targetPath = resolveImportPath(currentFilePath, specifier);
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
1283
|
+
const direct = moduleMap.get(targetPath);
|
|
1284
|
+
if (direct) {
|
|
1285
|
+
return direct;
|
|
1286
|
+
}
|
|
1287
|
+
const normalizedTarget = targetPath.replace(/\\/g, "/");
|
|
1288
|
+
for (const [key, identity] of moduleMap.entries()) {
|
|
1289
|
+
const normalizedKey = key.replace(/\\/g, "/");
|
|
1290
|
+
if (normalizedKey === normalizedTarget ||
|
|
1291
|
+
normalizedKey.endsWith(`/${normalizedTarget}`) ||
|
|
1292
|
+
normalizedTarget.endsWith(`/${normalizedKey}`)) {
|
|
1293
|
+
return identity;
|
|
268
1294
|
}
|
|
269
|
-
return undefined;
|
|
270
|
-
})();
|
|
271
|
-
if (!targetModule) {
|
|
272
|
-
return [completedTaskExpr, context];
|
|
273
1295
|
}
|
|
1296
|
+
return undefined;
|
|
1297
|
+
};
|
|
1298
|
+
const buildDynamicImportContainerType = (targetModule) => {
|
|
274
1299
|
const containerName = targetModule.hasTypeCollision
|
|
275
1300
|
? `${targetModule.className}__Module`
|
|
276
1301
|
: targetModule.className;
|
|
277
|
-
|
|
1302
|
+
return {
|
|
278
1303
|
kind: "identifierType",
|
|
279
1304
|
name: `global::${targetModule.namespace}.${containerName}`,
|
|
280
1305
|
};
|
|
281
|
-
|
|
282
|
-
|
|
1306
|
+
};
|
|
1307
|
+
const buildRunClassConstructorExpression = (containerType) => ({
|
|
1308
|
+
kind: "invocationExpression",
|
|
1309
|
+
expression: {
|
|
1310
|
+
kind: "memberAccessExpression",
|
|
283
1311
|
expression: {
|
|
1312
|
+
kind: "identifierExpression",
|
|
1313
|
+
identifier: "global::System.Runtime.CompilerServices.RuntimeHelpers",
|
|
1314
|
+
},
|
|
1315
|
+
memberName: "RunClassConstructor",
|
|
1316
|
+
},
|
|
1317
|
+
arguments: [
|
|
1318
|
+
{
|
|
284
1319
|
kind: "memberAccessExpression",
|
|
285
1320
|
expression: {
|
|
286
|
-
kind: "
|
|
287
|
-
|
|
1321
|
+
kind: "typeofExpression",
|
|
1322
|
+
type: containerType,
|
|
288
1323
|
},
|
|
289
|
-
memberName: "
|
|
1324
|
+
memberName: "TypeHandle",
|
|
290
1325
|
},
|
|
291
|
-
|
|
1326
|
+
],
|
|
1327
|
+
});
|
|
1328
|
+
const emitDynamicImportCall = (expr, context) => {
|
|
1329
|
+
if (expr.callee.kind !== "identifier" || expr.callee.name !== "import") {
|
|
1330
|
+
return null;
|
|
1331
|
+
}
|
|
1332
|
+
const specifier = getDynamicImportSpecifier(expr);
|
|
1333
|
+
if (!specifier)
|
|
1334
|
+
return null;
|
|
1335
|
+
const completedTaskExpr = {
|
|
1336
|
+
kind: "memberAccessExpression",
|
|
1337
|
+
expression: {
|
|
1338
|
+
kind: "identifierExpression",
|
|
1339
|
+
identifier: "global::System.Threading.Tasks.Task",
|
|
1340
|
+
},
|
|
1341
|
+
memberName: "CompletedTask",
|
|
1342
|
+
};
|
|
1343
|
+
const targetModule = resolveDynamicImportTargetModule(specifier, context);
|
|
1344
|
+
if (!expr.dynamicImportNamespace) {
|
|
1345
|
+
if (!targetModule || !targetModule.hasRuntimeContainer) {
|
|
1346
|
+
return [completedTaskExpr, context];
|
|
1347
|
+
}
|
|
1348
|
+
const containerType = buildDynamicImportContainerType(targetModule);
|
|
1349
|
+
const runClassConstructor = buildRunClassConstructorExpression(containerType);
|
|
1350
|
+
return [
|
|
292
1351
|
{
|
|
293
|
-
kind: "
|
|
1352
|
+
kind: "invocationExpression",
|
|
294
1353
|
expression: {
|
|
295
|
-
kind: "
|
|
296
|
-
|
|
1354
|
+
kind: "memberAccessExpression",
|
|
1355
|
+
expression: {
|
|
1356
|
+
kind: "identifierExpression",
|
|
1357
|
+
identifier: "global::System.Threading.Tasks.Task",
|
|
1358
|
+
},
|
|
1359
|
+
memberName: "Run",
|
|
297
1360
|
},
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
expression: {
|
|
307
|
-
kind: "identifierExpression",
|
|
308
|
-
identifier: "global::System.Threading.Tasks.Task",
|
|
1361
|
+
arguments: [
|
|
1362
|
+
{
|
|
1363
|
+
kind: "lambdaExpression",
|
|
1364
|
+
isAsync: false,
|
|
1365
|
+
parameters: [],
|
|
1366
|
+
body: runClassConstructor,
|
|
1367
|
+
},
|
|
1368
|
+
],
|
|
309
1369
|
},
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
1370
|
+
context,
|
|
1371
|
+
];
|
|
1372
|
+
}
|
|
1373
|
+
if (!targetModule) {
|
|
1374
|
+
throw new Error(`ICE: Closed-world dynamic import '${specifier}' was validated as a namespace import but no module identity was available during emission.`);
|
|
1375
|
+
}
|
|
1376
|
+
let currentContext = context;
|
|
1377
|
+
const [outputTaskType, outputTaskContext] = emitTypeAst(expr.inferredType ?? {
|
|
1378
|
+
kind: "referenceType",
|
|
1379
|
+
name: "Promise",
|
|
1380
|
+
typeArguments: [{ kind: "referenceType", name: "object" }],
|
|
1381
|
+
}, currentContext);
|
|
1382
|
+
currentContext = outputTaskContext;
|
|
1383
|
+
const [namespaceAst, namespaceContext] = expr.dynamicImportNamespace.properties.length === 0
|
|
1384
|
+
? [
|
|
313
1385
|
{
|
|
314
|
-
kind: "
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
body: runClassConstructor,
|
|
1386
|
+
kind: "objectCreationExpression",
|
|
1387
|
+
type: { kind: "identifierType", name: "object" },
|
|
1388
|
+
arguments: [],
|
|
318
1389
|
},
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
1390
|
+
currentContext,
|
|
1391
|
+
]
|
|
1392
|
+
: emitExpressionAst(expr.dynamicImportNamespace, currentContext, expr.dynamicImportNamespace.inferredType);
|
|
1393
|
+
currentContext = namespaceContext;
|
|
1394
|
+
const setupStatements = [];
|
|
1395
|
+
if (targetModule.hasRuntimeContainer) {
|
|
1396
|
+
const containerType = buildDynamicImportContainerType(targetModule);
|
|
1397
|
+
setupStatements.push({
|
|
1398
|
+
kind: "expressionStatement",
|
|
1399
|
+
expression: buildRunClassConstructorExpression(containerType),
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
return [
|
|
1403
|
+
buildTaskRunInvocation(outputTaskType, {
|
|
1404
|
+
kind: "blockStatement",
|
|
1405
|
+
statements: [
|
|
1406
|
+
...setupStatements,
|
|
1407
|
+
{
|
|
1408
|
+
kind: "returnStatement",
|
|
1409
|
+
expression: namespaceAst,
|
|
1410
|
+
},
|
|
1411
|
+
],
|
|
1412
|
+
}, false),
|
|
1413
|
+
currentContext,
|
|
1414
|
+
];
|
|
322
1415
|
};
|
|
323
1416
|
const emitPromiseThenCatchFinally = (expr, context) => {
|
|
324
1417
|
if (expr.callee.kind !== "memberAccess")
|
|
@@ -339,7 +1432,7 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
339
1432
|
: expr.inferredType;
|
|
340
1433
|
const [rawOutputTaskType, outputTaskCtx] = emitTypeAst(outputTypeHint ?? { kind: "referenceType", name: "Task" }, currentContext);
|
|
341
1434
|
currentContext = outputTaskCtx;
|
|
342
|
-
const
|
|
1435
|
+
const defaultOutputTaskType = isTaskTypeAst(rawOutputTaskType) &&
|
|
343
1436
|
rawOutputTaskType.typeArguments?.length === 1 &&
|
|
344
1437
|
containsVoidTypeAst(rawOutputTaskType.typeArguments[0])
|
|
345
1438
|
? { kind: "identifierType", name: "global::System.Threading.Tasks.Task" }
|
|
@@ -347,7 +1440,7 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
347
1440
|
const [sourceTaskType, sourceTaskCtx] = emitTypeAst(expr.callee.object.inferredType ?? { kind: "referenceType", name: "Task" }, currentContext);
|
|
348
1441
|
currentContext = sourceTaskCtx;
|
|
349
1442
|
const sourceResultType = getTaskResultType(sourceTaskType);
|
|
350
|
-
const
|
|
1443
|
+
const sourceResultIr = normalizePromiseChainResultIrType(expr.callee.object.inferredType);
|
|
351
1444
|
const exIdent = "__tsonic_promise_ex";
|
|
352
1445
|
const valueIdent = "__tsonic_promise_value";
|
|
353
1446
|
const fulfilledArg = expr.arguments[0];
|
|
@@ -371,6 +1464,42 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
371
1464
|
finallyAst = fiAst;
|
|
372
1465
|
currentContext = fiCtx;
|
|
373
1466
|
}
|
|
1467
|
+
const fulfilledResultIr = fulfilledArg && fulfilledArg.kind !== "spread"
|
|
1468
|
+
? normalizePromiseChainResultIrType(getCallbackReturnType(fulfilledArg))
|
|
1469
|
+
: undefined;
|
|
1470
|
+
const rejectedResultIr = rejectedArg && rejectedArg.kind !== "spread"
|
|
1471
|
+
? normalizePromiseChainResultIrType(getCallbackReturnType(rejectedArg))
|
|
1472
|
+
: undefined;
|
|
1473
|
+
const normalizedPromiseChainResultIr = (() => {
|
|
1474
|
+
if (expr.callee.property === "then") {
|
|
1475
|
+
if (rejectedArg && rejectedArg.kind !== "spread") {
|
|
1476
|
+
return mergePromiseChainResultIrTypes(fulfilledResultIr ?? sourceResultIr, rejectedResultIr);
|
|
1477
|
+
}
|
|
1478
|
+
return fulfilledResultIr ?? sourceResultIr;
|
|
1479
|
+
}
|
|
1480
|
+
if (expr.callee.property === "catch") {
|
|
1481
|
+
return mergePromiseChainResultIrTypes(sourceResultIr, rejectedResultIr);
|
|
1482
|
+
}
|
|
1483
|
+
if (expr.callee.property === "finally") {
|
|
1484
|
+
return sourceResultIr;
|
|
1485
|
+
}
|
|
1486
|
+
return undefined;
|
|
1487
|
+
})();
|
|
1488
|
+
const normalizedFrontendPromiseChainResultIr = normalizePromiseChainResultIrType(expr.inferredType);
|
|
1489
|
+
const preferredPromiseChainResultIr = normalizedFrontendPromiseChainResultIr &&
|
|
1490
|
+
!containsPromiseChainArtifact(normalizedFrontendPromiseChainResultIr)
|
|
1491
|
+
? normalizedFrontendPromiseChainResultIr
|
|
1492
|
+
: normalizedPromiseChainResultIr;
|
|
1493
|
+
let outputResultType = getTaskResultType(defaultOutputTaskType);
|
|
1494
|
+
let outputTaskType = defaultOutputTaskType;
|
|
1495
|
+
if (preferredPromiseChainResultIr) {
|
|
1496
|
+
const [normalizedResultAst, normalizedCtx] = emitTypeAst(preferredPromiseChainResultIr, currentContext);
|
|
1497
|
+
currentContext = normalizedCtx;
|
|
1498
|
+
outputResultType = containsVoidTypeAst(normalizedResultAst)
|
|
1499
|
+
? undefined
|
|
1500
|
+
: normalizedResultAst;
|
|
1501
|
+
outputTaskType = buildTaskTypeAst(outputResultType);
|
|
1502
|
+
}
|
|
374
1503
|
const awaitReceiverStatement = sourceResultType === undefined
|
|
375
1504
|
? {
|
|
376
1505
|
kind: "expressionStatement",
|
|
@@ -413,7 +1542,7 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
413
1542
|
callbackParameterCount(fulfilledArg) > 0
|
|
414
1543
|
? [sourceResultType]
|
|
415
1544
|
: [];
|
|
416
|
-
const callbackReturnIr =
|
|
1545
|
+
const callbackReturnIr = getCallbackDelegateReturnType(fulfilledArg);
|
|
417
1546
|
let callbackReturnTypeAst = outputResultType;
|
|
418
1547
|
if (callbackReturnIr !== undefined) {
|
|
419
1548
|
const [cbRetAst, cbRetCtx] = emitTypeAst(callbackReturnIr, currentContext);
|
|
@@ -465,7 +1594,7 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
465
1594
|
identifier: exIdent,
|
|
466
1595
|
});
|
|
467
1596
|
}
|
|
468
|
-
const callbackReturnIr =
|
|
1597
|
+
const callbackReturnIr = getCallbackDelegateReturnType(rejectedArg);
|
|
469
1598
|
let callbackReturnTypeAst = outputResultType;
|
|
470
1599
|
if (callbackReturnIr !== undefined) {
|
|
471
1600
|
const [cbRetAst, cbRetCtx] = emitTypeAst(callbackReturnIr, currentContext);
|
|
@@ -509,7 +1638,7 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
509
1638
|
const invokeFinally = () => {
|
|
510
1639
|
if (!finallyAst)
|
|
511
1640
|
return [];
|
|
512
|
-
const callbackReturnIr =
|
|
1641
|
+
const callbackReturnIr = getCallbackDelegateReturnType(finallyArg);
|
|
513
1642
|
let callbackReturnTypeAst = undefined;
|
|
514
1643
|
if (callbackReturnIr !== undefined) {
|
|
515
1644
|
const [cbRetAst, cbRetCtx] = emitTypeAst(callbackReturnIr, currentContext);
|
|
@@ -567,10 +1696,10 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
567
1696
|
]
|
|
568
1697
|
: thenStatements;
|
|
569
1698
|
return [
|
|
570
|
-
|
|
1699
|
+
buildTaskRunInvocation(outputTaskType, {
|
|
571
1700
|
kind: "blockStatement",
|
|
572
1701
|
statements: bodyStatements,
|
|
573
|
-
}),
|
|
1702
|
+
}, true),
|
|
574
1703
|
currentContext,
|
|
575
1704
|
];
|
|
576
1705
|
}
|
|
@@ -594,7 +1723,7 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
594
1723
|
},
|
|
595
1724
|
];
|
|
596
1725
|
return [
|
|
597
|
-
|
|
1726
|
+
buildTaskRunInvocation(outputTaskType, {
|
|
598
1727
|
kind: "blockStatement",
|
|
599
1728
|
statements: [
|
|
600
1729
|
{
|
|
@@ -603,7 +1732,7 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
603
1732
|
catches,
|
|
604
1733
|
},
|
|
605
1734
|
],
|
|
606
|
-
}),
|
|
1735
|
+
}, true),
|
|
607
1736
|
currentContext,
|
|
608
1737
|
];
|
|
609
1738
|
}
|
|
@@ -612,7 +1741,7 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
612
1741
|
? [{ kind: "expressionStatement", expression: buildAwait(receiverAst) }]
|
|
613
1742
|
: [{ kind: "returnStatement", expression: buildAwait(receiverAst) }];
|
|
614
1743
|
return [
|
|
615
|
-
|
|
1744
|
+
buildTaskRunInvocation(outputTaskType, {
|
|
616
1745
|
kind: "blockStatement",
|
|
617
1746
|
statements: [
|
|
618
1747
|
{
|
|
@@ -625,7 +1754,7 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
625
1754
|
},
|
|
626
1755
|
},
|
|
627
1756
|
],
|
|
628
|
-
}),
|
|
1757
|
+
}, true),
|
|
629
1758
|
currentContext,
|
|
630
1759
|
];
|
|
631
1760
|
}
|
|
@@ -633,20 +1762,40 @@ const emitPromiseThenCatchFinally = (expr, context) => {
|
|
|
633
1762
|
};
|
|
634
1763
|
/**
|
|
635
1764
|
* Emit call arguments as typed AST array.
|
|
636
|
-
* Handles spread
|
|
1765
|
+
* Handles spread arrays, castModifier (ref/out from cast), and argumentPassing modes.
|
|
637
1766
|
*/
|
|
638
1767
|
const emitCallArguments = (args, expr, context) => {
|
|
1768
|
+
const functionValueSignature = getFunctionValueSignature(expr);
|
|
1769
|
+
if (functionValueSignature &&
|
|
1770
|
+
functionValueSignature.parameters.some((parameter) => parameter?.isRest ||
|
|
1771
|
+
parameter?.isOptional ||
|
|
1772
|
+
parameter?.initializer !== undefined)) {
|
|
1773
|
+
return emitFunctionValueCallArguments(args, functionValueSignature, expr, context);
|
|
1774
|
+
}
|
|
639
1775
|
const parameterTypes = expr.parameterTypes ?? [];
|
|
1776
|
+
const restInfo = detectRestParameterInfo(args, parameterTypes);
|
|
640
1777
|
let currentContext = context;
|
|
641
1778
|
const argAsts = [];
|
|
642
1779
|
for (let i = 0; i < args.length; i++) {
|
|
643
1780
|
const arg = args[i];
|
|
644
1781
|
if (!arg)
|
|
645
1782
|
continue;
|
|
646
|
-
|
|
1783
|
+
if (restInfo &&
|
|
1784
|
+
i === restInfo.index &&
|
|
1785
|
+
args
|
|
1786
|
+
.slice(restInfo.index)
|
|
1787
|
+
.some((candidate) => candidate?.kind === "spread")) {
|
|
1788
|
+
const [flattenedRestArgs, flattenedContext] = emitFlattenedRestArguments(args.slice(restInfo.index), restInfo.elementType, currentContext);
|
|
1789
|
+
argAsts.push(...flattenedRestArgs);
|
|
1790
|
+
currentContext = flattenedContext;
|
|
1791
|
+
break;
|
|
1792
|
+
}
|
|
1793
|
+
const expectedType = restInfo && i >= restInfo.index
|
|
1794
|
+
? restInfo.elementType
|
|
1795
|
+
: parameterTypes[i];
|
|
647
1796
|
if (arg.kind === "spread") {
|
|
648
1797
|
const [spreadAst, ctx] = emitExpressionAst(arg.expression, currentContext);
|
|
649
|
-
argAsts.push(
|
|
1798
|
+
argAsts.push(spreadAst);
|
|
650
1799
|
currentContext = ctx;
|
|
651
1800
|
}
|
|
652
1801
|
else {
|
|
@@ -672,7 +1821,52 @@ const emitCallArguments = (args, expr, context) => {
|
|
|
672
1821
|
/**
|
|
673
1822
|
* Emit a JsonSerializer call with NativeAOT-compatible options.
|
|
674
1823
|
*/
|
|
675
|
-
const
|
|
1824
|
+
const isConcreteGlobalJsonParseTarget = (type) => {
|
|
1825
|
+
if (!type)
|
|
1826
|
+
return false;
|
|
1827
|
+
if (type.kind === "unknownType" ||
|
|
1828
|
+
type.kind === "anyType" ||
|
|
1829
|
+
type.kind === "voidType" ||
|
|
1830
|
+
type.kind === "neverType") {
|
|
1831
|
+
return false;
|
|
1832
|
+
}
|
|
1833
|
+
if (type.kind === "unionType" || type.kind === "intersectionType") {
|
|
1834
|
+
return false;
|
|
1835
|
+
}
|
|
1836
|
+
return !containsTypeParameter(type);
|
|
1837
|
+
};
|
|
1838
|
+
const emitJsRuntimeJsonParseCall = (expr, context, typeArgument) => {
|
|
1839
|
+
let currentContext = context;
|
|
1840
|
+
const argAsts = [];
|
|
1841
|
+
for (const arg of expr.arguments) {
|
|
1842
|
+
if (arg.kind === "spread") {
|
|
1843
|
+
const [spreadAst, ctx] = emitExpressionAst(arg.expression, currentContext);
|
|
1844
|
+
argAsts.push(spreadAst);
|
|
1845
|
+
currentContext = ctx;
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
const [argAst, ctx] = emitExpressionAst(arg, currentContext);
|
|
1849
|
+
argAsts.push(argAst);
|
|
1850
|
+
currentContext = ctx;
|
|
1851
|
+
}
|
|
1852
|
+
return [
|
|
1853
|
+
{
|
|
1854
|
+
kind: "invocationExpression",
|
|
1855
|
+
expression: {
|
|
1856
|
+
kind: "memberAccessExpression",
|
|
1857
|
+
expression: {
|
|
1858
|
+
kind: "identifierExpression",
|
|
1859
|
+
identifier: "global::Tsonic.JSRuntime.JSON",
|
|
1860
|
+
},
|
|
1861
|
+
memberName: "parse",
|
|
1862
|
+
},
|
|
1863
|
+
arguments: argAsts,
|
|
1864
|
+
typeArguments: [typeArgument],
|
|
1865
|
+
},
|
|
1866
|
+
currentContext,
|
|
1867
|
+
];
|
|
1868
|
+
};
|
|
1869
|
+
const emitJsonSerializerCall = (expr, context, method, deserializeTypeOverride) => {
|
|
676
1870
|
let currentContext = context;
|
|
677
1871
|
// Register the type with the JSON AOT registry
|
|
678
1872
|
if (method === "Serialize") {
|
|
@@ -682,14 +1876,22 @@ const emitJsonSerializerCall = (expr, context, method) => {
|
|
|
682
1876
|
}
|
|
683
1877
|
}
|
|
684
1878
|
else {
|
|
685
|
-
const typeArg = expr.typeArguments?.[0];
|
|
1879
|
+
const typeArg = deserializeTypeOverride ?? expr.typeArguments?.[0];
|
|
686
1880
|
if (typeArg) {
|
|
687
1881
|
registerJsonAotType(typeArg, context);
|
|
688
1882
|
}
|
|
689
1883
|
}
|
|
690
1884
|
// Emit type arguments for Deserialize<T>
|
|
691
1885
|
let typeArgAsts = [];
|
|
692
|
-
|
|
1886
|
+
const deserializeIrType = method === "Deserialize"
|
|
1887
|
+
? (deserializeTypeOverride ?? expr.typeArguments?.[0])
|
|
1888
|
+
: undefined;
|
|
1889
|
+
if (deserializeIrType) {
|
|
1890
|
+
const [typeArgs, typeContext] = emitTypeArgumentsAst([deserializeIrType], currentContext);
|
|
1891
|
+
typeArgAsts = typeArgs;
|
|
1892
|
+
currentContext = typeContext;
|
|
1893
|
+
}
|
|
1894
|
+
else if (expr.typeArguments && expr.typeArguments.length > 0) {
|
|
693
1895
|
const [typeArgs, typeContext] = emitTypeArgumentsAst(expr.typeArguments, currentContext);
|
|
694
1896
|
typeArgAsts = typeArgs;
|
|
695
1897
|
currentContext = typeContext;
|
|
@@ -708,8 +1910,11 @@ const emitJsonSerializerCall = (expr, context, method) => {
|
|
|
708
1910
|
currentContext = ctx;
|
|
709
1911
|
}
|
|
710
1912
|
}
|
|
711
|
-
//
|
|
712
|
-
|
|
1913
|
+
// Only pass TsonicJson.Options when this call site actually participates in the
|
|
1914
|
+
// NativeAOT JSON rewrite. Non-generic JSON.parse(...) intentionally returns
|
|
1915
|
+
// unknown and should emit plain JsonSerializer calls without requiring the
|
|
1916
|
+
// generated TsonicJson helper.
|
|
1917
|
+
if (context.options.jsonAotRegistry?.needsJsonAot) {
|
|
713
1918
|
argAsts.push({
|
|
714
1919
|
kind: "identifierExpression",
|
|
715
1920
|
identifier: "TsonicJson.Options",
|
|
@@ -730,13 +1935,32 @@ const emitJsonSerializerCall = (expr, context, method) => {
|
|
|
730
1935
|
};
|
|
731
1936
|
return [invocation, currentContext];
|
|
732
1937
|
};
|
|
1938
|
+
const emitGlobalJsonCall = (expr, context, method) => {
|
|
1939
|
+
if (method === "Serialize") {
|
|
1940
|
+
return emitJsonSerializerCall(expr, context, method);
|
|
1941
|
+
}
|
|
1942
|
+
const deserializeTarget = expr.typeArguments?.[0] ??
|
|
1943
|
+
(isConcreteGlobalJsonParseTarget(expr.inferredType)
|
|
1944
|
+
? expr.inferredType
|
|
1945
|
+
: undefined);
|
|
1946
|
+
if (deserializeTarget) {
|
|
1947
|
+
return emitJsonSerializerCall(expr, context, "Deserialize", deserializeTarget);
|
|
1948
|
+
}
|
|
1949
|
+
return emitJsRuntimeJsonParseCall(expr, context, {
|
|
1950
|
+
kind: "predefinedType",
|
|
1951
|
+
keyword: "object",
|
|
1952
|
+
});
|
|
1953
|
+
};
|
|
733
1954
|
/**
|
|
734
1955
|
* Emit a function call expression as CSharpExpressionAst
|
|
735
1956
|
*/
|
|
736
1957
|
export const emitCall = (expr, context) => {
|
|
737
|
-
const dynamicImport =
|
|
1958
|
+
const dynamicImport = emitDynamicImportCall(expr, context);
|
|
738
1959
|
if (dynamicImport)
|
|
739
1960
|
return dynamicImport;
|
|
1961
|
+
const promiseStaticCall = emitPromiseStaticCall(expr, context);
|
|
1962
|
+
if (promiseStaticCall)
|
|
1963
|
+
return promiseStaticCall;
|
|
740
1964
|
const promiseChain = emitPromiseThenCatchFinally(expr, context);
|
|
741
1965
|
if (promiseChain)
|
|
742
1966
|
return promiseChain;
|
|
@@ -767,7 +1991,7 @@ export const emitCall = (expr, context) => {
|
|
|
767
1991
|
// Check for global JSON.stringify/parse calls
|
|
768
1992
|
const globalJsonCall = isGlobalJsonCall(expr.callee);
|
|
769
1993
|
if (globalJsonCall) {
|
|
770
|
-
return
|
|
1994
|
+
return emitGlobalJsonCall(expr, context, globalJsonCall.method);
|
|
771
1995
|
}
|
|
772
1996
|
// EF Core query canonicalization: ToList().ToArray() → ToArray()
|
|
773
1997
|
if (expr.callee.kind === "memberAccess" &&
|
|
@@ -892,6 +2116,10 @@ export const emitCall = (expr, context) => {
|
|
|
892
2116
|
];
|
|
893
2117
|
}
|
|
894
2118
|
const arrayWrapperInteropCall = emitArrayWrapperInteropCall(expr, context);
|
|
2119
|
+
const arrayMutationInteropCall = emitArrayMutationInteropCall(expr, context);
|
|
2120
|
+
if (arrayMutationInteropCall) {
|
|
2121
|
+
return arrayMutationInteropCall;
|
|
2122
|
+
}
|
|
895
2123
|
if (arrayWrapperInteropCall) {
|
|
896
2124
|
return arrayWrapperInteropCall;
|
|
897
2125
|
}
|