goscript 0.0.83 → 0.1.0

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 (197) hide show
  1. package/README.md +13 -1
  2. package/cmd/goscript/cmd_compile.go +70 -69
  3. package/cmd/goscript/cmd_compile_test.go +79 -0
  4. package/cmd/goscript/main.go +10 -5
  5. package/compiler/compile-request.go +218 -0
  6. package/compiler/compiler.go +16 -1336
  7. package/compiler/compliance_test.go +196 -0
  8. package/compiler/config.go +6 -13
  9. package/compiler/diagnostic.go +70 -0
  10. package/compiler/index.test.ts +28 -28
  11. package/compiler/index.ts +40 -72
  12. package/compiler/lowered-program.go +132 -0
  13. package/compiler/lowering.go +3576 -0
  14. package/compiler/override-registry.go +422 -0
  15. package/compiler/override-registry_test.go +207 -0
  16. package/compiler/package-graph.go +231 -0
  17. package/compiler/package-graph_test.go +281 -0
  18. package/compiler/result.go +13 -0
  19. package/compiler/runtime-contract.go +279 -0
  20. package/compiler/runtime-contract_test.go +90 -0
  21. package/compiler/semantic-model-types.go +110 -0
  22. package/compiler/semantic-model.go +922 -0
  23. package/compiler/semantic-model_test.go +416 -0
  24. package/compiler/service.go +133 -0
  25. package/compiler/skeleton_test.go +1145 -0
  26. package/compiler/typescript-emitter.go +663 -0
  27. package/compiler/wasm/compile.go +2 -3
  28. package/compiler/wasm/compile_test.go +29 -0
  29. package/compiler/wasm_api.go +10 -159
  30. package/dist/compiler/index.d.ts +1 -3
  31. package/dist/compiler/index.js +31 -55
  32. package/dist/compiler/index.js.map +1 -1
  33. package/dist/gs/builtin/builtin.d.ts +13 -0
  34. package/dist/gs/builtin/builtin.js +27 -7
  35. package/dist/gs/builtin/builtin.js.map +1 -1
  36. package/dist/gs/builtin/channel.d.ts +3 -3
  37. package/dist/gs/builtin/channel.js.map +1 -1
  38. package/dist/gs/builtin/hostio.d.ts +86 -0
  39. package/dist/gs/builtin/hostio.js +266 -0
  40. package/dist/gs/builtin/hostio.js.map +1 -0
  41. package/dist/gs/builtin/index.d.ts +1 -0
  42. package/dist/gs/builtin/index.js +1 -0
  43. package/dist/gs/builtin/index.js.map +1 -1
  44. package/dist/gs/builtin/print.d.ts +8 -0
  45. package/dist/gs/builtin/print.js +111 -0
  46. package/dist/gs/builtin/print.js.map +1 -0
  47. package/dist/gs/builtin/slice.d.ts +1 -1
  48. package/dist/gs/builtin/slice.js.map +1 -1
  49. package/dist/gs/builtin/type.d.ts +11 -0
  50. package/dist/gs/builtin/type.js +55 -1
  51. package/dist/gs/builtin/type.js.map +1 -1
  52. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  53. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  54. package/dist/gs/bytes/reader.gs.js.map +1 -1
  55. package/dist/gs/context/context.js.map +1 -1
  56. package/dist/gs/crypto/rand/index.d.ts +5 -0
  57. package/dist/gs/crypto/rand/index.js +77 -0
  58. package/dist/gs/crypto/rand/index.js.map +1 -0
  59. package/dist/gs/encoding/json/index.d.ts +3 -0
  60. package/dist/gs/encoding/json/index.js +160 -0
  61. package/dist/gs/encoding/json/index.js.map +1 -0
  62. package/dist/gs/fmt/fmt.js +2 -22
  63. package/dist/gs/fmt/fmt.js.map +1 -1
  64. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  65. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  66. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  67. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  68. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  69. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  70. package/dist/gs/go/scanner/index.d.ts +29 -0
  71. package/dist/gs/go/scanner/index.js +120 -0
  72. package/dist/gs/go/scanner/index.js.map +1 -0
  73. package/dist/gs/go/token/index.d.ts +31 -0
  74. package/dist/gs/go/token/index.js +82 -0
  75. package/dist/gs/go/token/index.js.map +1 -0
  76. package/dist/gs/internal/abi/index.js.map +1 -1
  77. package/dist/gs/io/fs/fs.js.map +1 -1
  78. package/dist/gs/io/fs/readdir.js.map +1 -1
  79. package/dist/gs/io/fs/readfile.js.map +1 -1
  80. package/dist/gs/io/fs/stat.js.map +1 -1
  81. package/dist/gs/io/fs/sub.js.map +1 -1
  82. package/dist/gs/io/io.js.map +1 -1
  83. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  84. package/dist/gs/os/error.gs.js +2 -4
  85. package/dist/gs/os/error.gs.js.map +1 -1
  86. package/dist/gs/os/exec.gs.js.map +1 -1
  87. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  88. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  89. package/dist/gs/os/root_js.gs.js.map +1 -1
  90. package/dist/gs/os/tempfile.gs.js +66 -9
  91. package/dist/gs/os/tempfile.gs.js.map +1 -1
  92. package/dist/gs/os/types.gs.js.map +1 -1
  93. package/dist/gs/os/types_js.gs.d.ts +2 -51
  94. package/dist/gs/os/types_js.gs.js +67 -105
  95. package/dist/gs/os/types_js.gs.js.map +1 -1
  96. package/dist/gs/os/types_unix.gs.js.map +1 -1
  97. package/dist/gs/path/filepath/match.js.map +1 -1
  98. package/dist/gs/path/match.js.map +1 -1
  99. package/dist/gs/path/path.js.map +1 -1
  100. package/dist/gs/reflect/index.d.ts +2 -2
  101. package/dist/gs/reflect/index.js +1 -1
  102. package/dist/gs/reflect/index.js.map +1 -1
  103. package/dist/gs/reflect/map.js.map +1 -1
  104. package/dist/gs/reflect/type.d.ts +2 -1
  105. package/dist/gs/reflect/type.js +85 -14
  106. package/dist/gs/reflect/type.js.map +1 -1
  107. package/dist/gs/reflect/types.js.map +1 -1
  108. package/dist/gs/reflect/visiblefields.js.map +1 -1
  109. package/dist/gs/runtime/runtime.js.map +1 -1
  110. package/dist/gs/sort/sort.gs.js.map +1 -1
  111. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  112. package/dist/gs/strconv/quote.gs.js.map +1 -1
  113. package/dist/gs/strings/builder.js.map +1 -1
  114. package/dist/gs/strings/reader.js.map +1 -1
  115. package/dist/gs/strings/replace.js.map +1 -1
  116. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  117. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  118. package/dist/gs/sync/sync.d.ts +1 -0
  119. package/dist/gs/sync/sync.js +12 -0
  120. package/dist/gs/sync/sync.js.map +1 -1
  121. package/dist/gs/time/time.js.map +1 -1
  122. package/dist/gs/unicode/unicode.js.map +1 -1
  123. package/go.mod +2 -2
  124. package/gs/builtin/builtin.ts +31 -6
  125. package/gs/builtin/hostio.test.ts +246 -0
  126. package/gs/builtin/hostio.ts +413 -0
  127. package/gs/builtin/index.ts +1 -0
  128. package/gs/builtin/print.test.ts +48 -0
  129. package/gs/builtin/print.ts +154 -0
  130. package/gs/builtin/runtime-contract.test.ts +230 -0
  131. package/gs/builtin/type.ts +84 -1
  132. package/gs/crypto/rand/index.test.ts +32 -0
  133. package/gs/crypto/rand/index.ts +90 -0
  134. package/gs/crypto/rand/meta.json +5 -0
  135. package/gs/encoding/json/index.test.ts +65 -0
  136. package/gs/encoding/json/index.ts +186 -0
  137. package/gs/fmt/fmt.test.ts +41 -30
  138. package/gs/fmt/fmt.ts +2 -22
  139. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  140. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  141. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  142. package/gs/go/scanner/index.test.ts +50 -0
  143. package/gs/go/scanner/index.ts +157 -0
  144. package/gs/go/token/index.test.ts +21 -0
  145. package/gs/go/token/index.ts +120 -0
  146. package/gs/os/file_unix_js.test.ts +103 -0
  147. package/gs/os/meta.json +1 -2
  148. package/gs/os/tempfile.gs.test.ts +85 -0
  149. package/gs/os/tempfile.gs.ts +71 -11
  150. package/gs/os/types_js.gs.ts +74 -153
  151. package/gs/reflect/index.ts +1 -1
  152. package/gs/reflect/type.ts +106 -17
  153. package/gs/reflect/typefor.test.ts +75 -0
  154. package/gs/sync/sync.test.ts +24 -0
  155. package/gs/sync/sync.ts +12 -0
  156. package/package.json +13 -13
  157. package/compiler/analysis.go +0 -3475
  158. package/compiler/analysis_test.go +0 -338
  159. package/compiler/assignment.go +0 -580
  160. package/compiler/builtin_test.go +0 -92
  161. package/compiler/code-writer.go +0 -115
  162. package/compiler/compiler_test.go +0 -149
  163. package/compiler/composite-lit.go +0 -779
  164. package/compiler/config_test.go +0 -62
  165. package/compiler/constraint.go +0 -86
  166. package/compiler/decl.go +0 -801
  167. package/compiler/expr-call-async.go +0 -188
  168. package/compiler/expr-call-builtins.go +0 -208
  169. package/compiler/expr-call-helpers.go +0 -382
  170. package/compiler/expr-call-make.go +0 -318
  171. package/compiler/expr-call-type-conversion.go +0 -520
  172. package/compiler/expr-call.go +0 -413
  173. package/compiler/expr-selector.go +0 -343
  174. package/compiler/expr-star.go +0 -82
  175. package/compiler/expr-type.go +0 -442
  176. package/compiler/expr-value.go +0 -89
  177. package/compiler/expr.go +0 -773
  178. package/compiler/field.go +0 -183
  179. package/compiler/gs_dependencies_test.go +0 -298
  180. package/compiler/lit.go +0 -322
  181. package/compiler/output.go +0 -72
  182. package/compiler/primitive.go +0 -149
  183. package/compiler/protobuf.go +0 -697
  184. package/compiler/sanitize.go +0 -100
  185. package/compiler/spec-struct.go +0 -995
  186. package/compiler/spec-value.go +0 -540
  187. package/compiler/spec.go +0 -725
  188. package/compiler/stmt-assign.go +0 -664
  189. package/compiler/stmt-for.go +0 -266
  190. package/compiler/stmt-range.go +0 -475
  191. package/compiler/stmt-select.go +0 -262
  192. package/compiler/stmt-type-switch.go +0 -147
  193. package/compiler/stmt.go +0 -1308
  194. package/compiler/type-assert.go +0 -386
  195. package/compiler/type-info.go +0 -156
  196. package/compiler/type-utils.go +0 -207
  197. package/compiler/type.go +0 -892
