@tsonic/emitter 0.0.71 → 0.0.73
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/core/semantic/imports.js +9 -0
- package/dist/core/semantic/imports.js.map +1 -1
- package/dist/core/semantic/imports.test.js +43 -0
- package/dist/core/semantic/imports.test.js.map +1 -1
- package/dist/core/semantic/type-resolution.d.ts +1 -0
- package/dist/core/semantic/type-resolution.d.ts.map +1 -1
- package/dist/core/semantic/type-resolution.js +167 -5
- package/dist/core/semantic/type-resolution.js.map +1 -1
- package/dist/core/semantic/type-resolution.test.js +35 -0
- package/dist/core/semantic/type-resolution.test.js.map +1 -1
- package/dist/emitter-types/core.d.ts +5 -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 +801 -2
- package/dist/expression-emitter.js.map +1 -1
- package/dist/expressions/access.d.ts.map +1 -1
- package/dist/expressions/access.js +58 -1
- package/dist/expressions/access.js.map +1 -1
- package/dist/expressions/calls/call-analysis.d.ts +1 -0
- package/dist/expressions/calls/call-analysis.d.ts.map +1 -1
- package/dist/expressions/calls/call-analysis.js +57 -0
- package/dist/expressions/calls/call-analysis.js.map +1 -1
- package/dist/expressions/calls/call-emitter.d.ts.map +1 -1
- package/dist/expressions/calls/call-emitter.js +10 -47
- package/dist/expressions/calls/call-emitter.js.map +1 -1
- package/dist/expressions/collections.d.ts.map +1 -1
- package/dist/expressions/collections.js +211 -3
- package/dist/expressions/collections.js.map +1 -1
- package/dist/expressions/identifiers.d.ts.map +1 -1
- package/dist/expressions/identifiers.js +8 -1
- package/dist/expressions/identifiers.js.map +1 -1
- package/dist/expressions/index.test.js +117 -0
- package/dist/expressions/index.test.js.map +1 -1
- package/dist/expressions/operators/binary-emitter.d.ts.map +1 -1
- package/dist/expressions/operators/binary-emitter.js +97 -56
- package/dist/expressions/operators/binary-emitter.js.map +1 -1
- package/dist/expressions/other.d.ts.map +1 -1
- package/dist/expressions/other.js +59 -1
- package/dist/expressions/other.js.map +1 -1
- package/dist/integration.test.js +393 -5
- package/dist/integration.test.js.map +1 -1
- package/dist/specialization/type-aliases.test.js +8 -0
- package/dist/specialization/type-aliases.test.js.map +1 -1
- package/dist/statements/classes/members/methods.d.ts.map +1 -1
- package/dist/statements/classes/members/methods.js +2 -13
- package/dist/statements/classes/members/methods.js.map +1 -1
- package/dist/statements/control/conditionals/guard-analysis.d.ts +42 -0
- package/dist/statements/control/conditionals/guard-analysis.d.ts.map +1 -1
- package/dist/statements/control/conditionals/guard-analysis.js +284 -121
- package/dist/statements/control/conditionals/guard-analysis.js.map +1 -1
- package/dist/statements/control/conditionals/if-emitter.d.ts.map +1 -1
- package/dist/statements/control/conditionals/if-emitter.js +168 -44
- package/dist/statements/control/conditionals/if-emitter.js.map +1 -1
- package/dist/statements/declarations/functions.d.ts.map +1 -1
- package/dist/statements/declarations/functions.js +2 -13
- package/dist/statements/declarations/functions.js.map +1 -1
- package/dist/statements/declarations/type-aliases.d.ts.map +1 -1
- package/dist/statements/declarations/type-aliases.js +2 -1
- package/dist/statements/declarations/type-aliases.js.map +1 -1
- package/dist/statements/declarations/variables.d.ts.map +1 -1
- package/dist/statements/declarations/variables.js +17 -15
- package/dist/statements/declarations/variables.js.map +1 -1
- package/dist/statements/index.test.js +940 -0
- package/dist/statements/index.test.js.map +1 -1
- package/dist/types/references.d.ts.map +1 -1
- package/dist/types/references.js +12 -8
- package/dist/types/references.js.map +1 -1
- package/dist/types/references.test.js +112 -0
- package/dist/types/references.test.js.map +1 -1
- package/package.json +2 -2
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
* Primary entry point is emitExpressionAst which returns [CSharpExpressionAst, EmitterContext].
|
|
6
6
|
*/
|
|
7
7
|
import { emitTypeAst } from "./type-emitter.js";
|
|
8
|
-
import { substituteTypeArgs, resolveTypeAlias, stripNullish, } from "./core/semantic/type-resolution.js";
|
|
8
|
+
import { substituteTypeArgs, resolveTypeAlias, stripNullish, getPropertyType, getAllPropertySignatures, resolveLocalTypeInfo, } from "./core/semantic/type-resolution.js";
|
|
9
9
|
import { renderTypeAst } from "./core/format/backend-ast/utils.js";
|
|
10
|
+
import { allocateLocalName } from "./core/format/local-names.js";
|
|
11
|
+
import { emitCSharpName } from "./naming-policy.js";
|
|
10
12
|
// Import expression emitters from specialized modules
|
|
11
13
|
import { emitLiteral } from "./expressions/literals.js";
|
|
12
14
|
import { emitIdentifier } from "./expressions/identifiers.js";
|
|
@@ -17,6 +19,802 @@ import { emitNew } from "./expressions/calls/new-emitter.js";
|
|
|
17
19
|
import { emitBinary, emitLogical, emitUnary, emitUpdate, emitAssignment, emitConditional, } from "./expressions/operators.js";
|
|
18
20
|
import { emitFunctionExpression, emitArrowFunction, } from "./expressions/functions.js";
|
|
19
21
|
import { emitTemplateLiteral, emitSpread, emitAwait, } from "./expressions/other.js";
|
|
22
|
+
const hasNullishBranch = (type) => {
|
|
23
|
+
if (!type || type.kind !== "unionType")
|
|
24
|
+
return false;
|
|
25
|
+
return type.types.some((member) => member.kind === "primitiveType" &&
|
|
26
|
+
(member.name === "null" || member.name === "undefined"));
|
|
27
|
+
};
|
|
28
|
+
const buildDelegateType = (parameterTypes, returnType) => ({
|
|
29
|
+
kind: "identifierType",
|
|
30
|
+
name: "global::System.Func",
|
|
31
|
+
typeArguments: [...parameterTypes, returnType],
|
|
32
|
+
});
|
|
33
|
+
const collectLocalStructuralProperties = (info) => {
|
|
34
|
+
switch (info.kind) {
|
|
35
|
+
case "interface": {
|
|
36
|
+
if (info.members.some((member) => member.kind === "methodSignature")) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const props = [];
|
|
40
|
+
for (const member of info.members) {
|
|
41
|
+
if (member.kind !== "propertySignature")
|
|
42
|
+
continue;
|
|
43
|
+
props.push({
|
|
44
|
+
name: member.name,
|
|
45
|
+
type: member.type,
|
|
46
|
+
isOptional: member.isOptional,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return props;
|
|
50
|
+
}
|
|
51
|
+
case "class": {
|
|
52
|
+
if (info.members.some((member) => member.kind === "methodDeclaration")) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const props = [];
|
|
56
|
+
for (const member of info.members) {
|
|
57
|
+
if (member.kind !== "propertyDeclaration")
|
|
58
|
+
continue;
|
|
59
|
+
if (!member.type)
|
|
60
|
+
return undefined;
|
|
61
|
+
props.push({
|
|
62
|
+
name: member.name,
|
|
63
|
+
type: member.type,
|
|
64
|
+
isOptional: false,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return props;
|
|
68
|
+
}
|
|
69
|
+
case "typeAlias": {
|
|
70
|
+
const aliasType = info.type;
|
|
71
|
+
if (aliasType.kind !== "objectType")
|
|
72
|
+
return undefined;
|
|
73
|
+
if (aliasType.members.some((member) => member.kind === "methodSignature")) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
return aliasType.members
|
|
77
|
+
.filter((member) => member.kind === "propertySignature")
|
|
78
|
+
.map((member) => ({
|
|
79
|
+
name: member.name,
|
|
80
|
+
type: member.type,
|
|
81
|
+
isOptional: member.isOptional,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
default:
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const parseEmitterClrTypeString = (clrType) => {
|
|
89
|
+
if (clrType === "System.Void" || clrType === "void") {
|
|
90
|
+
return { kind: "voidType" };
|
|
91
|
+
}
|
|
92
|
+
const primitiveMap = {
|
|
93
|
+
"System.String": { kind: "primitiveType", name: "string" },
|
|
94
|
+
string: { kind: "primitiveType", name: "string" },
|
|
95
|
+
"System.Int32": { kind: "primitiveType", name: "int" },
|
|
96
|
+
int: { kind: "primitiveType", name: "int" },
|
|
97
|
+
"System.Double": { kind: "primitiveType", name: "number" },
|
|
98
|
+
double: { kind: "primitiveType", name: "number" },
|
|
99
|
+
"System.Boolean": { kind: "primitiveType", name: "boolean" },
|
|
100
|
+
bool: { kind: "primitiveType", name: "boolean" },
|
|
101
|
+
"System.Char": { kind: "primitiveType", name: "char" },
|
|
102
|
+
char: { kind: "primitiveType", name: "char" },
|
|
103
|
+
"System.Int64": { kind: "referenceType", name: "long" },
|
|
104
|
+
long: { kind: "referenceType", name: "long" },
|
|
105
|
+
"System.Object": { kind: "referenceType", name: "object" },
|
|
106
|
+
object: { kind: "referenceType", name: "object" },
|
|
107
|
+
};
|
|
108
|
+
const primitive = primitiveMap[clrType];
|
|
109
|
+
if (primitive)
|
|
110
|
+
return primitive;
|
|
111
|
+
if (clrType.endsWith("[]")) {
|
|
112
|
+
return {
|
|
113
|
+
kind: "arrayType",
|
|
114
|
+
elementType: parseEmitterClrTypeString(clrType.slice(0, -2)),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (clrType.endsWith("*")) {
|
|
118
|
+
return parseEmitterClrTypeString(clrType.slice(0, -1));
|
|
119
|
+
}
|
|
120
|
+
if (clrType.startsWith("System.Nullable`1")) {
|
|
121
|
+
const innerMatch = clrType.match(/System\.Nullable`1\[\[([^\]]+)\]\]/);
|
|
122
|
+
if (innerMatch?.[1]) {
|
|
123
|
+
return {
|
|
124
|
+
kind: "unionType",
|
|
125
|
+
types: [
|
|
126
|
+
parseEmitterClrTypeString(innerMatch[1]),
|
|
127
|
+
{ kind: "primitiveType", name: "undefined" },
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (/^T\d*$/.test(clrType) || /^T[A-Z][a-zA-Z]*$/.test(clrType)) {
|
|
133
|
+
return { kind: "typeParameterType", name: clrType };
|
|
134
|
+
}
|
|
135
|
+
const underscoreInstantiationMatch = clrType.match(/^(.+?)_(\d+)\[\[(.+)\]\]$/);
|
|
136
|
+
if (underscoreInstantiationMatch?.[1] &&
|
|
137
|
+
underscoreInstantiationMatch[2] &&
|
|
138
|
+
underscoreInstantiationMatch[3]) {
|
|
139
|
+
const baseName = underscoreInstantiationMatch[1];
|
|
140
|
+
const arity = Number.parseInt(underscoreInstantiationMatch[2], 10);
|
|
141
|
+
const args = splitEmitterTypeArguments(underscoreInstantiationMatch[3]);
|
|
142
|
+
return {
|
|
143
|
+
kind: "referenceType",
|
|
144
|
+
name: `${baseName}_${arity}`,
|
|
145
|
+
typeArguments: args.length === arity
|
|
146
|
+
? args.map((arg) => parseEmitterClrTypeString(arg.trim()))
|
|
147
|
+
: undefined,
|
|
148
|
+
resolvedClrType: clrType,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const genericMatch = clrType.match(/^(.+)`(\d+)(?:\[\[(.+)\]\])?$/);
|
|
152
|
+
if (genericMatch?.[1] && genericMatch[2]) {
|
|
153
|
+
const baseName = genericMatch[1];
|
|
154
|
+
const arity = Number.parseInt(genericMatch[2], 10);
|
|
155
|
+
const typeArguments = genericMatch[3]
|
|
156
|
+
? splitEmitterTypeArguments(genericMatch[3]).map((arg) => parseEmitterClrTypeString(arg.trim()))
|
|
157
|
+
: Array.from({ length: arity }, (_, index) => ({
|
|
158
|
+
kind: "typeParameterType",
|
|
159
|
+
name: index === 0 ? "T" : `T${index + 1}`,
|
|
160
|
+
}));
|
|
161
|
+
return {
|
|
162
|
+
kind: "referenceType",
|
|
163
|
+
name: baseName,
|
|
164
|
+
typeArguments,
|
|
165
|
+
resolvedClrType: clrType,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
kind: "referenceType",
|
|
170
|
+
name: clrType,
|
|
171
|
+
resolvedClrType: clrType,
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
const splitEmitterTypeArguments = (text) => {
|
|
175
|
+
const parts = [];
|
|
176
|
+
let depth = 0;
|
|
177
|
+
let current = "";
|
|
178
|
+
for (const char of text) {
|
|
179
|
+
if (char === "[") {
|
|
180
|
+
depth++;
|
|
181
|
+
current += char;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (char === "]") {
|
|
185
|
+
depth--;
|
|
186
|
+
current += char;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (char === "," && depth === 0) {
|
|
190
|
+
parts.push(current.trim());
|
|
191
|
+
current = "";
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
current += char;
|
|
195
|
+
}
|
|
196
|
+
if (current.trim()) {
|
|
197
|
+
parts.push(current.trim());
|
|
198
|
+
}
|
|
199
|
+
return parts;
|
|
200
|
+
};
|
|
201
|
+
const addUndefinedToBindingType = (type) => {
|
|
202
|
+
if (type.kind === "unionType" &&
|
|
203
|
+
type.types.some((candidate) => candidate.kind === "primitiveType" && candidate.name === "undefined")) {
|
|
204
|
+
return type;
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
kind: "unionType",
|
|
208
|
+
types: [type, { kind: "primitiveType", name: "undefined" }],
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
const parseBindingPropertyType = (normalizedSignature) => {
|
|
212
|
+
if (!normalizedSignature) {
|
|
213
|
+
return { kind: "unknownType" };
|
|
214
|
+
}
|
|
215
|
+
const indexerMatch = normalizedSignature.match(/\|\[[^\]]*\]:([^|]+)\|/);
|
|
216
|
+
if (indexerMatch?.[1]) {
|
|
217
|
+
return parseEmitterClrTypeString(indexerMatch[1]);
|
|
218
|
+
}
|
|
219
|
+
const propertyMatch = normalizedSignature.match(/\|:([^|]+)\|/);
|
|
220
|
+
if (propertyMatch?.[1]) {
|
|
221
|
+
return parseEmitterClrTypeString(propertyMatch[1]);
|
|
222
|
+
}
|
|
223
|
+
const fieldParts = normalizedSignature.split("|");
|
|
224
|
+
if (fieldParts.length >= 2 && fieldParts[1]) {
|
|
225
|
+
return parseEmitterClrTypeString(fieldParts[1]);
|
|
226
|
+
}
|
|
227
|
+
return { kind: "unknownType" };
|
|
228
|
+
};
|
|
229
|
+
const collectBindingStructuralProperties = (type, context) => {
|
|
230
|
+
const registry = context.bindingsRegistry;
|
|
231
|
+
if (!registry || registry.size === 0) {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
const candidates = new Set();
|
|
235
|
+
const add = (value) => {
|
|
236
|
+
if (value && value.length > 0) {
|
|
237
|
+
candidates.add(value);
|
|
238
|
+
if (value.includes(".")) {
|
|
239
|
+
candidates.add(value.split(".").pop() ?? value);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
add(type.name);
|
|
244
|
+
add(type.resolvedClrType);
|
|
245
|
+
add(type.typeId?.tsName);
|
|
246
|
+
add(type.typeId?.clrName);
|
|
247
|
+
for (const candidate of candidates) {
|
|
248
|
+
const binding = registry.get(candidate);
|
|
249
|
+
if (!binding)
|
|
250
|
+
continue;
|
|
251
|
+
if (binding.members.some((member) => member.kind === "method")) {
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
const props = binding.members
|
|
255
|
+
.filter((member) => member.kind === "property")
|
|
256
|
+
.map((member) => ({
|
|
257
|
+
name: member.alias,
|
|
258
|
+
type: member.semanticType !== undefined
|
|
259
|
+
? member.semanticOptional === true
|
|
260
|
+
? addUndefinedToBindingType(member.semanticType)
|
|
261
|
+
: member.semanticType
|
|
262
|
+
: parseBindingPropertyType(member.signature),
|
|
263
|
+
isOptional: member.semanticOptional === true,
|
|
264
|
+
}));
|
|
265
|
+
if (props.length > 0) {
|
|
266
|
+
return props;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return undefined;
|
|
270
|
+
};
|
|
271
|
+
const collectStructuralProperties = (type, context) => {
|
|
272
|
+
if (!type)
|
|
273
|
+
return undefined;
|
|
274
|
+
const resolved = resolveTypeAlias(stripNullish(type), context);
|
|
275
|
+
if (resolved.kind === "objectType") {
|
|
276
|
+
if (resolved.members.some((member) => member.kind === "methodSignature")) {
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
return resolved.members
|
|
280
|
+
.filter((member) => member.kind === "propertySignature")
|
|
281
|
+
.map((member) => ({
|
|
282
|
+
name: member.name,
|
|
283
|
+
type: member.type,
|
|
284
|
+
isOptional: member.isOptional,
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
if (resolved.kind !== "referenceType") {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
const inheritedInterfaceProps = getAllPropertySignatures(resolved, context);
|
|
291
|
+
if (inheritedInterfaceProps && inheritedInterfaceProps.length > 0) {
|
|
292
|
+
return inheritedInterfaceProps.map((member) => ({
|
|
293
|
+
name: member.name,
|
|
294
|
+
type: member.type,
|
|
295
|
+
isOptional: member.isOptional,
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
const localInfo = resolveLocalTypeInfo(resolved, context)?.info;
|
|
299
|
+
if (localInfo) {
|
|
300
|
+
return collectLocalStructuralProperties(localInfo);
|
|
301
|
+
}
|
|
302
|
+
if (resolved.structuralMembers && resolved.structuralMembers.length > 0) {
|
|
303
|
+
if (resolved.structuralMembers.some((member) => member.kind === "methodSignature")) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
return resolved.structuralMembers
|
|
307
|
+
.filter((member) => member.kind === "propertySignature")
|
|
308
|
+
.map((member) => ({
|
|
309
|
+
name: member.name,
|
|
310
|
+
type: member.type,
|
|
311
|
+
isOptional: member.isOptional,
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
return collectBindingStructuralProperties(resolved, context);
|
|
315
|
+
};
|
|
316
|
+
const resolveAnonymousStructuralReferenceType = (type, context) => {
|
|
317
|
+
const resolved = resolveTypeAlias(stripNullish(type), context);
|
|
318
|
+
if (resolved.kind !== "objectType")
|
|
319
|
+
return undefined;
|
|
320
|
+
const propertyNames = resolved.members
|
|
321
|
+
.filter((member) => member.kind === "propertySignature")
|
|
322
|
+
.map((member) => member.name)
|
|
323
|
+
.sort();
|
|
324
|
+
if (propertyNames.length === 0)
|
|
325
|
+
return undefined;
|
|
326
|
+
const candidateMaps = [];
|
|
327
|
+
if (context.localTypes) {
|
|
328
|
+
candidateMaps.push(context.localTypes);
|
|
329
|
+
}
|
|
330
|
+
if (context.options.moduleMap) {
|
|
331
|
+
for (const module of context.options.moduleMap.values()) {
|
|
332
|
+
if (module.localTypes) {
|
|
333
|
+
candidateMaps.push(module.localTypes);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const matches = new Set();
|
|
338
|
+
for (const localTypes of candidateMaps) {
|
|
339
|
+
for (const [typeName, info] of localTypes.entries()) {
|
|
340
|
+
if (info.kind !== "class" || !typeName.startsWith("__Anon_"))
|
|
341
|
+
continue;
|
|
342
|
+
const candidateProps = info.members
|
|
343
|
+
.filter((member) => member.kind === "propertyDeclaration")
|
|
344
|
+
.map((member) => member.name)
|
|
345
|
+
.sort();
|
|
346
|
+
if (candidateProps.length === propertyNames.length &&
|
|
347
|
+
candidateProps.every((name, index) => name === propertyNames[index])) {
|
|
348
|
+
matches.add(typeName);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (matches.size !== 1)
|
|
353
|
+
return undefined;
|
|
354
|
+
const onlyMatch = [...matches][0];
|
|
355
|
+
return onlyMatch ? { kind: "referenceType", name: onlyMatch } : undefined;
|
|
356
|
+
};
|
|
357
|
+
const isSameNominalType = (sourceType, targetType, context) => {
|
|
358
|
+
if (!sourceType || !targetType)
|
|
359
|
+
return false;
|
|
360
|
+
const sourceBase = stripNullish(sourceType);
|
|
361
|
+
const targetBase = stripNullish(targetType);
|
|
362
|
+
if (sourceBase.kind === "referenceType" &&
|
|
363
|
+
targetBase.kind === "referenceType") {
|
|
364
|
+
if (sourceBase.name === targetBase.name) {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
if (sourceBase.typeId?.stableId !== undefined &&
|
|
368
|
+
sourceBase.typeId.stableId === targetBase.typeId?.stableId) {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
if (sourceBase.resolvedClrType !== undefined &&
|
|
372
|
+
sourceBase.resolvedClrType === targetBase.resolvedClrType) {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const sourceResolved = resolveTypeAlias(sourceBase, context);
|
|
377
|
+
const targetResolved = resolveTypeAlias(targetBase, context);
|
|
378
|
+
if (sourceResolved.kind !== "referenceType" ||
|
|
379
|
+
targetResolved.kind !== "referenceType") {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
return (sourceResolved.name === targetResolved.name ||
|
|
383
|
+
(sourceResolved.resolvedClrType !== undefined &&
|
|
384
|
+
sourceResolved.resolvedClrType === targetResolved.resolvedClrType));
|
|
385
|
+
};
|
|
386
|
+
const buildStructuralSourceAccess = (sourceExpression, sourceType, propertyName, context) => {
|
|
387
|
+
const resolvedSource = resolveTypeAlias(stripNullish(sourceType), context);
|
|
388
|
+
if (resolvedSource.kind === "dictionaryType") {
|
|
389
|
+
return {
|
|
390
|
+
kind: "elementAccessExpression",
|
|
391
|
+
expression: sourceExpression,
|
|
392
|
+
arguments: [
|
|
393
|
+
{
|
|
394
|
+
kind: "literalExpression",
|
|
395
|
+
text: JSON.stringify(propertyName),
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
kind: "memberAccessExpression",
|
|
402
|
+
expression: sourceExpression,
|
|
403
|
+
memberName: emitCSharpName(propertyName, "properties", context),
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
const isDirectlyReusableExpression = (expression) => expression.kind === "identifierExpression" ||
|
|
407
|
+
expression.kind === "memberAccessExpression" ||
|
|
408
|
+
expression.kind === "elementAccessExpression";
|
|
409
|
+
const getArrayElementType = (type, context) => {
|
|
410
|
+
if (!type)
|
|
411
|
+
return undefined;
|
|
412
|
+
const resolved = resolveTypeAlias(stripNullish(type), context);
|
|
413
|
+
if (resolved.kind === "arrayType")
|
|
414
|
+
return resolved.elementType;
|
|
415
|
+
if (resolved.kind === "tupleType") {
|
|
416
|
+
if (resolved.elementTypes.length === 1)
|
|
417
|
+
return resolved.elementTypes[0];
|
|
418
|
+
return undefined;
|
|
419
|
+
}
|
|
420
|
+
if (resolved.kind === "referenceType" &&
|
|
421
|
+
(resolved.name === "Array" ||
|
|
422
|
+
resolved.name === "ReadonlyArray" ||
|
|
423
|
+
resolved.name === "JSArray") &&
|
|
424
|
+
resolved.typeArguments?.length === 1) {
|
|
425
|
+
return resolved.typeArguments[0];
|
|
426
|
+
}
|
|
427
|
+
return undefined;
|
|
428
|
+
};
|
|
429
|
+
const getDictionaryValueType = (type, context) => {
|
|
430
|
+
if (!type)
|
|
431
|
+
return undefined;
|
|
432
|
+
const resolved = resolveTypeAlias(stripNullish(type), context);
|
|
433
|
+
if (resolved.kind !== "dictionaryType")
|
|
434
|
+
return undefined;
|
|
435
|
+
return resolved.valueType;
|
|
436
|
+
};
|
|
437
|
+
const tryAdaptStructuralExpressionAst = (emittedAst, sourceType, context, expectedType) => {
|
|
438
|
+
if (!expectedType || !sourceType)
|
|
439
|
+
return undefined;
|
|
440
|
+
if (isSameNominalType(sourceType, expectedType, context)) {
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
const strippedExpectedType = stripNullish(expectedType);
|
|
444
|
+
const anonymousStructuralTarget = resolveAnonymousStructuralReferenceType(expectedType, context);
|
|
445
|
+
const targetStructuralType = anonymousStructuralTarget ??
|
|
446
|
+
resolveTypeAlias(strippedExpectedType, context);
|
|
447
|
+
const targetEmissionType = anonymousStructuralTarget ??
|
|
448
|
+
(strippedExpectedType.kind === "referenceType"
|
|
449
|
+
? strippedExpectedType
|
|
450
|
+
: undefined);
|
|
451
|
+
const targetProps = collectStructuralProperties(targetStructuralType, context);
|
|
452
|
+
if (targetProps && targetProps.length > 0) {
|
|
453
|
+
if (!targetEmissionType && targetStructuralType.kind === "objectType") {
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
const sourceProps = collectStructuralProperties(sourceType, context);
|
|
457
|
+
if (!sourceProps || sourceProps.length === 0)
|
|
458
|
+
return undefined;
|
|
459
|
+
const sourcePropNames = new Set(sourceProps.map((prop) => prop.name));
|
|
460
|
+
const materializedProps = targetProps.filter((prop) => prop.isOptional || sourcePropNames.has(prop.name));
|
|
461
|
+
if (materializedProps.length === 0)
|
|
462
|
+
return undefined;
|
|
463
|
+
for (const prop of targetProps) {
|
|
464
|
+
if (!prop.isOptional && !sourcePropNames.has(prop.name)) {
|
|
465
|
+
return undefined;
|
|
466
|
+
}
|
|
467
|
+
if (!sourcePropNames.has(prop.name))
|
|
468
|
+
continue;
|
|
469
|
+
if (!getPropertyType(sourceType, prop.name, context)) {
|
|
470
|
+
return undefined;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
let currentContext = context;
|
|
474
|
+
const [targetTypeAst, withType] = emitTypeAst(targetEmissionType ?? targetStructuralType, currentContext);
|
|
475
|
+
currentContext = withType;
|
|
476
|
+
const safeTargetTypeAst = targetTypeAst.kind === "nullableType"
|
|
477
|
+
? targetTypeAst.underlyingType
|
|
478
|
+
: targetTypeAst;
|
|
479
|
+
const sourcePropMap = new Map(sourceProps.map((prop) => [prop.name, prop]));
|
|
480
|
+
const buildInitializer = (sourceExpression, initContext) => {
|
|
481
|
+
let currentInitContext = initContext;
|
|
482
|
+
const assignments = materializedProps
|
|
483
|
+
.filter((prop) => sourcePropNames.has(prop.name))
|
|
484
|
+
.map((prop) => {
|
|
485
|
+
const sourceProp = sourcePropMap.get(prop.name);
|
|
486
|
+
const sourceAccess = buildStructuralSourceAccess(sourceExpression, sourceType, prop.name, currentInitContext);
|
|
487
|
+
const [adaptedValueAst, adaptedValueContext] = tryAdaptStructuralExpressionAst(sourceAccess, sourceProp?.type, currentInitContext, prop.type) ?? [sourceAccess, currentInitContext];
|
|
488
|
+
currentInitContext = adaptedValueContext;
|
|
489
|
+
return {
|
|
490
|
+
kind: "assignmentExpression",
|
|
491
|
+
operatorToken: "=",
|
|
492
|
+
left: {
|
|
493
|
+
kind: "identifierExpression",
|
|
494
|
+
identifier: emitCSharpName(prop.name, "properties", currentInitContext),
|
|
495
|
+
},
|
|
496
|
+
right: adaptedValueAst,
|
|
497
|
+
};
|
|
498
|
+
});
|
|
499
|
+
return [
|
|
500
|
+
{
|
|
501
|
+
kind: "objectCreationExpression",
|
|
502
|
+
type: safeTargetTypeAst,
|
|
503
|
+
arguments: [],
|
|
504
|
+
initializer: assignments,
|
|
505
|
+
},
|
|
506
|
+
currentInitContext,
|
|
507
|
+
];
|
|
508
|
+
};
|
|
509
|
+
const sourceMayBeNullish = hasNullishBranch(sourceType);
|
|
510
|
+
if (emittedAst.kind === "identifierExpression") {
|
|
511
|
+
const [initializer, initializerContext] = buildInitializer(emittedAst, currentContext);
|
|
512
|
+
if (!sourceMayBeNullish) {
|
|
513
|
+
return [initializer, initializerContext];
|
|
514
|
+
}
|
|
515
|
+
return [
|
|
516
|
+
{
|
|
517
|
+
kind: "conditionalExpression",
|
|
518
|
+
condition: {
|
|
519
|
+
kind: "binaryExpression",
|
|
520
|
+
operatorToken: "==",
|
|
521
|
+
left: emittedAst,
|
|
522
|
+
right: { kind: "literalExpression", text: "null" },
|
|
523
|
+
},
|
|
524
|
+
whenTrue: {
|
|
525
|
+
kind: "defaultExpression",
|
|
526
|
+
type: safeTargetTypeAst,
|
|
527
|
+
},
|
|
528
|
+
whenFalse: initializer,
|
|
529
|
+
},
|
|
530
|
+
initializerContext,
|
|
531
|
+
];
|
|
532
|
+
}
|
|
533
|
+
const temp = allocateLocalName("__struct", currentContext);
|
|
534
|
+
currentContext = temp.context;
|
|
535
|
+
const tempIdentifier = {
|
|
536
|
+
kind: "identifierExpression",
|
|
537
|
+
identifier: temp.emittedName,
|
|
538
|
+
};
|
|
539
|
+
const statements = [
|
|
540
|
+
{
|
|
541
|
+
kind: "localDeclarationStatement",
|
|
542
|
+
modifiers: [],
|
|
543
|
+
type: { kind: "varType" },
|
|
544
|
+
declarators: [{ name: temp.emittedName, initializer: emittedAst }],
|
|
545
|
+
},
|
|
546
|
+
];
|
|
547
|
+
if (sourceMayBeNullish) {
|
|
548
|
+
statements.push({
|
|
549
|
+
kind: "ifStatement",
|
|
550
|
+
condition: {
|
|
551
|
+
kind: "binaryExpression",
|
|
552
|
+
operatorToken: "==",
|
|
553
|
+
left: tempIdentifier,
|
|
554
|
+
right: { kind: "literalExpression", text: "null" },
|
|
555
|
+
},
|
|
556
|
+
thenStatement: {
|
|
557
|
+
kind: "blockStatement",
|
|
558
|
+
statements: [
|
|
559
|
+
{
|
|
560
|
+
kind: "returnStatement",
|
|
561
|
+
expression: {
|
|
562
|
+
kind: "defaultExpression",
|
|
563
|
+
type: safeTargetTypeAst,
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
],
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
const [initializer, initializerContext] = buildInitializer(tempIdentifier, currentContext);
|
|
571
|
+
currentContext = initializerContext;
|
|
572
|
+
statements.push({
|
|
573
|
+
kind: "returnStatement",
|
|
574
|
+
expression: initializer,
|
|
575
|
+
});
|
|
576
|
+
const lambdaAst = {
|
|
577
|
+
kind: "lambdaExpression",
|
|
578
|
+
isAsync: false,
|
|
579
|
+
parameters: [],
|
|
580
|
+
body: {
|
|
581
|
+
kind: "blockStatement",
|
|
582
|
+
statements,
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
return [
|
|
586
|
+
{
|
|
587
|
+
kind: "invocationExpression",
|
|
588
|
+
expression: {
|
|
589
|
+
kind: "parenthesizedExpression",
|
|
590
|
+
expression: {
|
|
591
|
+
kind: "castExpression",
|
|
592
|
+
type: buildDelegateType([], safeTargetTypeAst),
|
|
593
|
+
expression: {
|
|
594
|
+
kind: "parenthesizedExpression",
|
|
595
|
+
expression: lambdaAst,
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
arguments: [],
|
|
600
|
+
},
|
|
601
|
+
currentContext,
|
|
602
|
+
];
|
|
603
|
+
}
|
|
604
|
+
const targetElementType = getArrayElementType(expectedType, context);
|
|
605
|
+
const sourceElementType = getArrayElementType(sourceType, context);
|
|
606
|
+
if (targetElementType && sourceElementType) {
|
|
607
|
+
const item = allocateLocalName("__item", context);
|
|
608
|
+
let currentContext = item.context;
|
|
609
|
+
const itemIdentifier = {
|
|
610
|
+
kind: "identifierExpression",
|
|
611
|
+
identifier: item.emittedName,
|
|
612
|
+
};
|
|
613
|
+
const [adaptedElementAst, adaptedContext] = tryAdaptStructuralExpressionAst(itemIdentifier, sourceElementType, currentContext, targetElementType) ?? [undefined, currentContext];
|
|
614
|
+
currentContext = adaptedContext;
|
|
615
|
+
if (adaptedElementAst !== undefined) {
|
|
616
|
+
const selectAst = {
|
|
617
|
+
kind: "invocationExpression",
|
|
618
|
+
expression: {
|
|
619
|
+
kind: "identifierExpression",
|
|
620
|
+
identifier: "global::System.Linq.Enumerable.Select",
|
|
621
|
+
},
|
|
622
|
+
arguments: [
|
|
623
|
+
emittedAst,
|
|
624
|
+
{
|
|
625
|
+
kind: "lambdaExpression",
|
|
626
|
+
isAsync: false,
|
|
627
|
+
parameters: [{ name: item.emittedName }],
|
|
628
|
+
body: adaptedElementAst,
|
|
629
|
+
},
|
|
630
|
+
],
|
|
631
|
+
};
|
|
632
|
+
const toArrayAst = {
|
|
633
|
+
kind: "invocationExpression",
|
|
634
|
+
expression: {
|
|
635
|
+
kind: "identifierExpression",
|
|
636
|
+
identifier: "global::System.Linq.Enumerable.ToArray",
|
|
637
|
+
},
|
|
638
|
+
arguments: [selectAst],
|
|
639
|
+
};
|
|
640
|
+
if (!hasNullishBranch(sourceType)) {
|
|
641
|
+
return [toArrayAst, currentContext];
|
|
642
|
+
}
|
|
643
|
+
if (isDirectlyReusableExpression(emittedAst)) {
|
|
644
|
+
return [
|
|
645
|
+
{
|
|
646
|
+
kind: "conditionalExpression",
|
|
647
|
+
condition: {
|
|
648
|
+
kind: "binaryExpression",
|
|
649
|
+
operatorToken: "==",
|
|
650
|
+
left: emittedAst,
|
|
651
|
+
right: { kind: "literalExpression", text: "null" },
|
|
652
|
+
},
|
|
653
|
+
whenTrue: { kind: "defaultExpression" },
|
|
654
|
+
whenFalse: toArrayAst,
|
|
655
|
+
},
|
|
656
|
+
currentContext,
|
|
657
|
+
];
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
const targetValueType = getDictionaryValueType(expectedType, context);
|
|
662
|
+
const sourceValueType = getDictionaryValueType(sourceType, context);
|
|
663
|
+
if (targetValueType && sourceValueType) {
|
|
664
|
+
let currentContext = context;
|
|
665
|
+
const [targetValueTypeAst, valueTypeContext] = emitTypeAst(targetValueType, currentContext);
|
|
666
|
+
currentContext = valueTypeContext;
|
|
667
|
+
const dictTypeAst = {
|
|
668
|
+
kind: "identifierType",
|
|
669
|
+
name: "global::System.Collections.Generic.Dictionary",
|
|
670
|
+
typeArguments: [
|
|
671
|
+
{ kind: "predefinedType", keyword: "string" },
|
|
672
|
+
targetValueTypeAst,
|
|
673
|
+
],
|
|
674
|
+
};
|
|
675
|
+
const sourceTemp = allocateLocalName("__dict", currentContext);
|
|
676
|
+
currentContext = sourceTemp.context;
|
|
677
|
+
const entryTemp = allocateLocalName("__entry", currentContext);
|
|
678
|
+
currentContext = entryTemp.context;
|
|
679
|
+
const resultTemp = allocateLocalName("__result", currentContext);
|
|
680
|
+
currentContext = resultTemp.context;
|
|
681
|
+
const entryValueAst = {
|
|
682
|
+
kind: "memberAccessExpression",
|
|
683
|
+
expression: {
|
|
684
|
+
kind: "identifierExpression",
|
|
685
|
+
identifier: entryTemp.emittedName,
|
|
686
|
+
},
|
|
687
|
+
memberName: "Value",
|
|
688
|
+
};
|
|
689
|
+
const [adaptedValueAst, adaptedContext] = tryAdaptStructuralExpressionAst(entryValueAst, sourceValueType, currentContext, targetValueType) ?? [undefined, currentContext];
|
|
690
|
+
currentContext = adaptedContext;
|
|
691
|
+
if (adaptedValueAst !== undefined) {
|
|
692
|
+
const statements = [
|
|
693
|
+
{
|
|
694
|
+
kind: "localDeclarationStatement",
|
|
695
|
+
modifiers: [],
|
|
696
|
+
type: { kind: "varType" },
|
|
697
|
+
declarators: [
|
|
698
|
+
{
|
|
699
|
+
name: sourceTemp.emittedName,
|
|
700
|
+
initializer: emittedAst,
|
|
701
|
+
},
|
|
702
|
+
],
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
kind: "ifStatement",
|
|
706
|
+
condition: {
|
|
707
|
+
kind: "binaryExpression",
|
|
708
|
+
operatorToken: "==",
|
|
709
|
+
left: {
|
|
710
|
+
kind: "identifierExpression",
|
|
711
|
+
identifier: sourceTemp.emittedName,
|
|
712
|
+
},
|
|
713
|
+
right: { kind: "literalExpression", text: "null" },
|
|
714
|
+
},
|
|
715
|
+
thenStatement: {
|
|
716
|
+
kind: "blockStatement",
|
|
717
|
+
statements: [
|
|
718
|
+
{
|
|
719
|
+
kind: "returnStatement",
|
|
720
|
+
expression: { kind: "defaultExpression", type: dictTypeAst },
|
|
721
|
+
},
|
|
722
|
+
],
|
|
723
|
+
},
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
kind: "localDeclarationStatement",
|
|
727
|
+
modifiers: [],
|
|
728
|
+
type: { kind: "varType" },
|
|
729
|
+
declarators: [
|
|
730
|
+
{
|
|
731
|
+
name: resultTemp.emittedName,
|
|
732
|
+
initializer: {
|
|
733
|
+
kind: "objectCreationExpression",
|
|
734
|
+
type: dictTypeAst,
|
|
735
|
+
arguments: [],
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
],
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
kind: "foreachStatement",
|
|
742
|
+
isAwait: false,
|
|
743
|
+
type: { kind: "varType" },
|
|
744
|
+
identifier: entryTemp.emittedName,
|
|
745
|
+
expression: {
|
|
746
|
+
kind: "identifierExpression",
|
|
747
|
+
identifier: sourceTemp.emittedName,
|
|
748
|
+
},
|
|
749
|
+
body: {
|
|
750
|
+
kind: "blockStatement",
|
|
751
|
+
statements: [
|
|
752
|
+
{
|
|
753
|
+
kind: "expressionStatement",
|
|
754
|
+
expression: {
|
|
755
|
+
kind: "assignmentExpression",
|
|
756
|
+
operatorToken: "=",
|
|
757
|
+
left: {
|
|
758
|
+
kind: "elementAccessExpression",
|
|
759
|
+
expression: {
|
|
760
|
+
kind: "identifierExpression",
|
|
761
|
+
identifier: resultTemp.emittedName,
|
|
762
|
+
},
|
|
763
|
+
arguments: [
|
|
764
|
+
{
|
|
765
|
+
kind: "memberAccessExpression",
|
|
766
|
+
expression: {
|
|
767
|
+
kind: "identifierExpression",
|
|
768
|
+
identifier: entryTemp.emittedName,
|
|
769
|
+
},
|
|
770
|
+
memberName: "Key",
|
|
771
|
+
},
|
|
772
|
+
],
|
|
773
|
+
},
|
|
774
|
+
right: adaptedValueAst,
|
|
775
|
+
},
|
|
776
|
+
},
|
|
777
|
+
],
|
|
778
|
+
},
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
kind: "returnStatement",
|
|
782
|
+
expression: {
|
|
783
|
+
kind: "identifierExpression",
|
|
784
|
+
identifier: resultTemp.emittedName,
|
|
785
|
+
},
|
|
786
|
+
},
|
|
787
|
+
];
|
|
788
|
+
return [
|
|
789
|
+
{
|
|
790
|
+
kind: "invocationExpression",
|
|
791
|
+
expression: {
|
|
792
|
+
kind: "parenthesizedExpression",
|
|
793
|
+
expression: {
|
|
794
|
+
kind: "castExpression",
|
|
795
|
+
type: buildDelegateType([], dictTypeAst),
|
|
796
|
+
expression: {
|
|
797
|
+
kind: "parenthesizedExpression",
|
|
798
|
+
expression: {
|
|
799
|
+
kind: "lambdaExpression",
|
|
800
|
+
isAsync: false,
|
|
801
|
+
parameters: [],
|
|
802
|
+
body: {
|
|
803
|
+
kind: "blockStatement",
|
|
804
|
+
statements,
|
|
805
|
+
},
|
|
806
|
+
},
|
|
807
|
+
},
|
|
808
|
+
},
|
|
809
|
+
},
|
|
810
|
+
arguments: [],
|
|
811
|
+
},
|
|
812
|
+
currentContext,
|
|
813
|
+
];
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return undefined;
|
|
817
|
+
};
|
|
20
818
|
const getBareTypeParameterName = (type, context) => {
|
|
21
819
|
if (type.kind === "typeParameterType")
|
|
22
820
|
return type.name;
|
|
@@ -691,7 +1489,8 @@ export const emitExpressionAst = (expr, context, expectedType) => {
|
|
|
691
1489
|
})();
|
|
692
1490
|
const [castedAst, castedContext] = maybeCastNullishTypeParamAst(expr, ast, newContext, expectedType);
|
|
693
1491
|
const [dictUpcastAst, dictUpcastContext] = maybeUpcastDictionaryUnionValueAst(expr, castedAst, castedContext, expectedType);
|
|
694
|
-
const [
|
|
1492
|
+
const [materializedAst, materializedContext] = tryAdaptStructuralExpressionAst(dictUpcastAst, expr.inferredType, dictUpcastContext, expectedType) ?? [dictUpcastAst, dictUpcastContext];
|
|
1493
|
+
const [stringAdjustedAst, stringAdjustedContext] = maybeConvertCharToStringAst(expr, materializedAst, materializedContext, expectedType);
|
|
695
1494
|
return maybeUnwrapNullableValueTypeAst(expr, stringAdjustedAst, stringAdjustedContext, expectedType);
|
|
696
1495
|
};
|
|
697
1496
|
// Re-export commonly used functions from barrel
|