goscript 0.0.14 → 0.0.17

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 (182) hide show
  1. package/README.md +154 -40
  2. package/builtin/builtin.ts +1168 -178
  3. package/compiler/analysis.go +726 -0
  4. package/compiler/compiler.go +5706 -4
  5. package/compiler/compiler_test.go +104 -0
  6. package/compiler/config.go +3 -3
  7. package/compiler/config_test.go +89 -0
  8. package/compiler/output.go +26 -0
  9. package/compiler/write-type-spec.go +506 -0
  10. package/compiler/writer.go +25 -0
  11. package/dist/builtin/builtin.d.ts +204 -67
  12. package/dist/builtin/builtin.js +845 -144
  13. package/dist/builtin/builtin.js.map +1 -1
  14. package/go.mod +4 -7
  15. package/go.sum +6 -11
  16. package/package.json +4 -3
  17. package/compiler/compile.go +0 -190
  18. package/compiler/compile_comment.go +0 -41
  19. package/compiler/compile_decls.go +0 -84
  20. package/compiler/compile_expr.go +0 -1022
  21. package/compiler/compile_field.go +0 -110
  22. package/compiler/compile_spec.go +0 -566
  23. package/compiler/compile_stmt.go +0 -1616
  24. package/compiler/context.go +0 -9
  25. package/compiler/file_compiler.go +0 -80
  26. package/compiler/output_path.go +0 -31
  27. package/compiler/pkg_compiler.go +0 -72
  28. package/compiler/types/tokens.go +0 -66
  29. package/compiler/types/types.go +0 -46
  30. package/dist/compliance/tests/array_literal/array_literal.gs.d.ts +0 -1
  31. package/dist/compliance/tests/array_literal/array_literal.gs.js +0 -15
  32. package/dist/compliance/tests/array_literal/array_literal.gs.js.map +0 -1
  33. package/dist/compliance/tests/async_basic/async_basic.gs.d.ts +0 -1
  34. package/dist/compliance/tests/async_basic/async_basic.gs.js +0 -24
  35. package/dist/compliance/tests/async_basic/async_basic.gs.js.map +0 -1
  36. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.d.ts +0 -1
  37. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js +0 -82
  38. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js.map +0 -1
  39. package/dist/compliance/tests/basic_arithmetic/basic_arithmetic.gs.d.ts +0 -1
  40. package/dist/compliance/tests/basic_arithmetic/basic_arithmetic.gs.js +0 -16
  41. package/dist/compliance/tests/basic_arithmetic/basic_arithmetic.gs.js.map +0 -1
  42. package/dist/compliance/tests/boolean_logic/boolean_logic.gs.d.ts +0 -1
  43. package/dist/compliance/tests/boolean_logic/boolean_logic.gs.js +0 -14
  44. package/dist/compliance/tests/boolean_logic/boolean_logic.gs.js.map +0 -1
  45. package/dist/compliance/tests/channel_basic/channel_basic.gs.d.ts +0 -1
  46. package/dist/compliance/tests/channel_basic/channel_basic.gs.js +0 -14
  47. package/dist/compliance/tests/channel_basic/channel_basic.gs.js.map +0 -1
  48. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.d.ts +0 -1
  49. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js +0 -27
  50. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js.map +0 -1
  51. package/dist/compliance/tests/constants/constants.gs.d.ts +0 -1
  52. package/dist/compliance/tests/constants/constants.gs.js +0 -16
  53. package/dist/compliance/tests/constants/constants.gs.js.map +0 -1
  54. package/dist/compliance/tests/copy_independence/copy_independence.gs.d.ts +0 -1
  55. package/dist/compliance/tests/copy_independence/copy_independence.gs.js +0 -33
  56. package/dist/compliance/tests/copy_independence/copy_independence.gs.js.map +0 -1
  57. package/dist/compliance/tests/defer_statement/defer_statement.gs.d.ts +0 -1
  58. package/dist/compliance/tests/defer_statement/defer_statement.gs.js +0 -75
  59. package/dist/compliance/tests/defer_statement/defer_statement.gs.js.map +0 -1
  60. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.d.ts +0 -1
  61. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +0 -37
  62. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +0 -1
  63. package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.d.ts +0 -1
  64. package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js +0 -29
  65. package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js.map +0 -1
  66. package/dist/compliance/tests/float64/float64.gs.d.ts +0 -1
  67. package/dist/compliance/tests/float64/float64.gs.js +0 -24
  68. package/dist/compliance/tests/float64/float64.gs.js.map +0 -1
  69. package/dist/compliance/tests/for_loop_basic/for_loop_basic.gs.d.ts +0 -1
  70. package/dist/compliance/tests/for_loop_basic/for_loop_basic.gs.js +0 -10
  71. package/dist/compliance/tests/for_loop_basic/for_loop_basic.gs.js.map +0 -1
  72. package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.d.ts +0 -1
  73. package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js +0 -11
  74. package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js.map +0 -1
  75. package/dist/compliance/tests/for_loop_condition_only/main.gs.d.ts +0 -1
  76. package/dist/compliance/tests/for_loop_condition_only/main.gs.js +0 -10
  77. package/dist/compliance/tests/for_loop_condition_only/main.gs.js.map +0 -1
  78. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.d.ts +0 -1
  79. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js +0 -14
  80. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js.map +0 -1
  81. package/dist/compliance/tests/for_range/for_range.gs.d.ts +0 -1
  82. package/dist/compliance/tests/for_range/for_range.gs.js +0 -39
  83. package/dist/compliance/tests/for_range/for_range.gs.js.map +0 -1
  84. package/dist/compliance/tests/for_range_index_use/for_range_index_use.gs.d.ts +0 -1
  85. package/dist/compliance/tests/for_range_index_use/for_range_index_use.gs.js +0 -15
  86. package/dist/compliance/tests/for_range_index_use/for_range_index_use.gs.js.map +0 -1
  87. package/dist/compliance/tests/func_literal/func_literal.gs.d.ts +0 -1
  88. package/dist/compliance/tests/func_literal/func_literal.gs.js +0 -10
  89. package/dist/compliance/tests/func_literal/func_literal.gs.js.map +0 -1
  90. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.d.ts +0 -12
  91. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js +0 -30
  92. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js.map +0 -1
  93. package/dist/compliance/tests/if_statement/if_statement.gs.d.ts +0 -1
  94. package/dist/compliance/tests/if_statement/if_statement.gs.js +0 -13
  95. package/dist/compliance/tests/if_statement/if_statement.gs.js.map +0 -1
  96. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.d.ts +0 -1
  97. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js +0 -12
  98. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js.map +0 -1
  99. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.d.ts +0 -1
  100. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js +0 -34
  101. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js.map +0 -1
  102. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.d.ts +0 -1
  103. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +0 -32
  104. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js.map +0 -1
  105. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.d.ts +0 -1
  106. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js +0 -40
  107. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +0 -1
  108. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.d.ts +0 -1
  109. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js +0 -51
  110. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js.map +0 -1
  111. package/dist/compliance/tests/map_support/map_support.gs.d.ts +0 -1
  112. package/dist/compliance/tests/map_support/map_support.gs.js +0 -88
  113. package/dist/compliance/tests/map_support/map_support.gs.js.map +0 -1
  114. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.d.ts +0 -1
  115. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js +0 -24
  116. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js.map +0 -1
  117. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.d.ts +0 -1
  118. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js +0 -33
  119. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js.map +0 -1
  120. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.d.ts +0 -1
  121. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js +0 -22
  122. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js.map +0 -1
  123. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.d.ts +0 -1
  124. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js +0 -33
  125. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js.map +0 -1
  126. package/dist/compliance/tests/multiple_return_values/multiple_return_values.gs.d.ts +0 -1
  127. package/dist/compliance/tests/multiple_return_values/multiple_return_values.gs.js +0 -17
  128. package/dist/compliance/tests/multiple_return_values/multiple_return_values.gs.js.map +0 -1
  129. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.d.ts +0 -1
  130. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js +0 -29
  131. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js.map +0 -1
  132. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.d.ts +0 -1
  133. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js +0 -27
  134. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js.map +0 -1
  135. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.d.ts +0 -1
  136. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js +0 -22
  137. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js.map +0 -1
  138. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.d.ts +0 -1
  139. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js +0 -20
  140. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js.map +0 -1
  141. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.d.ts +0 -1
  142. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js +0 -28
  143. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js.map +0 -1
  144. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.d.ts +0 -1
  145. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js +0 -30
  146. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js.map +0 -1
  147. package/dist/compliance/tests/select_statement/select_statement.gs.d.ts +0 -1
  148. package/dist/compliance/tests/select_statement/select_statement.gs.js +0 -207
  149. package/dist/compliance/tests/select_statement/select_statement.gs.js.map +0 -1
  150. package/dist/compliance/tests/simple/simple.gs.d.ts +0 -1
  151. package/dist/compliance/tests/simple/simple.gs.js +0 -6
  152. package/dist/compliance/tests/simple/simple.gs.js.map +0 -1
  153. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.d.ts +0 -1
  154. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js +0 -24
  155. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js.map +0 -1
  156. package/dist/compliance/tests/slices/slices.gs.d.ts +0 -1
  157. package/dist/compliance/tests/slices/slices.gs.js +0 -294
  158. package/dist/compliance/tests/slices/slices.gs.js.map +0 -1
  159. package/dist/compliance/tests/string_conversion/string_conversion.gs.d.ts +0 -1
  160. package/dist/compliance/tests/string_conversion/string_conversion.gs.js +0 -41
  161. package/dist/compliance/tests/string_conversion/string_conversion.gs.js.map +0 -1
  162. package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.d.ts +0 -1
  163. package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.js +0 -17
  164. package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.js.map +0 -1
  165. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.d.ts +0 -1
  166. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js +0 -48
  167. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js.map +0 -1
  168. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.d.ts +0 -1
  169. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js +0 -19
  170. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js.map +0 -1
  171. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.d.ts +0 -1
  172. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js +0 -26
  173. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js.map +0 -1
  174. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.d.ts +0 -1
  175. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js +0 -30
  176. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js.map +0 -1
  177. package/dist/compliance/tests/switch_statement/switch_statement.gs.d.ts +0 -1
  178. package/dist/compliance/tests/switch_statement/switch_statement.gs.js +0 -76
  179. package/dist/compliance/tests/switch_statement/switch_statement.gs.js.map +0 -1
  180. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.d.ts +0 -1
  181. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js +0 -31
  182. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js.map +0 -1