@@ -1,580 +0,0 @@
1
- package compiler
2
-
3
- import (
4
- "fmt"
5
- "go/ast"
6
- "go/token"
7
- "go/types"
8
- )
9
-
10
- // writeAssignmentCore handles the central logic for translating Go assignment
11
- // operations (LHS op RHS) into TypeScript. It's called by `WriteStmtAssign`
12
- // and other functions that need to generate assignment code.
13
- //
14
- // Key behaviors:
15
- // - Multi-variable assignment (e.g., `a, b = b, a`): Translates to TypeScript
16
- // array destructuring: `[a_ts, b_ts] = [b_ts, a_ts]`. It correctly handles
17
- // non-null assertions for array index expressions on both LHS and RHS if
18
- // all expressions involved are index expressions (common in swaps).
19
- // - Single-variable assignment to a map index (`myMap[key] = value`): Translates
20
- // to `$.mapSet(myMap_ts, key_ts, value_ts)`.
21
- // - Other single-variable assignments (`variable = value`):
22
- // - The LHS expression is written (caller typically ensures `.value` is appended
23
- // if assigning to a VarRefed variable's content).
24
- // - The Go assignment token (`tok`, e.g., `=`, `+=`) is translated to its
25
- // TypeScript equivalent using `TokenToTs`.
26
- // - The RHS expression(s) are written. If `shouldApplyClone` indicates the RHS
27
- // is a struct value, `.clone()` is appended to the translated RHS to emulate
28
- // Go's value semantics for struct assignment.
29
- // - Blank identifiers (`_`) on the LHS are handled by omitting them in TypeScript
30
- // destructuring patterns or by skipping the assignment for single assignments.
31
- //
32
- // This function handles all assignment types including:
33
- // - Pointer dereference assignments (*p = v)
34
- // - Blank identifier assignments (_ = v)
35
- func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Token, addDeclaration bool) error {
36
- // Handle blank identifier (_) on the LHS for single assignments
37
- if len(lhs) == 1 && len(rhs) == 1 {
38
- if ident, ok := lhs[0].(*ast.Ident); ok && ident.Name == "_" {
39
- return c.writeBlankIdentifierAssign(rhs[0])
40
- }
41
-
42
- // Handle the special case of "*p = val" or "*p += val" (assignment to dereferenced pointer)
43
- if starExpr, ok := lhs[0].(*ast.StarExpr); ok {
44
- return c.writePointerDerefAssign(starExpr, rhs[0], tok)
45
- }
46
-
47
- // Handle variable referenced variables in declarations
48
- if addDeclaration && tok == token.DEFINE {
49
- if handled, err := c.writeVarRefShortDecl(lhs[0], rhs[0]); err != nil {
50
- return err
51
- } else if handled {
52
- return nil
53
- }
54
-
55
- c.tsw.WriteLiterally("let ")
56
- }
57
- }
58
-
59
- // Special case for multi-variable assignment to handle array element swaps
60
- if len(lhs) > 1 && len(rhs) > 1 {
61
- return c.writeMultiVarAssign(lhs, rhs, tok)
62
- }
63
-
64
- // --- Logic for assignments ---
65
- isMapIndexLHS := false // Track if the first LHS is a map index
66
- for i, l := range lhs {
67
- if i != 0 {
68
- c.tsw.WriteLiterally(", ")
69
- }
70
-
71
- // Handle map indexing assignment specially
72
- // Note: We don't use WriteIndexExpr here because we need to use $.mapSet instead of .get
73
- currentIsMapIndex := false
74
- if indexExpr, ok := l.(*ast.IndexExpr); ok {
75
- if tv, ok := c.pkg.TypesInfo.Types[indexExpr.X]; ok {
76
- // Check if it's a concrete map type
77
- if _, isMap := tv.Type.Underlying().(*types.Map); isMap {
78
- currentIsMapIndex = true
79
- if i == 0 {
80
- isMapIndexLHS = true
81
- }
82
- // Use mapSet helper
83
- c.tsw.WriteLiterally("$.mapSet(")
84
- if err := c.WriteValueExpr(indexExpr.X); err != nil { // Map
85
- return err
86
- }
87
- c.tsw.WriteLiterally(", ")
88
- if err := c.WriteValueExpr(indexExpr.Index); err != nil { // Key
89
- return err
90
- }
91
- c.tsw.WriteLiterally(", ")
92
- // Value will be added after operator and RHS
93
- } else if typeParam, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
94
- // Check if the type parameter is constrained to be a map type
95
- constraint := typeParam.Constraint()
96
- if constraint != nil {
97
- underlying := constraint.Underlying()
98
- if iface, isInterface := underlying.(*types.Interface); isInterface {
99
- if hasMapConstraint(iface) {
100
- currentIsMapIndex = true
101
- if i == 0 {
102
- isMapIndexLHS = true
103
- }
104
- // Use mapSet helper for type parameter constrained to map
105
- c.tsw.WriteLiterally("$.mapSet(")
106
- if err := c.WriteValueExpr(indexExpr.X); err != nil { // Map
107
- return err
108
- }
109
- c.tsw.WriteLiterally(", ")
110
- if err := c.WriteValueExpr(indexExpr.Index); err != nil { // Key
111
- return err
112
- }
113
- c.tsw.WriteLiterally(", ")
114
- // Value will be added after operator and RHS
115
- }
116
- }
117
- }
118
- }
119
- }
120
- }
121
-
122
- if !currentIsMapIndex {
123
- // For single assignments, handle variable referenced variables specially
124
- if len(lhs) == 1 && len(rhs) == 1 {
125
- lhsExprIdent, lhsExprIsIdent := l.(*ast.Ident)
126
- if lhsExprIsIdent {
127
- // Determine if LHS is variable referenced
128
- isLHSVarRefed := false
129
- var lhsObj types.Object
130
- // Get the types.Object from the identifier
131
- lhsObj = c.objectOfIdent(lhsExprIdent)
132
-
133
- // Check if this variable needs to be variable referenced
134
- if lhsObj != nil && c.analysis.NeedsVarRef(lhsObj) {
135
- isLHSVarRefed = true
136
- }
137
-
138
- // prevent writing .value unless lhs is variable referenced
139
- c.WriteIdent(lhsExprIdent, isLHSVarRefed)
140
- continue
141
- }
142
- }
143
-
144
- // Write the LHS expression normally
145
- if err := c.WriteValueExpr(l); err != nil {
146
- return err
147
- }
148
- }
149
- }
150
-
151
- // Check for integer division compound assignment (i /= n)
152
- // Go's integer division truncates, but JavaScript's /= doesn't
153
- // We need to convert `i /= n` to `i = Math.trunc(i / n)`
154
- isIntegerDivisionAssign := false
155
- if tok == token.QUO_ASSIGN && len(lhs) == 1 && len(rhs) == 1 {
156
- if lhsType := c.pkg.TypesInfo.TypeOf(lhs[0]); lhsType != nil {
157
- if basic, ok := lhsType.Underlying().(*types.Basic); ok {
158
- if basic.Info()&types.IsInteger != 0 {
159
- isIntegerDivisionAssign = true
160
- }
161
- }
162
- }
163
- }
164
-
165
- // Only write the assignment operator for regular variables, not for map assignments handled by mapSet
166
- if isMapIndexLHS && len(lhs) == 1 { // Only skip operator if it's a single map assignment
167
- // Continue, we've already written part of the mapSet() function call
168
- } else if isIntegerDivisionAssign {
169
- // For integer division compound assignment, convert `i /= n` to `i = Math.trunc(i / n)`
170
- c.tsw.WriteLiterally(" = Math.trunc(")
171
- // Write LHS again as first operand of division
172
- if err := c.WriteValueExpr(lhs[0]); err != nil {
173
- return err
174
- }
175
- c.tsw.WriteLiterally(" / ")
176
- // RHS will be written below, then we close the parenthesis
177
- } else {
178
- c.tsw.WriteLiterally(" ")
179
- if err := c.writeAssignmentOperator(tok); err != nil {
180
- return err
181
- }
182
- }
183
-
184
- // Write RHS
185
- for i, r := range rhs {
186
- if i != 0 {
187
- c.tsw.WriteLiterally(", ")
188
- }
189
-
190
- // Check if we need to access a variable referenced source value and apply clone
191
- // For struct value assignments, we need to handle:
192
- // 1. UnVarRefed source, unVarRefed target: source.clone()
193
- // 2. Variable referenced source, unVarRefed target: source.value.clone()
194
- // 3. UnVarRefed source, variable referenced target: $.varRef(source)
195
- // 4. Variable referenced source, variable referenced target: source (straight assignment of the variable reference)
196
-
197
- // Determine if RHS is a variable referenced variable (could be a struct or other variable)
198
- needsVarRefedAccessRHS := false
199
- var rhsObj types.Object
200
-
201
- // Check if RHS is an identifier (variable name)
202
- rhsIdent, rhsIsIdent := r.(*ast.Ident)
203
- if rhsIsIdent {
204
- rhsObj = c.objectOfIdent(rhsIdent)
205
-
206
- // Important: For struct copying, we need to check if the variable itself is variable referenced
207
- // Important: For struct copying, we need to check if the variable needs variable referenced access
208
- // This is more comprehensive than just checking if it's variable referenced
209
- if rhsObj != nil {
210
- needsVarRefedAccessRHS = c.analysis.NeedsVarRefAccess(rhsObj)
211
- }
212
- }
213
-
214
- // Check for pointer-to-pointer assignment
215
- if rhsIsIdent && rhsObj != nil && len(lhs) == 1 {
216
- lhsType := c.pkg.TypesInfo.TypeOf(lhs[0])
217
- rhsType := rhsObj.Type()
218
-
219
- if lhsType != nil && rhsType != nil {
220
- // Check if both LHS and RHS are pointer types
221
- if _, lhsIsPtr := lhsType.(*types.Pointer); lhsIsPtr {
222
- if _, rhsIsPtr := rhsType.(*types.Pointer); rhsIsPtr {
223
- // This is pointer-to-pointer assignment
224
- // The key question: is the RHS variable itself varref'd?
225
- // - If RHS is varref'd (like pp1), use .value to get the actual pointer
226
- // - If RHS is not varref'd (like p1), use the variable directly
227
-
228
- if c.analysis.NeedsVarRef(rhsObj) {
229
- // RHS variable is varref'd, so we need its .value to get the actual pointer
230
- c.WriteIdent(rhsIdent, true) // Add .value access
231
- } else {
232
- // RHS variable is not varref'd, so it directly holds the pointer
233
- c.WriteIdent(rhsIdent, false) // No .value access
234
- }
235
- continue
236
- }
237
- }
238
- }
239
- }
240
-
241
- // Handle different cases for struct cloning
242
- if shouldApplyClone(c.pkg, r) {
243
- // When cloning for value assignment, mark the result as struct value
244
- c.tsw.WriteLiterally("$.markAsStructValue(")
245
-
246
- // Check if RHS is an async call - if so, wrap in parentheses so .clone() binds correctly
247
- // Example: (await asyncFunc()).clone() instead of await asyncFunc().clone()
248
- needsParensForAsync := false
249
- if callExpr, isCall := r.(*ast.CallExpr); isCall && c.isCallExprAsync(callExpr) {
250
- needsParensForAsync = true
251
- c.tsw.WriteLiterally("(")
252
- }
253
-
254
- // For other expressions, we need to handle variable referenced access differently
255
- if _, isIdent := r.(*ast.Ident); isIdent {
256
- // For identifiers, WriteValueExpr already adds .value if needed
257
- if err := c.WriteValueExpr(r); err != nil {
258
- return err
259
- }
260
- } else {
261
- // For non-identifiers, write the expression and add .value if needed
262
- if err := c.WriteValueExpr(r); err != nil {
263
- return err
264
- }
265
- // Only add .value for non-identifiers that need variable referenced access
266
- if needsVarRefedAccessRHS {
267
- c.tsw.WriteLiterally(".value") // Access the variable referenced value
268
- }
269
- }
270
-
271
- if needsParensForAsync {
272
- c.tsw.WriteLiterally(")")
273
- }
274
- c.tsw.WriteLiterally(".clone())") // Always add clone for struct values
275
- } else {
276
- // Check if this is a pointer variable assignment to an interface type
277
- if rhsIsIdent && rhsObj != nil {
278
- // Check if LHS is interface type and RHS is a pointer variable
279
- if len(lhs) == 1 {
280
- lhsType := c.pkg.TypesInfo.TypeOf(lhs[0])
281
- rhsType := rhsObj.Type()
282
-
283
- if lhsType != nil && rhsType != nil {
284
- // Check if LHS is interface and RHS is pointer
285
- if _, isInterface := lhsType.Underlying().(*types.Interface); isInterface {
286
- if ptrType, isPtr := rhsType.(*types.Pointer); isPtr {
287
- // This is pointer-to-interface assignment
288
- // For pointer variables that point to varrefed values, write without .value
289
- // We want to pass the VarRef object itself to the interface, not its .value
290
- if c.analysis.NeedsVarRefAccess(rhsObj) {
291
- // Write the pointer variable without .value access
292
- c.WriteIdent(rhsIdent, false)
293
- continue
294
- }
295
-
296
- // Check if this is a struct pointer for the element type
297
- if _, isStruct := ptrType.Elem().Underlying().(*types.Struct); isStruct {
298
- // Struct pointer to interface - might need special handling
299
- // Continue to normal WriteValueExpr handling
300
- }
301
- }
302
- }
303
- }
304
- }
305
- }
306
-
307
- // Non-struct case: write RHS normally
308
- // Check if this is a primitive error type being assigned to an error interface
309
- if c.writePrimitiveErrorWrapperForAssign(lhs, r, i) {
310
- continue
311
- }
312
-
313
- if err := c.WriteValueExpr(r); err != nil { // RHS is a non-struct value
314
- return err
315
- }
316
- }
317
- }
318
-
319
- // Close the parenthesis for &^= transformation
320
- if tok == token.AND_NOT_ASSIGN && !(isMapIndexLHS && len(lhs) == 1) {
321
- c.tsw.WriteLiterally(")")
322
- }
323
-
324
- // Close the parenthesis for integer division compound assignment
325
- if isIntegerDivisionAssign {
326
- c.tsw.WriteLiterally(")")
327
- }
328
-
329
- // If the LHS was a single map index, close the mapSet call
330
- if isMapIndexLHS && len(lhs) == 1 {
331
- c.tsw.WriteLiterally(")")
332
- }
333
- return nil
334
- }
335
-
336
- // writePrimitiveErrorWrapperForAssign checks if an RHS value is a primitive type
337
- // that implements the error interface being assigned to an error-typed LHS,
338
- // and if so, wraps it with $.wrapPrimitiveError.
339
- // Returns true if the wrapper was written, false otherwise.
340
- func (c *GoToTSCompiler) writePrimitiveErrorWrapperForAssign(lhs []ast.Expr, rhs ast.Expr, rhsIndex int) bool {
341
- // Only handle single assignments for now
342
- if len(lhs) != 1 || rhsIndex != 0 {
343
- return false
344
- }
345
-
346
- // Get the LHS type
347
- lhsType := c.pkg.TypesInfo.TypeOf(lhs[0])
348
- if lhsType == nil {
349
- return false
350
- }
351
-
352
- // Check if the LHS type is the error interface
353
- if iface, ok := lhsType.Underlying().(*types.Interface); !ok || iface.String() != "interface{Error() string}" {
354
- return false
355
- }
356
-
357
- // Get the actual type of the RHS expression
358
- rhsType := c.pkg.TypesInfo.TypeOf(rhs)
359
- if rhsType == nil {
360
- return false
361
- }
362
-
363
- // Check if the RHS type is a wrapper type (named type with basic underlying type)
364
- if !c.isWrapperType(rhsType) {
365
- return false
366
- }
367
-
368
- // Check if the RHS type has an Error() method
369
- if !c.typeHasMethods(rhsType, "Error") {
370
- return false
371
- }
372
-
373
- // Get the qualified type name for the Error function
374
- typeName := c.getQualifiedTypeName(rhsType)
375
- if typeName == "" {
376
- return false
377
- }
378
-
379
- // Write: $.wrapPrimitiveError(value, TypeName_Error)
380
- c.tsw.WriteLiterally("$.wrapPrimitiveError(")
381
- if err := c.WriteValueExpr(rhs); err != nil {
382
- return false
383
- }
384
- c.tsw.WriteLiterally(", ")
385
- c.tsw.WriteLiterally(typeName)
386
- c.tsw.WriteLiterally("_Error)")
387
-
388
- return true
389
- }
390
-
391
- // writeBlankIdentifierAssign handles assignment to blank identifier (_)
392
- func (c *GoToTSCompiler) writeBlankIdentifierAssign(rhs ast.Expr) error {
393
- c.tsw.WriteLiterally("/* _ = */ ")
394
- return c.WriteValueExpr(rhs)
395
- }
396
-
397
- // writePointerDerefAssign handles assignments to dereferenced pointers (*p = val)
398
- func (c *GoToTSCompiler) writePointerDerefAssign(starExpr *ast.StarExpr, rhs ast.Expr, tok token.Token) error {
399
- // Check if RHS is a struct - if so, use $.assignStruct() to copy fields
400
- if shouldApplyClone(c.pkg, rhs) {
401
- // For struct assignments, use $.assignStruct(target, source)
402
- // This copies all fields from source to target
403
- c.tsw.WriteLiterally("$.assignStruct(")
404
-
405
- // Write the pointer as the target
406
- if ident, ok := starExpr.X.(*ast.Ident); ok {
407
- obj := c.objectOfIdent(ident)
408
- if obj != nil && c.analysis.NeedsVarRef(obj) {
409
- c.WriteIdent(ident, true)
410
- } else {
411
- c.WriteIdent(ident, false)
412
- }
413
- } else {
414
- if err := c.WriteValueExpr(starExpr.X); err != nil {
415
- return err
416
- }
417
- }
418
- c.tsw.WriteLiterally("!, ")
419
-
420
- // Write the RHS with clone as the source
421
- c.tsw.WriteLiterally("$.markAsStructValue(")
422
- if err := c.WriteValueExpr(rhs); err != nil {
423
- return err
424
- }
425
- c.tsw.WriteLiterally(".clone()))")
426
- return nil
427
- }
428
-
429
- // For non-struct types, use the original p!.value = val approach
430
- if ident, ok := starExpr.X.(*ast.Ident); ok {
431
- obj := c.objectOfIdent(ident)
432
- if obj != nil && c.analysis.NeedsVarRef(obj) {
433
- c.WriteIdent(ident, true)
434
- c.tsw.WriteLiterally("!.value")
435
- } else {
436
- c.WriteIdent(ident, false)
437
- c.tsw.WriteLiterally("!.value")
438
- }
439
- } else {
440
- if err := c.WriteValueExpr(starExpr.X); err != nil {
441
- return err
442
- }
443
- c.tsw.WriteLiterally("!.value")
444
- }
445
-
446
- // Write assignment operator
447
- if tok == token.AND_NOT_ASSIGN {
448
- c.tsw.WriteLiterally(" &= ~(")
449
- } else {
450
- c.tsw.WriteLiterally(" ")
451
- tokStr, ok := TokenToTs(tok)
452
- if !ok {
453
- return fmt.Errorf("unknown assignment token: %s", tok.String())
454
- }
455
- c.tsw.WriteLiterally(tokStr)
456
- c.tsw.WriteLiterally(" ")
457
- }
458
-
459
- // Write RHS
460
- if err := c.WriteValueExpr(rhs); err != nil {
461
- return err
462
- }
463
-
464
- if tok == token.AND_NOT_ASSIGN {
465
- c.tsw.WriteLiterally(")")
466
- }
467
-
468
- return nil
469
- }
470
-
471
- // writeVarRefShortDecl handles short declarations of varrefed variables
472
- // Returns true if handled, false otherwise
473
- func (c *GoToTSCompiler) writeVarRefShortDecl(lhs, rhs ast.Expr) (bool, error) {
474
- ident, ok := lhs.(*ast.Ident)
475
- if !ok {
476
- return false, nil
477
- }
478
-
479
- obj := c.objectOfIdent(ident)
480
- if obj == nil || !c.analysis.NeedsVarRef(obj) {
481
- return false, nil
482
- }
483
-
484
- c.tsw.WriteLiterally("let ")
485
- c.tsw.WriteLiterally(c.sanitizeIdentifier(ident.Name))
486
- c.tsw.WriteLiterally(" = $.varRef(")
487
- if err := c.WriteValueExpr(rhs); err != nil {
488
- return false, err
489
- }
490
- c.tsw.WriteLiterally(")")
491
- return true, nil
492
- }
493
-
494
- // writeMultiVarAssign handles multi-variable assignments with array destructuring
495
- func (c *GoToTSCompiler) writeMultiVarAssign(lhs, rhs []ast.Expr, tok token.Token) error {
496
- // Check if all expressions are index expressions (for swap optimization)
497
- allIndexExprs := true
498
- for _, expr := range append(lhs, rhs...) {
499
- if _, isIndexExpr := expr.(*ast.IndexExpr); !isIndexExpr {
500
- allIndexExprs = false
501
- break
502
- }
503
- }
504
-
505
- // Add semicolon to prevent TypeScript parsing issues
506
- if tok != token.DEFINE {
507
- c.tsw.WriteLiterally(";")
508
- }
509
-
510
- // Write LHS array destructuring pattern
511
- c.tsw.WriteLiterally("[")
512
- for i, l := range lhs {
513
- if i != 0 {
514
- c.tsw.WriteLiterally(", ")
515
- }
516
-
517
- if ident, ok := l.(*ast.Ident); ok && ident.Name == "_" {
518
- // Blank identifier - leave empty slot
519
- } else if indexExpr, ok := l.(*ast.IndexExpr); ok && allIndexExprs {
520
- if err := c.WriteValueExpr(indexExpr.X); err != nil {
521
- return err
522
- }
523
- c.tsw.WriteLiterally("![")
524
- if err := c.WriteValueExpr(indexExpr.Index); err != nil {
525
- return err
526
- }
527
- c.tsw.WriteLiterally("]")
528
- } else {
529
- if err := c.WriteValueExpr(l); err != nil {
530
- return err
531
- }
532
- }
533
- }
534
-
535
- // Write RHS array
536
- c.tsw.WriteLiterally("] = [")
537
- for i, r := range rhs {
538
- if i != 0 {
539
- c.tsw.WriteLiterally(", ")
540
- }
541
-
542
- if indexExpr, ok := r.(*ast.IndexExpr); ok && allIndexExprs {
543
- if err := c.WriteValueExpr(indexExpr.X); err != nil {
544
- return err
545
- }
546
- c.tsw.WriteLiterally("![")
547
- if err := c.WriteValueExpr(indexExpr.Index); err != nil {
548
- return err
549
- }
550
- c.tsw.WriteLiterally("]")
551
- } else if callExpr, isCallExpr := r.(*ast.CallExpr); isCallExpr {
552
- if err := c.WriteCallExpr(callExpr); err != nil {
553
- return err
554
- }
555
- } else {
556
- if err := c.WriteValueExpr(r); err != nil {
557
- return err
558
- }
559
- }
560
- }
561
- c.tsw.WriteLiterally("]")
562
-
563
- return nil
564
- }
565
-
566
- // writeAssignmentOperator writes the TypeScript assignment operator
567
- func (c *GoToTSCompiler) writeAssignmentOperator(tok token.Token) error {
568
- if tok == token.AND_NOT_ASSIGN {
569
- c.tsw.WriteLiterally("&= ~(")
570
- return nil
571
- }
572
-
573
- tokStr, ok := TokenToTs(tok)
574
- if !ok {
575
- return fmt.Errorf("unknown assignment token: %s", tok.String())
576
- }
577
- c.tsw.WriteLiterally(tokStr)
578
- c.tsw.WriteLiterally(" ")
579
- return nil
580
- }
@@ -1,92 +0,0 @@
1
- //go:build test_emit_builtin
2
-
3
- package compiler
4
-
5
- import (
6
- "context"
7
- "os"
8
- "path/filepath"
9
- "testing"
10
-
11
- "github.com/sirupsen/logrus"
12
- )
13
-
14
- func TestEmitBuiltinOption(t *testing.T) {
15
- // Create a temporary directory for the test output
16
- tempDir, err := os.MkdirTemp("", "goscript-test-emit-builtin")
17
- if err != nil {
18
- t.Fatalf("Failed to create temp dir: %v", err)
19
- }
20
- defer os.RemoveAll(tempDir)
21
-
22
- // Setup logger
23
- log := logrus.New()
24
- log.SetLevel(logrus.DebugLevel)
25
- le := logrus.NewEntry(log)
26
-
27
- // Case 1: DisableEmitBuiltin = true (default behavior in tests tests)
28
- t.Run("DisableEmitBuiltin=true", func(t *testing.T) {
29
- outputDir := filepath.Join(tempDir, "disabled")
30
- config := &Config{
31
- OutputPath: outputDir,
32
- AllDependencies: true,
33
- DisableEmitBuiltin: true,
34
- }
35
-
36
- compiler, err := NewCompiler(config, le, nil)
37
- if err != nil {
38
- t.Fatalf("Failed to create compiler: %v", err)
39
- }
40
-
41
- // Compile a package that depends on builtin (time)
42
- _, err = compiler.CompilePackages(context.Background(), "time")
43
- if err != nil {
44
- t.Fatalf("Compilation failed: %v", err)
45
- }
46
-
47
- // Check that handwritten packages like unsafe aren't emitted when DisableEmitBuiltin=true
48
- unsafePath := filepath.Join(outputDir, "@goscript/unsafe")
49
- if _, err := os.Stat(unsafePath); !os.IsNotExist(err) {
50
- t.Errorf("unsafe package was emitted when DisableEmitBuiltin=true")
51
- }
52
-
53
- // Also check for runtime package
54
- runtimePath := filepath.Join(outputDir, "@goscript/runtime")
55
- if _, err := os.Stat(runtimePath); !os.IsNotExist(err) {
56
- t.Errorf("runtime package was emitted when DisableEmitBuiltin=true")
57
- }
58
-
59
- // But time package should have been emitted
60
- timePath := filepath.Join(outputDir, "@goscript/time")
61
- if _, err := os.Stat(timePath); os.IsNotExist(err) {
62
- t.Errorf("time package was not emitted")
63
- }
64
- })
65
-
66
- // Case 2: DisableEmitBuiltin = false (new behavior for third-party projects)
67
- t.Run("DisableEmitBuiltin=false", func(t *testing.T) {
68
- outputDir := filepath.Join(tempDir, "enabled")
69
- config := &Config{
70
- OutputPath: outputDir,
71
- AllDependencies: true,
72
- DisableEmitBuiltin: false,
73
- }
74
-
75
- compiler, err := NewCompiler(config, le, nil)
76
- if err != nil {
77
- t.Fatalf("Failed to create compiler: %v", err)
78
- }
79
-
80
- // Compile a package that depends on builtin (time)
81
- _, err = compiler.CompilePackages(context.Background(), "time")
82
- if err != nil {
83
- t.Fatalf("Compilation failed: %v", err)
84
- }
85
-
86
- // Time package should also have been emitted
87
- timePath := filepath.Join(outputDir, "@goscript/time")
88
- if _, err := os.Stat(timePath); os.IsNotExist(err) {
89
- t.Errorf("time package was not emitted")
90
- }
91
- })
92
- }