@tsonic/frontend 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/package.json +53 -0
  2. package/src/dependency-graph.ts +18 -0
  3. package/src/dotnet-metadata.ts +121 -0
  4. package/src/graph/builder.ts +81 -0
  5. package/src/graph/circular.ts +58 -0
  6. package/src/graph/extraction/exports.ts +55 -0
  7. package/src/graph/extraction/imports.ts +81 -0
  8. package/src/graph/extraction/index.ts +7 -0
  9. package/src/graph/extraction/orchestrator.ts +99 -0
  10. package/src/graph/extraction.ts +10 -0
  11. package/src/graph/helpers.ts +51 -0
  12. package/src/graph/index.ts +17 -0
  13. package/src/graph/types.ts +13 -0
  14. package/src/index.ts +80 -0
  15. package/src/ir/binding-resolution.test.ts +585 -0
  16. package/src/ir/builder/exports.ts +78 -0
  17. package/src/ir/builder/helpers.ts +27 -0
  18. package/src/ir/builder/imports.ts +153 -0
  19. package/src/ir/builder/index.ts +10 -0
  20. package/src/ir/builder/orchestrator.ts +178 -0
  21. package/src/ir/builder/statements.ts +55 -0
  22. package/src/ir/builder/types.ts +8 -0
  23. package/src/ir/builder/validation.ts +129 -0
  24. package/src/ir/builder.test.ts +581 -0
  25. package/src/ir/builder.ts +14 -0
  26. package/src/ir/converters/expressions/access.ts +99 -0
  27. package/src/ir/converters/expressions/calls.ts +137 -0
  28. package/src/ir/converters/expressions/collections.ts +84 -0
  29. package/src/ir/converters/expressions/functions.ts +62 -0
  30. package/src/ir/converters/expressions/helpers.ts +264 -0
  31. package/src/ir/converters/expressions/index.ts +43 -0
  32. package/src/ir/converters/expressions/literals.ts +22 -0
  33. package/src/ir/converters/expressions/operators.ts +147 -0
  34. package/src/ir/converters/expressions/other.ts +60 -0
  35. package/src/ir/converters/statements/control/blocks.ts +22 -0
  36. package/src/ir/converters/statements/control/conditionals.ts +67 -0
  37. package/src/ir/converters/statements/control/exceptions.ts +43 -0
  38. package/src/ir/converters/statements/control/index.ts +17 -0
  39. package/src/ir/converters/statements/control/loops.ts +99 -0
  40. package/src/ir/converters/statements/control.ts +17 -0
  41. package/src/ir/converters/statements/declarations/classes/constructors.ts +120 -0
  42. package/src/ir/converters/statements/declarations/classes/index.ts +12 -0
  43. package/src/ir/converters/statements/declarations/classes/methods.ts +61 -0
  44. package/src/ir/converters/statements/declarations/classes/orchestrator.ts +166 -0
  45. package/src/ir/converters/statements/declarations/classes/override-detection.ts +116 -0
  46. package/src/ir/converters/statements/declarations/classes/properties.ts +63 -0
  47. package/src/ir/converters/statements/declarations/classes.ts +6 -0
  48. package/src/ir/converters/statements/declarations/enums.ts +29 -0
  49. package/src/ir/converters/statements/declarations/functions.ts +39 -0
  50. package/src/ir/converters/statements/declarations/index.ts +14 -0
  51. package/src/ir/converters/statements/declarations/interfaces.ts +131 -0
  52. package/src/ir/converters/statements/declarations/registry.ts +45 -0
  53. package/src/ir/converters/statements/declarations/type-aliases.ts +25 -0
  54. package/src/ir/converters/statements/declarations/variables.ts +60 -0
  55. package/src/ir/converters/statements/declarations.ts +16 -0
  56. package/src/ir/converters/statements/helpers.ts +174 -0
  57. package/src/ir/converters/statements/index.ts +40 -0
  58. package/src/ir/expression-converter.ts +207 -0
  59. package/src/ir/generic-validator.ts +100 -0
  60. package/src/ir/hierarchical-bindings-e2e.test.ts +163 -0
  61. package/src/ir/index.ts +6 -0
  62. package/src/ir/statement-converter.ts +128 -0
  63. package/src/ir/type-converter/arrays.ts +20 -0
  64. package/src/ir/type-converter/converter.ts +10 -0
  65. package/src/ir/type-converter/functions.ts +22 -0
  66. package/src/ir/type-converter/index.ts +11 -0
  67. package/src/ir/type-converter/inference.ts +122 -0
  68. package/src/ir/type-converter/literals.ts +40 -0
  69. package/src/ir/type-converter/objects.ts +107 -0
  70. package/src/ir/type-converter/orchestrator.ts +85 -0
  71. package/src/ir/type-converter/patterns.ts +73 -0
  72. package/src/ir/type-converter/primitives.ts +57 -0
  73. package/src/ir/type-converter/references.ts +64 -0
  74. package/src/ir/type-converter/unions-intersections.ts +34 -0
  75. package/src/ir/type-converter.ts +13 -0
  76. package/src/ir/types/expressions.ts +215 -0
  77. package/src/ir/types/guards.ts +39 -0
  78. package/src/ir/types/helpers.ts +135 -0
  79. package/src/ir/types/index.ts +108 -0
  80. package/src/ir/types/ir-types.ts +96 -0
  81. package/src/ir/types/module.ts +57 -0
  82. package/src/ir/types/statements.ts +238 -0
  83. package/src/ir/types.ts +97 -0
  84. package/src/metadata/bindings-loader.test.ts +144 -0
  85. package/src/metadata/bindings-loader.ts +357 -0
  86. package/src/metadata/index.ts +15 -0
  87. package/src/metadata/library-loader.ts +153 -0
  88. package/src/metadata/loader.test.ts +156 -0
  89. package/src/metadata/loader.ts +382 -0
  90. package/src/program/bindings.test.ts +512 -0
  91. package/src/program/bindings.ts +253 -0
  92. package/src/program/config.ts +30 -0
  93. package/src/program/creation.ts +249 -0
  94. package/src/program/dependency-graph.ts +245 -0
  95. package/src/program/diagnostics.ts +103 -0
  96. package/src/program/index.ts +19 -0
  97. package/src/program/metadata.ts +68 -0
  98. package/src/program/queries.ts +18 -0
  99. package/src/program/types.ts +38 -0
  100. package/src/program.ts +13 -0
  101. package/src/resolver/dotnet-import-resolver.ts +226 -0
  102. package/src/resolver/import-resolution.ts +177 -0
  103. package/src/resolver/index.ts +18 -0
  104. package/src/resolver/namespace.test.ts +86 -0
  105. package/src/resolver/namespace.ts +42 -0
  106. package/src/resolver/naming.ts +38 -0
  107. package/src/resolver/path-resolution.ts +22 -0
  108. package/src/resolver/types.ts +15 -0
  109. package/src/resolver.test.ts +155 -0
  110. package/src/resolver.ts +14 -0
  111. package/src/symbol-table/builder.ts +114 -0
  112. package/src/symbol-table/creation.ts +42 -0
  113. package/src/symbol-table/helpers.ts +18 -0
  114. package/src/symbol-table/index.ts +13 -0
  115. package/src/symbol-table/queries.ts +42 -0
  116. package/src/symbol-table/types.ts +28 -0
  117. package/src/symbol-table.ts +14 -0
  118. package/src/types/bindings.ts +172 -0
  119. package/src/types/diagnostic.test.ts +164 -0
  120. package/src/types/diagnostic.ts +153 -0
  121. package/src/types/explicit-views.test.ts +113 -0
  122. package/src/types/explicit-views.ts +218 -0
  123. package/src/types/metadata.ts +229 -0
  124. package/src/types/module.ts +99 -0
  125. package/src/types/nested-types.test.ts +194 -0
  126. package/src/types/nested-types.ts +215 -0
  127. package/src/types/parameter-modifiers.ts +173 -0
  128. package/src/types/ref-parameters.test.ts +192 -0
  129. package/src/types/ref-parameters.ts +268 -0
  130. package/src/types/result.test.ts +157 -0
  131. package/src/types/result.ts +48 -0
  132. package/src/types/support-types.test.ts +81 -0
  133. package/src/types/support-types.ts +288 -0
  134. package/src/types/test-harness.ts +180 -0
  135. package/src/validation/exports.ts +98 -0
  136. package/src/validation/features.ts +89 -0
  137. package/src/validation/generics.ts +40 -0
  138. package/src/validation/helpers.ts +31 -0
  139. package/src/validation/imports.ts +97 -0
  140. package/src/validation/index.ts +11 -0
  141. package/src/validation/orchestrator.ts +51 -0
  142. package/src/validation/static-safety.ts +267 -0
  143. package/src/validator.test.ts +468 -0
  144. package/src/validator.ts +15 -0
  145. package/tsconfig.json +13 -0
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Nested Types Handling - Parse and transform nested type names.
3
+ *
4
+ * C# supports nested types (types inside types). TypeScript doesn't have true nested
5
+ * type syntax, so tsbindgen flattens them using a dollar sign separator:
6
+ *
7
+ * CLR: Outer+Nested (reflection uses +)
8
+ * TypeScript: Outer$Nested ($ separator)
9
+ * C#: Outer.Nested (dot in source code)
10
+ *
11
+ * @see spec/nested-types.md for complete documentation
12
+ */
13
+
14
+ /**
15
+ * Information about a nested type name.
16
+ */
17
+ export type NestedTypeInfo = {
18
+ readonly isNested: boolean;
19
+ readonly outerType: string;
20
+ readonly nestedType: string;
21
+ readonly fullPath: readonly string[];
22
+ readonly depth: number;
23
+ };
24
+
25
+ /**
26
+ * Parse a TypeScript type name to check if it represents a nested type.
27
+ *
28
+ * @param tsEmitName - TypeScript emit name (e.g., "List_1$Enumerator")
29
+ * @returns Nested type info if nested, undefined if not nested
30
+ */
31
+ export const parseNestedTypeName = (
32
+ tsEmitName: string
33
+ ): NestedTypeInfo | undefined => {
34
+ // Check if name contains $ separator
35
+ if (!tsEmitName.includes("$")) {
36
+ return undefined;
37
+ }
38
+
39
+ // Split on $ to get nesting path
40
+ const parts = tsEmitName.split("$");
41
+ if (parts.length < 2) {
42
+ return undefined;
43
+ }
44
+
45
+ const outerType = parts[0];
46
+ const nestedType = parts[parts.length - 1];
47
+
48
+ if (!outerType || !nestedType) {
49
+ return undefined;
50
+ }
51
+
52
+ return {
53
+ isNested: true,
54
+ outerType,
55
+ nestedType,
56
+ fullPath: parts,
57
+ depth: parts.length - 1,
58
+ };
59
+ };
60
+
61
+ /**
62
+ * Check if a TypeScript emit name represents a nested type.
63
+ *
64
+ * @param tsEmitName - TypeScript emit name
65
+ * @returns True if nested type
66
+ */
67
+ export const isNestedType = (tsEmitName: string): boolean => {
68
+ return tsEmitName.includes("$");
69
+ };
70
+
71
+ /**
72
+ * Convert TypeScript nested type name to C# nested type name.
73
+ *
74
+ * TypeScript: List_1$Enumerator
75
+ * C#: List<T>.Enumerator
76
+ *
77
+ * Note: This only converts the separator ($ → .). Generic arity substitution
78
+ * (_1 → <T>) is handled separately by generic type processing.
79
+ *
80
+ * @param tsEmitName - TypeScript emit name with $ separator
81
+ * @returns C# name with dot separator (generic arity not substituted)
82
+ */
83
+ export const tsCSharpNestedTypeName = (tsEmitName: string): string => {
84
+ return tsEmitName.replace(/\$/g, ".");
85
+ };
86
+
87
+ /**
88
+ * Convert CLR reflection name to TypeScript emit name.
89
+ *
90
+ * CLR uses + for nested types (reflection metadata).
91
+ * TypeScript uses $.
92
+ *
93
+ * CLR: List`1+Enumerator
94
+ * TypeScript: List_1$Enumerator
95
+ *
96
+ * @param clrName - CLR reflection name with + separator and ` for generics
97
+ * @returns TypeScript emit name with $ separator and _ for generics
98
+ */
99
+ export const clrToTsNestedTypeName = (clrName: string): string => {
100
+ // First replace backticks with underscores for generic arity
101
+ let tsName = clrName.replace(/`/g, "_");
102
+
103
+ // Then replace plus signs with dollar signs for nesting
104
+ tsName = tsName.replace(/\+/g, "$");
105
+
106
+ return tsName;
107
+ };
108
+
109
+ /**
110
+ * Convert TypeScript emit name to CLR reflection name.
111
+ *
112
+ * Reverse of clrToTsNestedTypeName.
113
+ *
114
+ * TypeScript: List_1$Enumerator
115
+ * CLR: List`1+Enumerator
116
+ *
117
+ * @param tsEmitName - TypeScript emit name
118
+ * @returns CLR reflection name
119
+ */
120
+ export const tsToCLRNestedTypeName = (tsEmitName: string): string => {
121
+ // Replace dollar signs with plus signs for nesting
122
+ let clrName = tsEmitName.replace(/\$/g, "+");
123
+
124
+ // Replace underscores with backticks for generic arity
125
+ // Note: This is a simple replacement. In practice, we need to be careful
126
+ // not to replace underscores that are part of the actual type name.
127
+ // For now, we assume _N pattern at end of each segment indicates arity.
128
+ clrName = clrName.replace(/_(\d+)/g, "`$1");
129
+
130
+ return clrName;
131
+ };
132
+
133
+ /**
134
+ * Get all nesting levels from a nested type name.
135
+ *
136
+ * @param tsEmitName - TypeScript emit name (e.g., "A$B$C")
137
+ * @returns Array of type names at each level ["A", "A$B", "A$B$C"]
138
+ */
139
+ export const getNestedTypeLevels = (tsEmitName: string): readonly string[] => {
140
+ if (!isNestedType(tsEmitName)) {
141
+ return [tsEmitName];
142
+ }
143
+
144
+ const parts = tsEmitName.split("$");
145
+ const levels: string[] = [];
146
+
147
+ for (let i = 0; i < parts.length; i++) {
148
+ levels.push(parts.slice(0, i + 1).join("$"));
149
+ }
150
+
151
+ return levels;
152
+ };
153
+
154
+ /**
155
+ * Get the outermost type name from a nested type.
156
+ *
157
+ * @param tsEmitName - TypeScript emit name (e.g., "List_1$Enumerator")
158
+ * @returns Outermost type name (e.g., "List_1")
159
+ */
160
+ export const getOutermostType = (tsEmitName: string): string => {
161
+ const dollarIndex = tsEmitName.indexOf("$");
162
+ if (dollarIndex === -1) {
163
+ return tsEmitName;
164
+ }
165
+ return tsEmitName.substring(0, dollarIndex);
166
+ };
167
+
168
+ /**
169
+ * Get the innermost type name from a nested type.
170
+ *
171
+ * @param tsEmitName - TypeScript emit name (e.g., "A$B$C")
172
+ * @returns Innermost type name (e.g., "C")
173
+ */
174
+ export const getInnermostType = (tsEmitName: string): string => {
175
+ const parts = tsEmitName.split("$");
176
+ const innermost = parts[parts.length - 1];
177
+ return innermost || tsEmitName;
178
+ };
179
+
180
+ /**
181
+ * Check if one type is nested inside another.
182
+ *
183
+ * @param innerType - Potentially nested type (e.g., "List_1$Enumerator")
184
+ * @param outerType - Potentially containing type (e.g., "List_1")
185
+ * @returns True if innerType is nested inside outerType
186
+ */
187
+ export const isNestedInside = (
188
+ innerType: string,
189
+ outerType: string
190
+ ): boolean => {
191
+ if (!isNestedType(innerType)) {
192
+ return false;
193
+ }
194
+
195
+ return innerType.startsWith(outerType + "$");
196
+ };
197
+
198
+ /**
199
+ * Get parent type name from a nested type.
200
+ *
201
+ * @param tsEmitName - TypeScript emit name (e.g., "A$B$C")
202
+ * @returns Parent type name (e.g., "A$B"), or undefined if not nested
203
+ */
204
+ export const getParentType = (tsEmitName: string): string | undefined => {
205
+ const info = parseNestedTypeName(tsEmitName);
206
+ if (!info) {
207
+ return undefined;
208
+ }
209
+
210
+ if (info.fullPath.length < 2) {
211
+ return undefined;
212
+ }
213
+
214
+ return info.fullPath.slice(0, -1).join("$");
215
+ };
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Parameter Modifier Tracking - Track ref/out/in types from @tsonic/types
3
+ *
4
+ * This module tracks which ref, out, and In types are legitimately imported
5
+ * from @tsonic/types to ensure we only apply parameter modifiers to the
6
+ * correct types (not user-defined types with the same names).
7
+ */
8
+
9
+ import * as ts from "typescript";
10
+
11
+ /**
12
+ * Registry for tracking trusted parameter modifier types
13
+ */
14
+ export class ParameterModifierRegistry {
15
+ private static instance: ParameterModifierRegistry | null = null;
16
+
17
+ // Track which symbols are imported from @tsonic/types
18
+ private trustedSymbols = new Set<string>();
19
+
20
+ // Track aliases (e.g., import { ref as myRef })
21
+ private aliasMap = new Map<string, string>();
22
+
23
+ private constructor() {}
24
+
25
+ /**
26
+ * Get singleton instance
27
+ */
28
+ static getInstance(): ParameterModifierRegistry {
29
+ if (!ParameterModifierRegistry.instance) {
30
+ ParameterModifierRegistry.instance = new ParameterModifierRegistry();
31
+ }
32
+ return ParameterModifierRegistry.instance;
33
+ }
34
+
35
+ /**
36
+ * Reset the registry (useful for testing)
37
+ */
38
+ static reset(): void {
39
+ if (ParameterModifierRegistry.instance) {
40
+ ParameterModifierRegistry.instance.trustedSymbols.clear();
41
+ ParameterModifierRegistry.instance.aliasMap.clear();
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Process an import declaration to track ref/out/In from @tsonic/types
47
+ */
48
+ processImport(importDecl: ts.ImportDeclaration): void {
49
+ // Check if this is an import from @tsonic/types
50
+ if (!ts.isStringLiteral(importDecl.moduleSpecifier)) {
51
+ return;
52
+ }
53
+
54
+ const moduleSpecifier = importDecl.moduleSpecifier.text;
55
+ if (moduleSpecifier !== "@tsonic/types") {
56
+ return;
57
+ }
58
+
59
+ // Extract named imports
60
+ if (importDecl.importClause?.namedBindings) {
61
+ if (ts.isNamedImports(importDecl.importClause.namedBindings)) {
62
+ for (const spec of importDecl.importClause.namedBindings.elements) {
63
+ const importedName = (spec.propertyName ?? spec.name).text;
64
+ const localName = spec.name.text;
65
+
66
+ // Check if this is ref, out, or In
67
+ if (
68
+ importedName === "ref" ||
69
+ importedName === "out" ||
70
+ importedName === "In"
71
+ ) {
72
+ this.trustedSymbols.add(localName);
73
+
74
+ // Track alias if different from imported name
75
+ if (importedName !== localName) {
76
+ this.aliasMap.set(localName, importedName);
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Check if a type name is a trusted parameter modifier type
86
+ */
87
+ isTrustedParameterModifier(typeName: string): boolean {
88
+ return this.trustedSymbols.has(typeName);
89
+ }
90
+
91
+ /**
92
+ * Get the parameter modifier kind for a trusted type
93
+ * @returns 'ref', 'out', 'in', or null if not a parameter modifier
94
+ */
95
+ getParameterModifierKind(typeName: string): "ref" | "out" | "in" | null {
96
+ if (!this.isTrustedParameterModifier(typeName)) {
97
+ return null;
98
+ }
99
+
100
+ // Check if it's an alias
101
+ const originalName = this.aliasMap.get(typeName) ?? typeName;
102
+
103
+ switch (originalName) {
104
+ case "ref":
105
+ return "ref";
106
+ case "out":
107
+ return "out";
108
+ case "In":
109
+ return "in";
110
+ default:
111
+ return null;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Check if a TypeScript type is a parameter modifier type from @tsonic/types
117
+ * This checks both the type name and verifies it was imported from @tsonic/types
118
+ */
119
+ isParameterModifierType(type: ts.Type, _checker: ts.TypeChecker): boolean {
120
+ // Get the symbol name
121
+ const symbol = type.aliasSymbol || type.symbol;
122
+ if (!symbol) {
123
+ return false;
124
+ }
125
+
126
+ const typeName = symbol.getName();
127
+ return this.isTrustedParameterModifier(typeName);
128
+ }
129
+
130
+ /**
131
+ * Get parameter modifier info from a type
132
+ */
133
+ getParameterModifierInfo(
134
+ type: ts.Type,
135
+ _checker: ts.TypeChecker
136
+ ): { kind: "ref" | "out" | "in"; wrappedType: ts.Type } | null {
137
+ const symbol = type.aliasSymbol || type.symbol;
138
+ if (!symbol) {
139
+ return null;
140
+ }
141
+
142
+ const typeName = symbol.getName();
143
+ const kind = this.getParameterModifierKind(typeName);
144
+ if (!kind) {
145
+ return null;
146
+ }
147
+
148
+ // Extract the wrapped type (T in ref<T>, out<T>, or In<T>)
149
+ let wrappedType: ts.Type | undefined;
150
+
151
+ if (type.aliasTypeArguments && type.aliasTypeArguments.length > 0) {
152
+ wrappedType = type.aliasTypeArguments[0];
153
+ } else if (
154
+ (type as any).typeArguments &&
155
+ (type as any).typeArguments.length > 0
156
+ ) {
157
+ wrappedType = (type as any).typeArguments[0];
158
+ }
159
+
160
+ if (!wrappedType) {
161
+ return null;
162
+ }
163
+
164
+ return { kind, wrappedType };
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Get the global parameter modifier registry instance
170
+ */
171
+ export const getParameterModifierRegistry = (): ParameterModifierRegistry => {
172
+ return ParameterModifierRegistry.getInstance();
173
+ };
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Tests for ref/out parameter handling
3
+ */
4
+
5
+ import { describe, it } from "mocha";
6
+ import { strict as assert } from "assert";
7
+ import {
8
+ getParameterModifier,
9
+ requiresTSByRef,
10
+ generateCSharpParameter,
11
+ generateCSharpArgument,
12
+ } from "./ref-parameters.js";
13
+ import type { ParameterMetadata } from "./metadata.js";
14
+
15
+ describe("Ref Parameters", () => {
16
+ describe("getParameterModifier", () => {
17
+ it("should return 'out' for out parameter", () => {
18
+ const param: ParameterMetadata = {
19
+ name: "result",
20
+ type: "System.Int32",
21
+ isRef: false,
22
+ isOut: true,
23
+ isParams: false,
24
+ };
25
+
26
+ const modifier = getParameterModifier(param);
27
+
28
+ assert.equal(modifier, "out");
29
+ });
30
+
31
+ it("should return 'ref' for ref parameter", () => {
32
+ const param: ParameterMetadata = {
33
+ name: "value",
34
+ type: "System.Int32",
35
+ isRef: true,
36
+ isOut: false,
37
+ isParams: false,
38
+ };
39
+
40
+ const modifier = getParameterModifier(param);
41
+
42
+ assert.equal(modifier, "ref");
43
+ });
44
+
45
+ it("should return 'in' for in parameter", () => {
46
+ const param: ParameterMetadata = {
47
+ name: "data",
48
+ type: "LargeStruct",
49
+ isRef: false,
50
+ isOut: false,
51
+ isIn: true,
52
+ isParams: false,
53
+ };
54
+
55
+ const modifier = getParameterModifier(param);
56
+
57
+ assert.equal(modifier, "in");
58
+ });
59
+
60
+ it("should return 'none' for regular parameter", () => {
61
+ const param: ParameterMetadata = {
62
+ name: "input",
63
+ type: "System.String",
64
+ isRef: false,
65
+ isOut: false,
66
+ isParams: false,
67
+ };
68
+
69
+ const modifier = getParameterModifier(param);
70
+
71
+ assert.equal(modifier, "none");
72
+ });
73
+
74
+ it("should prioritize 'out' over 'ref'", () => {
75
+ // This should not happen in valid metadata, but test defensive behavior
76
+ const param: ParameterMetadata = {
77
+ name: "weird",
78
+ type: "System.Int32",
79
+ isRef: true,
80
+ isOut: true,
81
+ isParams: false,
82
+ };
83
+
84
+ const modifier = getParameterModifier(param);
85
+
86
+ assert.equal(modifier, "out");
87
+ });
88
+ });
89
+
90
+ describe("requiresTSByRef", () => {
91
+ it("should return true for out parameter", () => {
92
+ const param: ParameterMetadata = {
93
+ name: "result",
94
+ type: "System.Int32",
95
+ isRef: false,
96
+ isOut: true,
97
+ isParams: false,
98
+ };
99
+
100
+ assert.ok(requiresTSByRef(param));
101
+ });
102
+
103
+ it("should return true for ref parameter", () => {
104
+ const param: ParameterMetadata = {
105
+ name: "value",
106
+ type: "System.Int32",
107
+ isRef: true,
108
+ isOut: false,
109
+ isParams: false,
110
+ };
111
+
112
+ assert.ok(requiresTSByRef(param));
113
+ });
114
+
115
+ it("should return true for in parameter", () => {
116
+ const param: ParameterMetadata = {
117
+ name: "data",
118
+ type: "LargeStruct",
119
+ isRef: false,
120
+ isOut: false,
121
+ isIn: true,
122
+ isParams: false,
123
+ };
124
+
125
+ assert.ok(requiresTSByRef(param));
126
+ });
127
+
128
+ it("should return false for regular parameter", () => {
129
+ const param: ParameterMetadata = {
130
+ name: "input",
131
+ type: "System.String",
132
+ isRef: false,
133
+ isOut: false,
134
+ isParams: false,
135
+ };
136
+
137
+ assert.ok(!requiresTSByRef(param));
138
+ });
139
+ });
140
+
141
+ describe("generateCSharpParameter", () => {
142
+ it("should generate out parameter", () => {
143
+ const param = generateCSharpParameter("out", "int", "result");
144
+
145
+ assert.equal(param, "out int result");
146
+ });
147
+
148
+ it("should generate ref parameter", () => {
149
+ const param = generateCSharpParameter("ref", "string", "value");
150
+
151
+ assert.equal(param, "ref string value");
152
+ });
153
+
154
+ it("should generate in parameter", () => {
155
+ const param = generateCSharpParameter("in", "LargeStruct", "data");
156
+
157
+ assert.equal(param, "in LargeStruct data");
158
+ });
159
+
160
+ it("should generate regular parameter without keyword", () => {
161
+ const param = generateCSharpParameter("none", "string", "input");
162
+
163
+ assert.equal(param, "string input");
164
+ });
165
+ });
166
+
167
+ describe("generateCSharpArgument", () => {
168
+ it("should generate out argument", () => {
169
+ const arg = generateCSharpArgument("out", "result");
170
+
171
+ assert.equal(arg, "out result");
172
+ });
173
+
174
+ it("should generate ref argument", () => {
175
+ const arg = generateCSharpArgument("ref", "value");
176
+
177
+ assert.equal(arg, "ref value");
178
+ });
179
+
180
+ it("should generate in argument", () => {
181
+ const arg = generateCSharpArgument("in", "data");
182
+
183
+ assert.equal(arg, "in data");
184
+ });
185
+
186
+ it("should generate regular argument without keyword", () => {
187
+ const arg = generateCSharpArgument("none", "input");
188
+
189
+ assert.equal(arg, "input");
190
+ });
191
+ });
192
+ });