@@ -0,0 +1,726 @@
1
+ package compiler
2
+
3
+ import (
4
+ "go/ast"
5
+ "go/token"
6
+ "go/types"
7
+
8
+ "golang.org/x/tools/go/packages"
9
+ )
10
+
11
+ // fileImport tracks an import in a file.
12
+ type fileImport struct {
13
+ importPath string
14
+ importVars map[string]struct{}
15
+ }
16
+
17
+ // AssignmentType indicates how a variable's value was assigned or used.
18
+ type AssignmentType int
19
+
20
+ const (
21
+ // DirectAssignment represents a direct value copy (e.g., x = y)
22
+ DirectAssignment AssignmentType = iota
23
+ // AddressOfAssignment represents taking the address (e.g., p = &y)
24
+ // or assigning to a dereferenced pointer (*p = y) - indicating the pointer p is used.
25
+ AddressOfAssignment
26
+ )
27
+
28
+ // AssignmentInfo stores information about a single assignment source or destination.
29
+ type AssignmentInfo struct {
30
+ Object types.Object // The source or destination variable object
31
+ Type AssignmentType // The type of assignment involved
32
+ }
33
+
34
+ // VariableUsageInfo tracks how a variable is used throughout the code.
35
+ type VariableUsageInfo struct {
36
+ // Sources lists variables whose values (or addresses) are assigned TO this variable.
37
+ // Example: For `x = y`, y is a source for x. For `x = &y`, y is a source for x.
38
+ Sources []AssignmentInfo
39
+ // Destinations lists variables that are assigned the value (or address) FROM this variable.
40
+ // Example: For `y = x`, y is a destination for x. For `p = &x`, p is a destination for x.
41
+ Destinations []AssignmentInfo
42
+ }
43
+
44
+ // Analysis holds information gathered during the analysis phase of the Go code compilation.
45
+ // This data is used to make decisions about how to generate TypeScript code.
46
+ // Analysis is read-only after being built and should not be modified during code generation.
47
+ type Analysis struct {
48
+ // VariableUsage tracks how variables are assigned and used, particularly for pointer analysis.
49
+ // The key is the variable's types.Object.
50
+ VariableUsage map[types.Object]*VariableUsageInfo
51
+
52
+ // Imports stores the imports for the file
53
+ Imports map[string]*fileImport
54
+
55
+ // Cmap stores the comment map for the file
56
+ Cmap ast.CommentMap
57
+
58
+ // AsyncFuncs tracks which functions are async using the function's types.Object
59
+ // as the key to avoid false-positive matches with functions having the same name
60
+ AsyncFuncs map[types.Object]bool
61
+
62
+ // NeedsDeferMap tracks nodes that need defer handling
63
+ NeedsDeferMap map[ast.Node]bool
64
+
65
+ // IsInAsyncFunctionMap tracks nodes that are inside async functions
66
+ IsInAsyncFunctionMap map[ast.Node]bool
67
+ }
68
+
69
+ // NewAnalysis creates a new Analysis instance.
70
+ func NewAnalysis() *Analysis {
71
+ return &Analysis{
72
+ VariableUsage: make(map[types.Object]*VariableUsageInfo),
73
+ Imports: make(map[string]*fileImport),
74
+ AsyncFuncs: make(map[types.Object]bool),
75
+ NeedsDeferMap: make(map[ast.Node]bool),
76
+ IsInAsyncFunctionMap: make(map[ast.Node]bool),
77
+ }
78
+ }
79
+
80
+ // NeedsDefer returns whether the given node needs defer handling.
81
+ func (a *Analysis) NeedsDefer(node ast.Node) bool {
82
+ if node == nil {
83
+ return false
84
+ }
85
+ return a.NeedsDeferMap[node]
86
+ }
87
+
88
+ // IsInAsyncFunction returns whether the given node is inside an async function.
89
+ func (a *Analysis) IsInAsyncFunction(node ast.Node) bool {
90
+ if node == nil {
91
+ return false
92
+ }
93
+ return a.IsInAsyncFunctionMap[node]
94
+ }
95
+
96
+ // IsAsyncFunc returns whether the given object represents an async function.
97
+ func (a *Analysis) IsAsyncFunc(obj types.Object) bool {
98
+ if obj == nil {
99
+ return false
100
+ }
101
+ return a.AsyncFuncs[obj]
102
+ }
103
+
104
+ // IsFuncLitAsync checks if a function literal is async based on our analysis.
105
+ func (a *Analysis) IsFuncLitAsync(funcLit *ast.FuncLit) bool {
106
+ if funcLit == nil {
107
+ return false
108
+ }
109
+ // Function literals are marked during analysis if they contain async operations
110
+ return a.IsInAsyncFunctionMap[funcLit]
111
+ }
112
+
113
+ // NeedsBoxed returns whether the given object needs to be boxed.
114
+ // According to the new logic, a variable needs boxing if its address is taken
115
+ // and assigned to another variable (i.e., it appears as a destination with AddressOfAssignment).
116
+ func (a *Analysis) NeedsBoxed(obj types.Object) bool {
117
+ if obj == nil {
118
+ return false
119
+ }
120
+ usageInfo, exists := a.VariableUsage[obj]
121
+ if !exists {
122
+ return false
123
+ }
124
+ // Check if any destination assignment involves taking the address of 'obj'
125
+ for _, destInfo := range usageInfo.Destinations {
126
+ if destInfo.Type == AddressOfAssignment {
127
+ return true
128
+ }
129
+ }
130
+ return false
131
+ }
132
+
133
+ // NeedsBoxedAccess returns whether accessing the given object requires '.value' access in TypeScript.
134
+ // This function is critical for correctly handling pointer dereferencing by determining when
135
+ // a variable is boxed and needs .value to access its content.
136
+ //
137
+ // Two distinct cases determine when a variable needs .value access:
138
+ //
139
+ // 1. The variable itself is boxed (its address is taken)
140
+ // Example: let x = $.box(10) => x.value
141
+ //
142
+ // 2. For pointer variables: it points to a boxed struct variable rather than a direct struct literal
143
+ // Example: let ptrToVal = val (where val is boxed) => ptrToVal.value
144
+ // vs. let ptr = new MyStruct() => ptr (no .value needed)
145
+ //
146
+ // This distinction is crucial for avoiding over-dereferencing or under-dereferencing,
147
+ // which was the root cause of several bugs in our pointer handling.
148
+ func (a *Analysis) NeedsBoxedAccess(obj types.Object) bool {
149
+ if obj == nil {
150
+ return false
151
+ }
152
+
153
+ // First, check if the variable itself is boxed - this always requires .value
154
+ // A variable is boxed if its address is taken elsewhere in the code
155
+ if a.NeedsBoxed(obj) {
156
+ return true
157
+ }
158
+
159
+ // Check if this is a pointer variable pointing to a boxed struct value
160
+ objType := obj.Type()
161
+ if ptrType, isPointer := objType.Underlying().(*types.Pointer); isPointer {
162
+ // Check if it's a pointer to a struct
163
+ if elemType := ptrType.Elem(); elemType != nil {
164
+ if _, isStructType := elemType.Underlying().(*types.Struct); isStructType {
165
+ // For struct pointers, check if it points to a boxed struct variable
166
+ if usageInfo, exists := a.VariableUsage[obj]; exists {
167
+ for _, src := range usageInfo.Sources {
168
+ // Check if this pointer was assigned the address of another variable
169
+ // (e.g., ptr = &someVar) rather than a direct literal (ptr = &Struct{})
170
+ if src.Type == AddressOfAssignment && src.Object != nil {
171
+ // Bug fix: If the source variable is boxed, the pointer needs .value to access it
172
+ // This distinguishes between:
173
+ // - ptrToVal := &val (val is boxed, so we need ptrToVal.value)
174
+ // - ptr := &MyStruct{} (direct literal, no boxing needed)
175
+ return a.NeedsBoxed(src.Object)
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ return false
184
+ }
185
+
186
+ // NeedsBoxedDeref determines whether a pointer dereference operation (*ptr) needs
187
+ // the .value suffix in TypeScript when used in a direct dereference expression.
188
+ //
189
+ // Critical distinction (source of bugs):
190
+ //
191
+ // 1. For primitive types and pointers-to-primitive: Need .value
192
+ // *p => p!.value
193
+ // **p => p!.value!.value
194
+ //
195
+ // 2. For pointers to structs: No .value needed because structs are references
196
+ // *p => p!
197
+ // Where p is a pointer to a struct
198
+ //
199
+ // This distinction is essential because in TypeScript:
200
+ // - Primitives are stored inside $.Box with a .value property
201
+ // - Structs are reference types, so dereferencing just removes the null possibility
202
+ func (a *Analysis) NeedsBoxedDeref(ptrType types.Type) bool {
203
+ // If we don't have a valid pointer type, default to true (safer)
204
+ if ptrType == nil {
205
+ return true
206
+ }
207
+
208
+ // Unwrap the pointer to get the element type
209
+ ptrTypeUnwrapped, ok := ptrType.(*types.Pointer)
210
+ if !ok {
211
+ return true // Not a pointer type, default to true
212
+ }
213
+
214
+ // Get the underlying element type
215
+ elemType := ptrTypeUnwrapped.Elem()
216
+ if elemType == nil {
217
+ return true
218
+ }
219
+
220
+ // Check if the element is another pointer - if so, we always need .value
221
+ // This fixes the bug with multi-level pointer dereferencing like **p
222
+ if _, isPointer := elemType.(*types.Pointer); isPointer {
223
+ return true
224
+ }
225
+
226
+ // Check if the element is a struct (directly or via a named type)
227
+ // Bug fix: Struct pointers in TS don't need .value when dereferenced
228
+ // because structs are already references in JavaScript/TypeScript
229
+ if _, isStruct := elemType.Underlying().(*types.Struct); isStruct {
230
+ return false // Pointers to structs don't need .value suffix in direct dereference (*p)
231
+ }
232
+
233
+ // For all other cases (primitives, pointers-to-pointers, etc.) need .value
234
+ // This ensures primitives and nested pointers are correctly dereferenced
235
+ return true
236
+ }
237
+
238
+ // NeedsBoxedFieldAccess determines whether a pointer variable needs the .value
239
+ // suffix when accessing fields (e.g., ptr.field).
240
+ //
241
+ // Bug fix: This function was a major source of issues with struct field access.
242
+ // The critical discovery was that field access through a pointer depends not on the
243
+ // field itself, but on whether the pointer variable is boxed:
244
+ //
245
+ // 1. For normal struct pointers (unboxed): No .value needed
246
+ // Example: let ptr = new MyStruct() => ptr.field
247
+ // (Common case from &MyStruct{} literals)
248
+ //
249
+ // 2. For boxed struct pointers: Need .value to access the pointed-to struct
250
+ // Example: let ptrToVal = val (where val is boxed) => ptrToVal.value.field
251
+ //
252
+ // We ultimately delegated this decision to WriteSelectorExpr which examines
253
+ // the actual variable to determine if it's boxed, rather than just the type.
254
+ func (a *Analysis) NeedsBoxedFieldAccess(ptrType types.Type) bool {
255
+ // If we don't have a valid pointer type, default to false
256
+ if ptrType == nil {
257
+ return false
258
+ }
259
+
260
+ // Unwrap the pointer to get the element type
261
+ ptrTypeUnwrapped, ok := ptrType.(*types.Pointer)
262
+ if !ok {
263
+ return false // Not a pointer type, no dereference needed for field access
264
+ }
265
+
266
+ // Check if the element is a struct (directly or via a named type)
267
+ elemType := ptrTypeUnwrapped.Elem()
268
+ if elemType == nil {
269
+ return false // Not pointing to anything
270
+ }
271
+
272
+ // For pointers to structs, check if it's a struct type first
273
+ _, isStruct := elemType.Underlying().(*types.Struct)
274
+ if !isStruct {
275
+ return false // Not a pointer to a struct
276
+ }
277
+
278
+ // The critical decision: We'll determine if .value is needed in the WriteSelectorExpr function
279
+ // by checking if the pointer variable itself is boxed.
280
+ // This allows us to handle both:
281
+ // - ptr := &MyStruct{} (unboxed, direct access)
282
+ // - ptrToVal := &val (boxed, needs .value)
283
+ return false
284
+ }
285
+
286
+ // analysisVisitor implements ast.Visitor and is used to traverse the AST during analysis.
287
+ type analysisVisitor struct {
288
+ // analysis stores information gathered during the traversal
289
+ analysis *Analysis
290
+
291
+ // pkg provides type information and other package details
292
+ pkg *packages.Package
293
+
294
+ // inAsyncFunction tracks if we're currently inside an async function
295
+ inAsyncFunction bool
296
+
297
+ // currentFuncName tracks the name of the function we're currently analyzing
298
+ currentFuncName string
299
+
300
+ // currentReceiver tracks the object of the receiver if inside a method
301
+ currentReceiver *types.Var
302
+
303
+ // currentFuncObj tracks the object of the function declaration we're currently analyzing
304
+ currentFuncObj types.Object
305
+ }
306
+
307
+ // getOrCreateUsageInfo retrieves or creates the VariableUsageInfo for a given object.
308
+ func (v *analysisVisitor) getOrCreateUsageInfo(obj types.Object) *VariableUsageInfo {
309
+ if obj == nil {
310
+ return nil // Should not happen with valid objects
311
+ }
312
+ info, exists := v.analysis.VariableUsage[obj]
313
+ if !exists {
314
+ info = &VariableUsageInfo{}
315
+ v.analysis.VariableUsage[obj] = info
316
+ }
317
+ return info
318
+ }
319
+
320
+ // Visit implements the ast.Visitor interface.
321
+ // It analyzes each node in the AST to gather information needed for code generation.
322
+ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
323
+ if node == nil {
324
+ return nil
325
+ }
326
+
327
+ // Store async state for the current node
328
+ v.analysis.IsInAsyncFunctionMap[node] = v.inAsyncFunction
329
+
330
+ switch n := node.(type) {
331
+ case *ast.GenDecl:
332
+ // Handle general declarations (var, const, type, import)
333
+ if n.Tok == token.VAR {
334
+ for _, spec := range n.Specs {
335
+ if valueSpec, ok := spec.(*ast.ValueSpec); ok {
336
+ // Process each declared variable (LHS)
337
+ for i, lhsIdent := range valueSpec.Names {
338
+ if lhsIdent.Name == "_" {
339
+ continue
340
+ }
341
+ lhsObj := v.pkg.TypesInfo.ObjectOf(lhsIdent)
342
+ if lhsObj == nil {
343
+ continue
344
+ }
345
+ // Ensure usage info exists for LHS
346
+ lhsUsageInfo := v.getOrCreateUsageInfo(lhsObj)
347
+
348
+ // Check if there's a corresponding initial value (RHS)
349
+ if valueSpec.Values != nil && i < len(valueSpec.Values) {
350
+ rhsExpr := valueSpec.Values[i]
351
+
352
+ // --- Analyze RHS and Update Usage Info (similar to AssignStmt) ---
353
+ assignmentType := DirectAssignment
354
+ var sourceObj types.Object
355
+
356
+ if unaryExpr, ok := rhsExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
357
+ // Case: var lhs = &rhs_ident
358
+ assignmentType = AddressOfAssignment
359
+ if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
360
+ sourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
361
+ }
362
+ } else if rhsIdent, ok := rhsExpr.(*ast.Ident); ok {
363
+ // Case: var lhs = rhs_ident
364
+ assignmentType = DirectAssignment
365
+ sourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
366
+ }
367
+
368
+ // --- Record Usage ---
369
+ if sourceObj != nil {
370
+ // Record source for LHS
371
+ lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
372
+ Object: sourceObj,
373
+ Type: assignmentType,
374
+ })
375
+
376
+ // Record destination for RHS source
377
+ sourceUsageInfo := v.getOrCreateUsageInfo(sourceObj)
378
+ sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
379
+ Object: lhsObj,
380
+ Type: assignmentType,
381
+ })
382
+ }
383
+ // Note: We might need to handle var lhs = &T{} cases later if necessary.
384
+ }
385
+ }
386
+ }
387
+ }
388
+ }
389
+ // Continue traversal AFTER processing the declaration itself
390
+ // to handle expressions within initial values if needed.
391
+ // However, the core usage tracking is done above.
392
+ // Let standard traversal handle children.
393
+ return v
394
+
395
+ case *ast.FuncDecl:
396
+ // Determine if this function declaration is async based on its body
397
+ v.currentFuncName = n.Name.Name
398
+ isAsync := false
399
+ if n.Body != nil {
400
+ containsAsyncOps := v.containsAsyncOperations(n.Body)
401
+ if containsAsyncOps {
402
+ // Get the object for this function declaration
403
+ if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
404
+ v.analysis.AsyncFuncs[obj] = true
405
+ }
406
+ isAsync = true
407
+ }
408
+ }
409
+ v.analysis.IsInAsyncFunctionMap[n] = isAsync
410
+
411
+ // Set current receiver if this is a method
412
+ originalReceiver := v.currentReceiver
413
+ v.currentReceiver = nil // Reset for current function
414
+ if n.Recv != nil && len(n.Recv.List) > 0 {
415
+ // Assuming a single receiver for simplicity for now
416
+ if len(n.Recv.List[0].Names) > 0 {
417
+ if ident := n.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
418
+ if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
419
+ if vr, ok := def.(*types.Var); ok {
420
+ v.currentReceiver = vr
421
+ // Add the receiver variable to the VariableUsage map
422
+ // to ensure it is properly analyzed for boxing
423
+ v.getOrCreateUsageInfo(v.currentReceiver)
424
+ }
425
+ }
426
+ }
427
+ }
428
+ }
429
+
430
+ // Save original states to restore after visiting
431
+ originalInAsync := v.inAsyncFunction
432
+ originalFuncObj := v.currentFuncObj
433
+
434
+ // Update visitor state for this function
435
+ v.inAsyncFunction = isAsync
436
+ v.currentFuncObj = v.pkg.TypesInfo.ObjectOf(n.Name)
437
+ v.analysis.IsInAsyncFunctionMap[n] = isAsync // Ensure FuncDecl node itself is marked
438
+
439
+ // Check if the body contains any defer statements
440
+ if n.Body != nil && v.containsDefer(n.Body) {
441
+ v.analysis.NeedsDeferMap[n.Body] = true
442
+ }
443
+
444
+ // Visit the body with updated state
445
+ ast.Walk(v, n.Body)
446
+
447
+ // Restore states after visiting
448
+ defer func() {
449
+ v.currentFuncName = ""
450
+ v.inAsyncFunction = originalInAsync
451
+ v.currentReceiver = originalReceiver
452
+ v.currentFuncObj = originalFuncObj
453
+ }()
454
+ return nil // Stop traversal here, ast.Walk handled the body
455
+
456
+ case *ast.FuncLit:
457
+ // Determine if this function literal is async based on its body
458
+ isAsync := v.containsAsyncOperations(n.Body)
459
+ v.analysis.IsInAsyncFunctionMap[n] = isAsync
460
+
461
+ // Save original inAsyncFunction state to restore after visiting
462
+ originalInAsync := v.inAsyncFunction
463
+ v.inAsyncFunction = isAsync
464
+
465
+ // Check if the body contains any defer statements
466
+ if n.Body != nil && v.containsDefer(n.Body) {
467
+ v.analysis.NeedsDeferMap[n.Body] = true
468
+ }
469
+
470
+ // Visit the body with updated state
471
+ ast.Walk(v, n.Body)
472
+
473
+ // Restore inAsyncFunction state after visiting
474
+ v.inAsyncFunction = originalInAsync
475
+ return nil // Stop traversal here, ast.Walk handled the body
476
+
477
+ case *ast.BlockStmt:
478
+ // Check for defer statements in this block
479
+ if v.containsDefer(n) {
480
+ v.analysis.NeedsDeferMap[n] = true
481
+ }
482
+
483
+ // Store async state for this block
484
+ v.analysis.IsInAsyncFunctionMap[n] = v.inAsyncFunction
485
+
486
+ return v
487
+
488
+ case *ast.UnaryExpr:
489
+ // We handle address-of (&) within AssignStmt where it's actually used.
490
+ // Standalone &x doesn't directly assign, but its usage in assignments
491
+ // or function calls determines boxing. Assignments are handled below.
492
+ // Function calls like foo(&x) would require different tracking if needed.
493
+ // For now, we focus on assignments as per the request.
494
+ return v
495
+
496
+ case *ast.CallExpr:
497
+ // Check if this is a function call that might be async
498
+ if funcIdent, ok := n.Fun.(*ast.Ident); ok {
499
+ // Get the object for this function call
500
+ if obj := v.pkg.TypesInfo.Uses[funcIdent]; obj != nil && v.analysis.IsAsyncFunc(obj) {
501
+ // We're calling an async function, so mark current function as async if we're in one
502
+ if v.currentFuncObj != nil {
503
+ v.analysis.AsyncFuncs[v.currentFuncObj] = true
504
+ v.inAsyncFunction = true // Update visitor state
505
+ // Mark the FuncDecl node itself if possible (might need to store the node too)
506
+ for nodeAst := range v.analysis.IsInAsyncFunctionMap { // Find the node to update
507
+ if fd, ok := nodeAst.(*ast.FuncDecl); ok && v.pkg.TypesInfo.ObjectOf(fd.Name) == v.currentFuncObj {
508
+ v.analysis.IsInAsyncFunctionMap[nodeAst] = true
509
+ }
510
+ }
511
+ }
512
+ }
513
+ }
514
+
515
+ // Store async state for this call expression
516
+ v.analysis.IsInAsyncFunctionMap[n] = v.inAsyncFunction
517
+
518
+ return v
519
+
520
+ case *ast.SelectorExpr:
521
+ // No need to track private field access since all fields are public
522
+ return v
523
+
524
+ case *ast.AssignStmt:
525
+ for i, currentLHSExpr := range n.Lhs {
526
+ if i >= len(n.Rhs) {
527
+ break // Should not happen in valid Go
528
+ }
529
+ currentRHSExpr := n.Rhs[i]
530
+
531
+ // --- Analyze RHS to determine assignment type and source object (if any) ---
532
+ rhsAssignmentType := DirectAssignment
533
+ var rhsSourceObj types.Object // The variable object on the RHS (e.g., 'y' in x = y or x = &y)
534
+
535
+ if unaryExpr, ok := currentRHSExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
536
+ // RHS is &some_expr
537
+ rhsAssignmentType = AddressOfAssignment
538
+ if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
539
+ // RHS is &variable
540
+ rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
541
+ }
542
+ // If RHS is &structLit{} or &array[0], rhsSourceObj remains nil.
543
+ // _, ok := unaryExpr.X.(*ast.CompositeLit); ok
544
+ } else if rhsIdent, ok := currentRHSExpr.(*ast.Ident); ok {
545
+ // RHS is variable
546
+ rhsAssignmentType = DirectAssignment
547
+ rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
548
+ }
549
+ // If RHS is a literal, function call, etc., rhsSourceObj remains nil.
550
+
551
+ // --- Determine the LHS object (if it's a simple variable or a known field) ---
552
+ var lhsTrackedObj types.Object // The object on the LHS we might record info *for* (e.g. its sources)
553
+
554
+ if lhsIdent, ok := currentLHSExpr.(*ast.Ident); ok {
555
+ if lhsIdent.Name == "_" {
556
+ continue // Skip blank identifier assignments
557
+ }
558
+ lhsTrackedObj = v.pkg.TypesInfo.ObjectOf(lhsIdent)
559
+ } else if selExpr, ok := currentLHSExpr.(*ast.SelectorExpr); ok {
560
+ // LHS is struct.field or package.Var
561
+ if selection := v.pkg.TypesInfo.Selections[selExpr]; selection != nil {
562
+ lhsTrackedObj = selection.Obj() // This is the field or selected var object
563
+ }
564
+ } else if _, ok := currentLHSExpr.(*ast.StarExpr); ok {
565
+ // LHS is *pointer.
566
+ // We don't try to get a types.Object for the dereferenced entity itself to store in VariableUsage.
567
+ // lhsTrackedObj remains nil. The effect on rhsSourceObj (if its address is taken) is handled below.
568
+ }
569
+ // For other complex LHS (e.g., map_expr[key_expr]), lhsTrackedObj remains nil.
570
+
571
+ // --- Record Usage Information ---
572
+
573
+ // 1. If LHS is a trackable variable/field, record what's assigned to it (its sources).
574
+ // We only want to create VariableUsage entries for actual variables/fields.
575
+ if _, isVar := lhsTrackedObj.(*types.Var); isVar {
576
+ lhsUsageInfo := v.getOrCreateUsageInfo(lhsTrackedObj)
577
+ if rhsSourceObj != nil {
578
+ // Case: var1 = var2 OR var1 = &var2 OR field1 = var2 OR field1 = &var2
579
+ lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
580
+ Object: rhsSourceObj,
581
+ Type: rhsAssignmentType,
582
+ })
583
+ } else if rhsAssignmentType == AddressOfAssignment {
584
+ // Case: var1 = &non_ident_expr (e.g., &T{}) OR field1 = &non_ident_expr
585
+ // lhsTrackedObj is assigned an address, but not of a named variable.
586
+ lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
587
+ Object: nil, // No specific source variable object
588
+ Type: rhsAssignmentType,
589
+ })
590
+ }
591
+ // If rhsSourceObj is nil and rhsAssignmentType is DirectAssignment (e.g. var1 = 10),
592
+ // no source object to record for LHS sources.
593
+ }
594
+
595
+ // 2. If RHS involved a source variable (rhsSourceObj is not nil),
596
+ // record that this source variable was used (its destinations).
597
+ // This is CRITICAL for boxing analysis (e.g., if &rhsSourceObj was assigned).
598
+ if rhsSourceObj != nil {
599
+ sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
600
+ // The 'Object' in DestinationInfo is what/where rhsSourceObj (or its address) was assigned TO.
601
+ // This can be lhsTrackedObj (if LHS was an ident or field).
602
+ // If LHS was complex (e.g., *ptr, map[k]), lhsTrackedObj might be nil for that DestinationInfo.Object.
603
+ // Even if lhsTrackedObj is nil for the DestinationInfo.Object, if rhsAssignmentType is AddressOfAssignment,
604
+ // it's important to record that rhsSourceObj's address was taken.
605
+ sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
606
+ Object: lhsTrackedObj, // This can be nil if LHS is complex (*p, map[k])
607
+ Type: rhsAssignmentType,
608
+ })
609
+ }
610
+ }
611
+ return v // Continue traversal
612
+
613
+ case *ast.CompositeLit:
614
+ // No need to track private field access in composite literals since all fields are public
615
+ return v
616
+
617
+ default:
618
+ // For all other nodes, continue traversal
619
+ return v
620
+ }
621
+ }
622
+
623
+ // containsAsyncOperations checks if a node contains any async operations like channel operations.
624
+ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
625
+ var hasAsync bool
626
+
627
+ ast.Inspect(node, func(n ast.Node) bool {
628
+ if n == nil {
629
+ return false
630
+ }
631
+
632
+ switch s := n.(type) {
633
+ case *ast.SendStmt:
634
+ // Channel send operation (ch <- value)
635
+ hasAsync = true
636
+ return false
637
+
638
+ case *ast.UnaryExpr:
639
+ // Channel receive operation (<-ch)
640
+ if s.Op == token.ARROW {
641
+ hasAsync = true
642
+ return false
643
+ }
644
+
645
+ case *ast.CallExpr:
646
+ // Check if we're calling a function known to be async
647
+ if funcIdent, ok := s.Fun.(*ast.Ident); ok {
648
+ // Get the object for this function call
649
+ if obj := v.pkg.TypesInfo.Uses[funcIdent]; obj != nil && v.analysis.IsAsyncFunc(obj) {
650
+ hasAsync = true
651
+ return false
652
+ }
653
+ }
654
+
655
+ // TODO: Add detection of method calls on async types
656
+ }
657
+
658
+ return true
659
+ })
660
+
661
+ return hasAsync
662
+ }
663
+
664
+ // containsDefer checks if a block contains any defer statements.
665
+ func (v *analysisVisitor) containsDefer(block *ast.BlockStmt) bool {
666
+ hasDefer := false
667
+
668
+ ast.Inspect(block, func(n ast.Node) bool {
669
+ if _, ok := n.(*ast.DeferStmt); ok {
670
+ hasDefer = true
671
+ return false
672
+ }
673
+ return true
674
+ })
675
+
676
+ return hasDefer
677
+ }
678
+
679
+ // AnalyzeFile analyzes a Go source file AST and populates the Analysis struct with information
680
+ // that will be used during code generation to properly handle pointers, variables that need boxing, etc.
681
+ func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap ast.CommentMap) {
682
+ // Store the comment map in the analysis object
683
+ analysis.Cmap = cmap
684
+
685
+ // Process imports from the file
686
+ for _, imp := range file.Imports {
687
+ path := ""
688
+ if imp.Path != nil {
689
+ path = imp.Path.Value
690
+ // Remove quotes from the import path string
691
+ path = path[1 : len(path)-1]
692
+ }
693
+
694
+ // Store the import in the analysis
695
+ if path != "" {
696
+ name := ""
697
+ if imp.Name != nil {
698
+ name = imp.Name.Name
699
+ }
700
+
701
+ fileImp := &fileImport{
702
+ importPath: path,
703
+ importVars: make(map[string]struct{}),
704
+ }
705
+
706
+ // Use the import name or path as the key
707
+ key := path
708
+ if name != "" {
709
+ key = name
710
+ }
711
+
712
+ analysis.Imports[key] = fileImp
713
+ }
714
+ }
715
+
716
+ // Create an analysis visitor to traverse the AST
717
+ visitor := &analysisVisitor{
718
+ analysis: analysis,
719
+ pkg: pkg,
720
+ inAsyncFunction: false,
721
+ currentReceiver: nil, // Initialize currentReceiver
722
+ }
723
+
724
+ // Walk the AST with our visitor
725
+ ast.Walk(visitor, file)
726
+ }