@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.
Files changed (71) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/core/semantic/imports.js +9 -0
  3. package/dist/core/semantic/imports.js.map +1 -1
  4. package/dist/core/semantic/imports.test.js +43 -0
  5. package/dist/core/semantic/imports.test.js.map +1 -1
  6. package/dist/core/semantic/type-resolution.d.ts +1 -0
  7. package/dist/core/semantic/type-resolution.d.ts.map +1 -1
  8. package/dist/core/semantic/type-resolution.js +167 -5
  9. package/dist/core/semantic/type-resolution.js.map +1 -1
  10. package/dist/core/semantic/type-resolution.test.js +35 -0
  11. package/dist/core/semantic/type-resolution.test.js.map +1 -1
  12. package/dist/emitter-types/core.d.ts +5 -0
  13. package/dist/emitter-types/core.d.ts.map +1 -1
  14. package/dist/expression-emitter.d.ts.map +1 -1
  15. package/dist/expression-emitter.js +801 -2
  16. package/dist/expression-emitter.js.map +1 -1
  17. package/dist/expressions/access.d.ts.map +1 -1
  18. package/dist/expressions/access.js +58 -1
  19. package/dist/expressions/access.js.map +1 -1
  20. package/dist/expressions/calls/call-analysis.d.ts +1 -0
  21. package/dist/expressions/calls/call-analysis.d.ts.map +1 -1
  22. package/dist/expressions/calls/call-analysis.js +57 -0
  23. package/dist/expressions/calls/call-analysis.js.map +1 -1
  24. package/dist/expressions/calls/call-emitter.d.ts.map +1 -1
  25. package/dist/expressions/calls/call-emitter.js +10 -47
  26. package/dist/expressions/calls/call-emitter.js.map +1 -1
  27. package/dist/expressions/collections.d.ts.map +1 -1
  28. package/dist/expressions/collections.js +211 -3
  29. package/dist/expressions/collections.js.map +1 -1
  30. package/dist/expressions/identifiers.d.ts.map +1 -1
  31. package/dist/expressions/identifiers.js +8 -1
  32. package/dist/expressions/identifiers.js.map +1 -1
  33. package/dist/expressions/index.test.js +117 -0
  34. package/dist/expressions/index.test.js.map +1 -1
  35. package/dist/expressions/operators/binary-emitter.d.ts.map +1 -1
  36. package/dist/expressions/operators/binary-emitter.js +97 -56
  37. package/dist/expressions/operators/binary-emitter.js.map +1 -1
  38. package/dist/expressions/other.d.ts.map +1 -1
  39. package/dist/expressions/other.js +59 -1
  40. package/dist/expressions/other.js.map +1 -1
  41. package/dist/integration.test.js +393 -5
  42. package/dist/integration.test.js.map +1 -1
  43. package/dist/specialization/type-aliases.test.js +8 -0
  44. package/dist/specialization/type-aliases.test.js.map +1 -1
  45. package/dist/statements/classes/members/methods.d.ts.map +1 -1
  46. package/dist/statements/classes/members/methods.js +2 -13
  47. package/dist/statements/classes/members/methods.js.map +1 -1
  48. package/dist/statements/control/conditionals/guard-analysis.d.ts +42 -0
  49. package/dist/statements/control/conditionals/guard-analysis.d.ts.map +1 -1
  50. package/dist/statements/control/conditionals/guard-analysis.js +284 -121
  51. package/dist/statements/control/conditionals/guard-analysis.js.map +1 -1
  52. package/dist/statements/control/conditionals/if-emitter.d.ts.map +1 -1
  53. package/dist/statements/control/conditionals/if-emitter.js +168 -44
  54. package/dist/statements/control/conditionals/if-emitter.js.map +1 -1
  55. package/dist/statements/declarations/functions.d.ts.map +1 -1
  56. package/dist/statements/declarations/functions.js +2 -13
  57. package/dist/statements/declarations/functions.js.map +1 -1
  58. package/dist/statements/declarations/type-aliases.d.ts.map +1 -1
  59. package/dist/statements/declarations/type-aliases.js +2 -1
  60. package/dist/statements/declarations/type-aliases.js.map +1 -1
  61. package/dist/statements/declarations/variables.d.ts.map +1 -1
  62. package/dist/statements/declarations/variables.js +17 -15
  63. package/dist/statements/declarations/variables.js.map +1 -1
  64. package/dist/statements/index.test.js +940 -0
  65. package/dist/statements/index.test.js.map +1 -1
  66. package/dist/types/references.d.ts.map +1 -1
  67. package/dist/types/references.js +12 -8
  68. package/dist/types/references.js.map +1 -1
  69. package/dist/types/references.test.js +112 -0
  70. package/dist/types/references.test.js.map +1 -1
  71. 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 [stringAdjustedAst, stringAdjustedContext] = maybeConvertCharToStringAst(expr, dictUpcastAst, dictUpcastContext, expectedType);
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