goscript 0.0.23 → 0.0.25

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 (94) hide show
  1. package/README.md +2 -2
  2. package/cmd/goscript/cmd_compile.go +18 -2
  3. package/compiler/analysis.go +74 -132
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +37 -43
  6. package/compiler/builtin_test.go +90 -0
  7. package/compiler/compiler.go +307 -22
  8. package/compiler/composite-lit.go +108 -43
  9. package/compiler/config.go +7 -3
  10. package/compiler/config_test.go +6 -33
  11. package/compiler/decl.go +7 -1
  12. package/compiler/expr-call.go +212 -2
  13. package/compiler/expr-selector.go +66 -41
  14. package/compiler/expr-star.go +57 -65
  15. package/compiler/expr-type.go +1 -1
  16. package/compiler/expr-value.go +1 -1
  17. package/compiler/expr.go +125 -20
  18. package/compiler/field.go +4 -4
  19. package/compiler/primitive.go +11 -10
  20. package/compiler/spec-struct.go +3 -3
  21. package/compiler/spec-value.go +75 -29
  22. package/compiler/spec.go +9 -3
  23. package/compiler/stmt-assign.go +36 -2
  24. package/compiler/stmt-for.go +11 -0
  25. package/compiler/stmt-range.go +314 -1
  26. package/compiler/stmt.go +52 -0
  27. package/compiler/type.go +83 -15
  28. package/dist/gs/builtin/builtin.d.ts +9 -0
  29. package/dist/gs/builtin/builtin.js +46 -0
  30. package/dist/gs/builtin/builtin.js.map +1 -0
  31. package/dist/gs/builtin/channel.d.ts +193 -0
  32. package/dist/gs/builtin/channel.js +471 -0
  33. package/dist/gs/builtin/channel.js.map +1 -0
  34. package/dist/gs/builtin/defer.d.ts +38 -0
  35. package/dist/gs/builtin/defer.js +54 -0
  36. package/dist/gs/builtin/defer.js.map +1 -0
  37. package/dist/gs/builtin/index.d.ts +1 -0
  38. package/dist/gs/builtin/index.js +2 -0
  39. package/dist/gs/builtin/index.js.map +1 -0
  40. package/dist/gs/builtin/io.d.ts +16 -0
  41. package/dist/gs/builtin/io.js +15 -0
  42. package/dist/gs/builtin/io.js.map +1 -0
  43. package/dist/gs/builtin/map.d.ts +33 -0
  44. package/dist/gs/builtin/map.js +44 -0
  45. package/dist/gs/builtin/map.js.map +1 -0
  46. package/dist/gs/builtin/slice.d.ts +173 -0
  47. package/dist/gs/builtin/slice.js +799 -0
  48. package/dist/gs/builtin/slice.js.map +1 -0
  49. package/dist/gs/builtin/type.d.ts +203 -0
  50. package/dist/gs/builtin/type.js +744 -0
  51. package/dist/gs/builtin/type.js.map +1 -0
  52. package/dist/gs/builtin/varRef.d.ts +14 -0
  53. package/dist/gs/builtin/varRef.js +14 -0
  54. package/dist/gs/builtin/varRef.js.map +1 -0
  55. package/dist/gs/cmp/index.d.ts +4 -0
  56. package/dist/gs/cmp/index.js +27 -0
  57. package/dist/gs/cmp/index.js.map +1 -0
  58. package/dist/gs/context/context.d.ts +26 -0
  59. package/dist/gs/context/context.js +305 -0
  60. package/dist/gs/context/context.js.map +1 -0
  61. package/dist/gs/context/index.d.ts +1 -0
  62. package/dist/gs/context/index.js +2 -0
  63. package/dist/gs/context/index.js.map +1 -0
  64. package/dist/gs/internal/goarch/index.d.ts +6 -0
  65. package/dist/gs/internal/goarch/index.js +14 -0
  66. package/dist/gs/internal/goarch/index.js.map +1 -0
  67. package/dist/gs/iter/index.d.ts +1 -0
  68. package/dist/gs/iter/index.js +2 -0
  69. package/dist/gs/iter/index.js.map +1 -0
  70. package/dist/gs/iter/iter.d.ts +4 -0
  71. package/dist/gs/iter/iter.js +91 -0
  72. package/dist/gs/iter/iter.js.map +1 -0
  73. package/dist/gs/math/bits/index.d.ts +47 -0
  74. package/dist/gs/math/bits/index.js +298 -0
  75. package/dist/gs/math/bits/index.js.map +1 -0
  76. package/dist/gs/runtime/index.d.ts +1 -0
  77. package/dist/gs/runtime/index.js +2 -0
  78. package/dist/gs/runtime/index.js.map +1 -0
  79. package/dist/gs/runtime/runtime.d.ts +41 -0
  80. package/dist/gs/runtime/runtime.js +158 -0
  81. package/dist/gs/runtime/runtime.js.map +1 -0
  82. package/dist/gs/slices/index.d.ts +1 -0
  83. package/dist/gs/slices/index.js +2 -0
  84. package/dist/gs/slices/index.js.map +1 -0
  85. package/dist/gs/slices/slices.d.ts +8 -0
  86. package/dist/gs/slices/slices.js +20 -0
  87. package/dist/gs/slices/slices.js.map +1 -0
  88. package/dist/gs/time/index.d.ts +1 -0
  89. package/dist/gs/time/index.js +2 -0
  90. package/dist/gs/time/index.js.map +1 -0
  91. package/dist/gs/time/time.d.ts +57 -0
  92. package/dist/gs/time/time.js +208 -0
  93. package/dist/gs/time/time.js.map +1 -0
  94. package/package.json +3 -2
@@ -13,16 +13,16 @@ import (
13
13
  // access on an object or struct.
14
14
  // - For package selectors, it writes `PackageName.IdentifierName`. The `IdentifierName`
15
15
  // is written using `WriteIdent` which handles potential `.value` access if the
16
- // package-level variable is boxed.
16
+ // package-level variable is varrefed.
17
17
  // - For field or method access on an object (`exp.X`), it first writes the base
18
- // expression (`exp.X`) using `WriteValueExpr` (which handles its own boxing).
18
+ // expression (`exp.X`) using `WriteValueExpr` (which handles its own varRefing).
19
19
  // Then, it writes a dot (`.`) followed by the selected identifier (`exp.Sel`)
20
- // using `WriteIdent`, which appends `.value` if the field itself is boxed
20
+ // using `WriteIdent`, which appends `.value` if the field itself is varrefed
21
21
  // (e.g., accessing a field of primitive type through a pointer to a struct
22
22
  // where the field's address might have been taken).
23
23
  //
24
24
  // This function aims to correctly navigate Go's automatic dereferencing and
25
- // TypeScript's explicit boxing model.
25
+ // TypeScript's explicit varRefing model.
26
26
  func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
27
27
  // Check if this is a package selector (e.g., time.Now)
28
28
  if pkgIdent, isPkgIdent := exp.X.(*ast.Ident); isPkgIdent {
@@ -31,62 +31,89 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
31
31
  // Package selectors should never use .value on the package name
32
32
  c.tsw.WriteLiterally(pkgIdent.Name)
33
33
  c.tsw.WriteLiterally(".")
34
- // Write the selected identifier, allowing .value if it's a boxed package variable
34
+ // Write the selected identifier, allowing .value if it's a varrefed package variable
35
35
  c.WriteIdent(exp.Sel, true)
36
36
  return nil
37
37
  }
38
38
  }
39
39
  }
40
40
 
41
- // --- Special case for dereferenced pointer to struct with field access: (*p).field ---
41
+ // --- Special case for dereferenced pointer to struct with field access: (*p).field or (**p).field etc ---
42
42
  var baseExpr ast.Expr = exp.X
43
43
  // Look inside parentheses if present
44
44
  if parenExpr, isParen := exp.X.(*ast.ParenExpr); isParen {
45
45
  baseExpr = parenExpr.X
46
46
  }
47
47
 
48
+ // Check if we have one or more star expressions (dereferences)
48
49
  if starExpr, isStarExpr := baseExpr.(*ast.StarExpr); isStarExpr {
49
- // Get the type of the pointer being dereferenced (e.g., type of 'p' in *p)
50
- ptrType := c.pkg.TypesInfo.TypeOf(starExpr.X)
51
- if ptrType != nil {
52
- if ptrTypeUnwrapped, ok := ptrType.(*types.Pointer); ok {
53
- elemType := ptrTypeUnwrapped.Elem()
54
- if elemType != nil {
55
- // If it's a pointer to a struct, handle field access specially
56
- if _, isStruct := elemType.Underlying().(*types.Struct); isStruct {
57
- // Get the object for the pointer variable itself (e.g., 'p')
58
- var ptrObj types.Object
59
- if ptrIdent, isIdent := starExpr.X.(*ast.Ident); isIdent {
60
- ptrObj = c.pkg.TypesInfo.ObjectOf(ptrIdent)
61
- }
50
+ // Count the levels of dereference and find the innermost expression
51
+ dereferenceCount := 0
52
+ currentExpr := baseExpr
53
+ for {
54
+ if star, ok := currentExpr.(*ast.StarExpr); ok {
55
+ dereferenceCount++
56
+ currentExpr = star.X
57
+ } else {
58
+ break
59
+ }
60
+ }
62
61
 
63
- // Write the pointer expression (e.g., p or p.value if p is boxed)
64
- if err := c.WriteValueExpr(starExpr.X); err != nil {
65
- return fmt.Errorf("failed to write pointer expression for (*p).field: %w", err)
66
- }
62
+ // Get the type of the innermost expression (the pointer variable)
63
+ innerType := c.pkg.TypesInfo.TypeOf(currentExpr)
64
+ if innerType != nil {
65
+ // Check if after all dereferences we end up with a struct
66
+ finalType := innerType
67
+ for i := 0; i < dereferenceCount; i++ {
68
+ if ptrType, ok := finalType.(*types.Pointer); ok {
69
+ finalType = ptrType.Elem()
70
+ } else {
71
+ break
72
+ }
73
+ }
67
74
 
68
- // Add ! for non-null assertion
69
- c.tsw.WriteLiterally("!")
75
+ // If the final type is a struct, handle field access specially
76
+ if _, isStruct := finalType.Underlying().(*types.Struct); isStruct {
77
+ // Write the fully dereferenced expression
78
+ if err := c.WriteValueExpr(starExpr); err != nil {
79
+ return fmt.Errorf("failed to write dereferenced expression for field access: %w", err)
80
+ }
70
81
 
71
- // Add .value ONLY if the pointer variable itself needs boxed access
72
- // This handles the case where 'p' points to a boxed struct (e.g., p = s where s is Box<MyStruct>)
73
- if ptrObj != nil && c.analysis.NeedsBoxedAccess(ptrObj) {
74
- c.tsw.WriteLiterally(".value")
82
+ // Check if we need an extra .value for varrefed struct access
83
+ // This happens when the struct being pointed to is varrefed
84
+ needsExtraValue := false
85
+ if ident, ok := currentExpr.(*ast.Ident); ok {
86
+ if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
87
+ // Check if after dereferencing, we get a varrefed struct
88
+ ptrType := obj.Type()
89
+ for i := 0; i < dereferenceCount; i++ {
90
+ if ptr, ok := ptrType.(*types.Pointer); ok {
91
+ ptrType = ptr.Elem()
92
+ }
93
+ }
94
+ // If the final pointed-to type suggests the struct is varrefed
95
+ // (i.e., the dereference operation results in VarRef<Struct>)
96
+ if c.analysis.NeedsVarRefAccess(obj) {
97
+ needsExtraValue = true
75
98
  }
76
-
77
- // Add .field
78
- c.tsw.WriteLiterally(".")
79
- c.WriteIdent(exp.Sel, false) // Don't add .value to the field itself
80
- return nil
81
99
  }
82
100
  }
101
+
102
+ if needsExtraValue {
103
+ c.tsw.WriteLiterally("!.value")
104
+ }
105
+
106
+ // Add .field
107
+ c.tsw.WriteLiterally(".")
108
+ c.WriteIdent(exp.Sel, false) // Don't add .value to the field itself
109
+ return nil
83
110
  }
84
111
  }
85
112
  }
86
113
  // --- End Special Case ---
87
114
 
88
115
  // Fallback / Normal Case (e.g., obj.Field, pkg.Var, method calls)
89
- // WriteValueExpr handles adding .value for the base variable itself if it's boxed.
116
+ // WriteValueExpr handles adding .value for the base variable itself if it's varrefed.
90
117
  if err := c.WriteValueExpr(exp.X); err != nil {
91
118
  return fmt.Errorf("failed to write selector base expression: %w", err)
92
119
  }
@@ -115,11 +142,9 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
115
142
  }
116
143
 
117
144
  // Write the field/method name.
118
- // Pass 'true' to WriteIdent to potentially add '.value' if the field itself
119
- // needs boxed access (e.g., accessing a primitive field via pointer where
120
- // the field's address might have been taken elsewhere - less common but possible).
121
- // For simple struct field access like p.Val or (*p).Val, WriteIdent(..., true)
122
- // relies on NeedsBoxedAccess for the field 'Val', which should typically be false.
123
- c.WriteIdent(exp.Sel, true)
145
+ // Pass 'false' to WriteIdent to NOT add '.value' for struct fields.
146
+ // Struct fields use getters/setters, so we don't want to add .value here.
147
+ // The setter will handle the internal .value access.
148
+ c.WriteIdent(exp.Sel, false)
124
149
  return nil
125
150
  }
@@ -1,89 +1,81 @@
1
1
  package compiler
2
2
 
3
3
  import (
4
- "fmt"
5
4
  "go/ast"
5
+ "go/types"
6
6
  )
7
7
 
8
8
  // WriteStarExpr translates a Go pointer dereference expression (`ast.StarExpr`, e.g., `*p`)
9
9
  // into its TypeScript equivalent. This involves careful handling of Go's pointers
10
- // and TypeScript's boxing mechanism for emulating pointer semantics.
10
+ // and TypeScript's varRefing mechanism for emulating pointer semantics.
11
11
  //
12
- // The translation depends on whether the pointer variable `p` itself is boxed and
12
+ // The translation depends on whether the pointer variable `p` itself is varrefed and
13
13
  // what type of value it points to:
14
- // 1. If `p` is not boxed and points to a primitive or another pointer: `*p` -> `p!.value`.
15
- // (`p` holds a box, so dereference accesses its `value` field).
16
- // 2. If `p` is not boxed and points to a struct: `*p` -> `p!`.
14
+ // 1. If `p` is not varrefed and points to a primitive or another pointer: `*p` -> `p!.value`.
15
+ // (`p` holds a varRef, so dereference accesses its `value` field).
16
+ // 2. If `p` is not varrefed and points to a struct: `*p` -> `p!`.
17
17
  // (`p` holds the struct instance directly; structs are reference types in TS).
18
- // 3. If `p` is boxed (i.e., `p` is `$.Box<PointerType>`) and points to a primitive/pointer:
19
- // `*p` -> `p.value!.value`.
20
- // (First `.value` unboxes `p`, then `!.value` dereferences the inner pointer).
21
- // 4. If `p` is boxed and points to a struct: `*p` -> `p.value!`.
22
- // (First `.value` unboxes `p` to get the struct instance).
18
+ // 3. If `p` is variable referenced (i.e., `p` is `$.VarRef<PointerType>`) and points to a primitive/pointer:
19
+ // `p.value!.value` (access the variable reference, then dereference the pointer)
20
+ // 4. If `p` is varrefed and points to a struct: `p.value!`.
21
+ // (First `.value` unvarRefes `p` to get the struct instance).
23
22
  //
24
- // `WriteValueExpr(operand)` handles the initial unboxing of `p` if `p` itself is a boxed variable.
23
+ // `WriteValueExpr(operand)` handles the initial unvarRefing of `p` if `p` itself is a varrefed variable.
25
24
  // A non-null assertion `!` is always added as pointers can be nil.
26
- // `c.analysis.NeedsBoxedDeref(ptrType)` determines if an additional `.value` is needed
27
- // based on whether the dereferenced type is a primitive/pointer (requires `.value`) or
28
- // a struct (does not require `.value`).
25
+ // The function determines if `.value` access is needed by checking what the Go pointer operand points to.
26
+ //
27
+ // For multi-level dereferences like `***p`, this function is called recursively, with each level
28
+ // adding the appropriate `!.value` suffix.
29
+ //
30
+ // Examples:
31
+ // - Simple pointer to primitive: `p!.value` (where p is *int)
32
+ // - Variable referenced pointer to primitive: `p.value!.value` (where p is VarRef<*int>)
33
+ // Example: let p = $.varRef(x) (where x is another variable reference) => p.value!.value
34
+ // - Pointer to struct: `p!` (where p is *MyStruct)
35
+ // Example: let p = $.varRef(new MyStruct()) => p.value!
36
+ // - Variable referenced pointer to struct: `p.value!` (where p is VarRef<*MyStruct>)
37
+ // - Triple pointer: `p3!.value!.value!.value` (where p3 is VarRef<VarRef<VarRef<number> | null> | null> | null)
29
38
  func (c *GoToTSCompiler) WriteStarExpr(exp *ast.StarExpr) error {
30
- // Generate code for a pointer dereference expression (*p).
31
- //
32
- // IMPORTANT: Pointer dereferencing in TypeScript requires careful handling of the box/unbox state:
33
- //
34
- // 1. p!.value - when p is not boxed and points to a primitive/pointer
35
- // Example: let p = x (where x is a box) => p!.value
36
- //
37
- // 2. p! - when p is not boxed and points to a struct
38
- // Example: let p = new MyStruct() => p! (structs are reference types)
39
- //
40
- // 3. p.value!.value - when p is boxed and points to a primitive/pointer
41
- // Example: let p = $.box(x) (where x is another box) => p.value!.value
42
- //
43
- // 4. p.value! - when p is boxed and points to a struct
44
- // Example: let p = $.box(new MyStruct()) => p.value!
45
- //
46
- // Critical bug fix: We must handle each case correctly to avoid over-dereferencing
47
- // (adding too many .value) or under-dereferencing (missing .value where needed)
48
- //
49
- // NOTE: This logic aligns with design/BOXES_POINTERS.md.
50
-
51
- // Get the operand expression and its type information
52
- operand := exp.X
53
-
54
- // Get the type of the operand (the pointer being dereferenced)
55
- ptrType := c.pkg.TypesInfo.TypeOf(operand)
56
-
57
- // Special case for handling multi-level dereferencing:
58
- // Check if the operand is itself a StarExpr (e.g., **p or ***p)
59
- // We need to handle these specially to correctly generate nested .value accesses
60
- if starExpr, isStarExpr := operand.(*ast.StarExpr); isStarExpr {
61
- // First, write the inner star expression
62
- if err := c.WriteStarExpr(starExpr); err != nil {
63
- return fmt.Errorf("failed to write inner star expression: %w", err)
39
+ // Check if the operand is an identifier that is varrefed
40
+ isVarrefedIdent := false
41
+ if ident, ok := exp.X.(*ast.Ident); ok {
42
+ if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
43
+ isVarrefedIdent = c.analysis.NeedsVarRef(obj)
64
44
  }
65
-
66
- // Always add .value for multi-level dereferences
67
- // For expressions like **p, each * adds a .value
68
- c.tsw.WriteLiterally("!.value")
69
- return nil
70
45
  }
71
46
 
72
- // Standard case: single-level dereference
73
- // Write the pointer expression, which will access .value if the variable is boxed
74
- // WriteValueExpr will add .value if the variable itself is boxed (p.value)
75
- if err := c.WriteValueExpr(operand); err != nil {
76
- return fmt.Errorf("failed to write star expression operand: %w", err)
47
+ // Write the operand
48
+ if isVarrefedIdent {
49
+ // For varrefed identifiers, we need to access the value first
50
+ if err := c.WriteValueExpr(exp.X); err != nil {
51
+ return err
52
+ }
53
+ } else {
54
+ // For non-varrefed identifiers and other expressions
55
+ switch operand := exp.X.(type) {
56
+ case *ast.Ident:
57
+ // Write identifier without .value access
58
+ c.WriteIdent(operand, false)
59
+ default:
60
+ // For other expressions (like nested star expressions), use WriteValueExpr
61
+ if err := c.WriteValueExpr(exp.X); err != nil {
62
+ return err
63
+ }
64
+ }
77
65
  }
78
66
 
79
- // Add ! for null assertion - all pointers can be null in TypeScript
67
+ // Add non-null assertion for pointer safety
80
68
  c.tsw.WriteLiterally("!")
81
69
 
82
- // Add .value only if we need boxed dereferencing for this type of pointer
83
- // This depends on whether we're dereferencing to a primitive (needs .value)
84
- // or to a struct (no .value needed)
85
- if c.analysis.NeedsBoxedDeref(ptrType) {
86
- c.tsw.WriteLiterally(".value")
70
+ // Check what the operand points to (not what the result is)
71
+ operandType := c.pkg.TypesInfo.TypeOf(exp.X)
72
+ if ptrType, isPtr := operandType.(*types.Pointer); isPtr {
73
+ elemType := ptrType.Elem()
74
+ // Only add .value if NOT pointing to a struct
75
+ if _, isStruct := elemType.Underlying().(*types.Struct); !isStruct {
76
+ c.tsw.WriteLiterally(".value")
77
+ }
78
+ // If pointing to a struct, don't add .value (structs are reference types in TS)
87
79
  }
88
80
 
89
81
  return nil
@@ -11,7 +11,7 @@ import (
11
11
  // It handles various Go type expressions:
12
12
  // - Basic types (e.g., int, string, bool) -> TypeScript primitives (number, string, boolean)
13
13
  // - Named types -> TypeScript class/interface names
14
- // - Pointer types (`*T`) -> `$.Box<T_ts> | null`
14
+ // - Pointer types (`*T`) -> `$.VarRef<T_ts> | null`
15
15
  // - Slice types (`[]T`) -> `$.Slice<T_ts>`
16
16
  // - Array types (`[N]T`) -> `T_ts[]`
17
17
  // - Map types (`map[K]V`) -> `Map<K_ts, V_ts>`
@@ -7,7 +7,7 @@ import (
7
7
  // WriteValueExpr translates a Go abstract syntax tree (AST) expression (`ast.Expr`)
8
8
  // that represents a value into its TypeScript value equivalent.
9
9
  // This is a central dispatch function for various expression types:
10
- // - Identifiers (`ast.Ident`): Delegates to `WriteIdent`, potentially adding `.value` for boxed variables.
10
+ // - Identifiers (`ast.Ident`): Delegates to `WriteIdent`, potentially adding `.value` for varrefed variables.
11
11
  // - Selector expressions (`ast.SelectorExpr`, e.g., `obj.Field` or `pkg.Var`): Delegates to `WriteSelectorExpr`.
12
12
  // - Pointer dereferences (`ast.StarExpr`, e.g., `*ptr`): Delegates to `WriteStarExpr`.
13
13
  // - Function calls (`ast.CallExpr`): Delegates to `WriteCallExpr`.
package/compiler/expr.go CHANGED
@@ -71,8 +71,44 @@ func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
71
71
  }
72
72
 
73
73
  // Check if it's a type parameter with a union constraint (e.g., string | []byte)
74
- if _, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
75
- // For type parameters with string | []byte constraint, use specialized function
74
+ if typeParam, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
75
+ // Check if the type parameter is constrained to slice types
76
+ constraint := typeParam.Constraint()
77
+ if constraint != nil {
78
+ underlying := constraint.Underlying()
79
+ if iface, isInterface := underlying.(*types.Interface); isInterface {
80
+ // Check if this is a mixed string/byte constraint (like string | []byte)
81
+ if hasMixedStringByteConstraint(iface) {
82
+ // For mixed constraints, use specialized function that returns number (byte value)
83
+ c.tsw.WriteLiterally("$.indexStringOrBytes(")
84
+ if err := c.WriteValueExpr(exp.X); err != nil {
85
+ return err
86
+ }
87
+ c.tsw.WriteLiterally(", ")
88
+ if err := c.WriteValueExpr(exp.Index); err != nil {
89
+ return err
90
+ }
91
+ c.tsw.WriteLiterally(")")
92
+ return nil
93
+ }
94
+
95
+ // Check if the constraint includes only slice types (pure slice constraint)
96
+ if hasSliceConstraint(iface) {
97
+ // This is a pure slice type parameter, use regular slice indexing
98
+ if err := c.WriteValueExpr(exp.X); err != nil {
99
+ return err
100
+ }
101
+ c.tsw.WriteLiterally("![") // non-null assertion
102
+ if err := c.WriteValueExpr(exp.Index); err != nil {
103
+ return err
104
+ }
105
+ c.tsw.WriteLiterally("]")
106
+ return nil
107
+ }
108
+ }
109
+ }
110
+
111
+ // For other type parameters, use specialized function as fallback
76
112
  // that returns number (byte value) for better TypeScript typing
77
113
  c.tsw.WriteLiterally("$.indexStringOrBytes(")
78
114
  if err := c.WriteValueExpr(exp.X); err != nil {
@@ -157,7 +193,7 @@ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
157
193
  // of the left (X) and right (Y) operands of the binary expression.
158
194
  // Returns `true` if both operands are determined to be pointer types,
159
195
  // `false` otherwise. This is used to apply specific comparison semantics
160
- // for pointers (e.g., comparing the box objects directly).
196
+ // for pointers (e.g., comparing the varRef objects directly).
161
197
  func (c *GoToTSCompiler) isPointerComparison(exp *ast.BinaryExpr) bool {
162
198
  leftType := c.pkg.TypesInfo.TypeOf(exp.X)
163
199
  rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
@@ -195,10 +231,10 @@ func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
195
231
  // It handles several cases:
196
232
  // - Channel send (`ch <- val`): Becomes `await ch.send(val)`.
197
233
  // - Nil comparison for pointers (`ptr == nil` or `ptr != nil`): Compares the
198
- // pointer (which may be a box object or `null`) directly to `null` using
234
+ // pointer (which may be a varRef object or `null`) directly to `null` using
199
235
  // the translated operator (`==` or `!=`).
200
236
  // - Pointer comparison (non-nil, `ptr1 == ptr2` or `ptr1 != ptr2`): Compares
201
- // the box objects directly using strict equality (`===` or `!==`).
237
+ // the varRef objects directly using strict equality (`===` or `!==`).
202
238
  // - Bitwise operations (`&`, `|`, `^`, `<<`, `>>`, `&^`): The expression is wrapped
203
239
  // in parentheses `()` to ensure correct precedence in TypeScript, and operators
204
240
  // are mapped (e.g., `&^` might need special handling or is mapped to a runtime helper).
@@ -243,9 +279,26 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
243
279
  }
244
280
 
245
281
  if isNilComparison {
246
- // Compare the box object directly to null
247
- if err := c.WriteValueExpr(ptrExpr); err != nil {
248
- return fmt.Errorf("failed to write pointer expression in nil comparison: %w", err)
282
+ // For nil comparisons, we need to decide whether to write .value or not
283
+ // If the pointer variable is varrefed, we need to access .value
284
+ if ident, ok := ptrExpr.(*ast.Ident); ok {
285
+ if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
286
+ if c.analysis.NeedsVarRef(obj) {
287
+ // Variable is varrefed, so we need to access .value
288
+ c.WriteIdent(ident, true) // This will add .value
289
+ } else {
290
+ // Variable is not varrefed, write directly
291
+ c.WriteIdent(ident, false)
292
+ }
293
+ } else {
294
+ // No object info, write directly
295
+ c.WriteIdent(ident, false)
296
+ }
297
+ } else {
298
+ // For other expressions, use WriteValueExpr (but this might need review)
299
+ if err := c.WriteValueExpr(ptrExpr); err != nil {
300
+ return fmt.Errorf("failed to write pointer expression in nil comparison: %w", err)
301
+ }
249
302
  }
250
303
  c.tsw.WriteLiterally(" ")
251
304
  tokStr, ok := TokenToTs(exp.Op)
@@ -258,7 +311,7 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
258
311
  }
259
312
 
260
313
  // Check if this is a pointer comparison (non-nil)
261
- // Compare the box objects directly using === or !==
314
+ // Compare the varRef objects directly using === or !==
262
315
  if c.isPointerComparison(exp) {
263
316
  c.tsw.WriteLiterally("(") // Wrap comparison
264
317
  if err := c.WriteValueExpr(exp.X); err != nil {
@@ -284,6 +337,50 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
284
337
  return nil
285
338
  }
286
339
 
340
+ // Check for Duration arithmetic operations (multiplication)
341
+ if exp.Op == token.MUL && c.pkg != nil && c.pkg.TypesInfo != nil {
342
+ leftType := c.pkg.TypesInfo.TypeOf(exp.X)
343
+ rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
344
+
345
+ // Check if left operand is a Duration type (from time package)
346
+ if leftType != nil {
347
+ if namedType, ok := leftType.(*types.Named); ok {
348
+ if namedType.Obj().Pkg() != nil && namedType.Obj().Pkg().Path() == "time" && namedType.Obj().Name() == "Duration" {
349
+ // Duration * number -> Duration.multiply(duration, number)
350
+ c.tsw.WriteLiterally("$.multiplyDuration(")
351
+ if err := c.WriteValueExpr(exp.X); err != nil {
352
+ return fmt.Errorf("failed to write Duration in multiplication: %w", err)
353
+ }
354
+ c.tsw.WriteLiterally(", ")
355
+ if err := c.WriteValueExpr(exp.Y); err != nil {
356
+ return fmt.Errorf("failed to write multiplier in Duration multiplication: %w", err)
357
+ }
358
+ c.tsw.WriteLiterally(")")
359
+ return nil
360
+ }
361
+ }
362
+ }
363
+
364
+ // Check if right operand is a Duration type (number * Duration)
365
+ if rightType != nil {
366
+ if namedType, ok := rightType.(*types.Named); ok {
367
+ if namedType.Obj().Pkg() != nil && namedType.Obj().Pkg().Path() == "time" && namedType.Obj().Name() == "Duration" {
368
+ // number * Duration -> Duration.multiply(duration, number)
369
+ c.tsw.WriteLiterally("$.multiplyDuration(")
370
+ if err := c.WriteValueExpr(exp.Y); err != nil {
371
+ return fmt.Errorf("failed to write Duration in multiplication: %w", err)
372
+ }
373
+ c.tsw.WriteLiterally(", ")
374
+ if err := c.WriteValueExpr(exp.X); err != nil {
375
+ return fmt.Errorf("failed to write multiplier in Duration multiplication: %w", err)
376
+ }
377
+ c.tsw.WriteLiterally(")")
378
+ return nil
379
+ }
380
+ }
381
+ }
382
+ }
383
+
287
384
  // Check if the operator is a bitwise operator
288
385
  isBitwise := false
289
386
  switch exp.Op {
@@ -316,6 +413,7 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
316
413
  if !ok {
317
414
  return errors.Errorf("unhandled binary op: %s", exp.Op.String())
318
415
  }
416
+
319
417
  c.tsw.WriteLiterally(tokStr)
320
418
  c.tsw.WriteLiterally(" ")
321
419
  if err := c.WriteValueExpr(exp.Y); err != nil {
@@ -334,11 +432,11 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
334
432
  // It handles several unary operations:
335
433
  // - Channel receive (`<-ch`): Becomes `await ch.receive()`.
336
434
  // - Address-of (`&var`):
337
- // - If `var` is a boxed variable (its address was taken), `&var` evaluates
338
- // to the box itself (i.e., `varName` in TypeScript, which holds the box).
339
- // - Otherwise (e.g., `&unboxedVar`, `&MyStruct{}`, `&FuncCall()`), it evaluates
435
+ // - If `var` is a varrefed variable (its address was taken), `&var` evaluates
436
+ // to the varRef itself (i.e., `varName` in TypeScript, which holds the varRef).
437
+ // - Otherwise (e.g., `&unvarrefedVar`, `&MyStruct{}`, `&FuncCall()`), it evaluates
340
438
  // the operand `var`. The resulting TypeScript value (e.g., a new object instance)
341
- // acts as the "pointer". Boxing decisions for such pointers are handled at
439
+ // acts as the "pointer". VarRefing decisions for such pointers are handled at
342
440
  // the assignment site.
343
441
  // - Other unary operators (`+`, `-`, `!`, `^`): Mapped to their TypeScript
344
442
  // equivalents (e.g., `+`, `-`, `!`, `~` for bitwise NOT). Parentheses are added
@@ -359,26 +457,26 @@ func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
359
457
  }
360
458
 
361
459
  if exp.Op == token.AND { // Address-of operator (&)
362
- // If the operand is an identifier for a variable that is boxed,
363
- // the result of & is the box itself.
460
+ // If the operand is an identifier for a variable that is varrefed,
461
+ // the result of & is the varRef itself.
364
462
  if ident, ok := exp.X.(*ast.Ident); ok {
365
463
  var obj types.Object
366
464
  obj = c.pkg.TypesInfo.Uses[ident]
367
465
  if obj == nil {
368
466
  obj = c.pkg.TypesInfo.Defs[ident]
369
467
  }
370
- if obj != nil && c.analysis.NeedsBoxed(obj) {
371
- // &boxedVar -> boxedVar (the box itself)
372
- c.tsw.WriteLiterally(ident.Name) // Write the identifier name (which holds the box)
468
+ if obj != nil && c.analysis.NeedsVarRef(obj) {
469
+ // &varRefVar -> varRefVar (the variable reference itself)
470
+ c.tsw.WriteLiterally(ident.Name) // Write the identifier name (which holds the variable reference)
373
471
  return nil
374
472
  }
375
473
  }
376
474
 
377
- // Otherwise (&unboxedVar, &CompositeLit{}, &FuncCall(), etc.),
475
+ // Otherwise (&unvarrefedVar, &CompositeLit{}, &FuncCall(), etc.),
378
476
  // the address-of operator in Go, when used to create a pointer,
379
477
  // translates to simply evaluating the operand in TypeScript.
380
478
  // The resulting value (e.g., a new object instance) acts as the "pointer".
381
- // Boxing decisions are handled at the assignment site based on the LHS variable.
479
+ // VarRefing decisions are handled at the assignment site based on the LHS variable.
382
480
  if err := c.WriteValueExpr(exp.X); err != nil {
383
481
  return fmt.Errorf("failed to write &-operand: %w", err)
384
482
  }
@@ -391,6 +489,13 @@ func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
391
489
  if !ok {
392
490
  return errors.Errorf("unhandled unary op: %s", exp.Op.String())
393
491
  }
492
+
493
+ // Special case: In Go, ^ is bitwise NOT when used as unary operator
494
+ // In TypeScript, bitwise NOT is ~, not ^
495
+ if exp.Op == token.XOR {
496
+ tokStr = "~"
497
+ }
498
+
394
499
  c.tsw.WriteLiterally(tokStr)
395
500
 
396
501
  // Add space if operator is not postfix (e.g., !)
package/compiler/field.go CHANGED
@@ -47,7 +47,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
47
47
  if j > 0 {
48
48
  c.tsw.WriteLiterally(", ")
49
49
  }
50
- c.tsw.WriteLiterally(name.Name)
50
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
51
51
  c.tsw.WriteLiterally(": ")
52
52
  typ := c.pkg.TypesInfo.TypeOf(field.Type)
53
53
  c.WriteGoType(typ, GoTypeContextGeneral)
@@ -65,7 +65,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
65
65
  c.tsw.WriteLiterally(", ")
66
66
  }
67
67
  c.tsw.WriteLiterally("...")
68
- c.tsw.WriteLiterally(name.Name)
68
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
69
69
  }
70
70
 
71
71
  c.tsw.WriteLiterally(": ")
@@ -86,7 +86,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
86
86
  if j > 0 {
87
87
  c.tsw.WriteLiterally(", ")
88
88
  }
89
- c.tsw.WriteLiterally(name.Name)
89
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
90
90
  c.tsw.WriteLiterally(": ")
91
91
  typ := c.pkg.TypesInfo.TypeOf(field.Type)
92
92
  c.WriteGoType(typ, GoTypeContextGeneral) // Use WriteGoType for parameter type
@@ -143,7 +143,7 @@ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
143
143
 
144
144
  // argument names: keep original casing, no access modifier
145
145
  if isArguments {
146
- c.tsw.WriteLiterally(name.Name)
146
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
147
147
  // Argument type is handled in WriteFieldList, so continue
148
148
  continue
149
149
  } else {
@@ -82,16 +82,17 @@ func GoBuiltinToTypescript(typeName string) (string, bool) {
82
82
  // in their respective expression/statement writers and might not be directly mapped here.
83
83
  // Bitwise AND NOT (`&^=`) is also mapped but may require specific runtime support if not directly translatable.
84
84
  var tokenMap = map[token.Token]string{
85
- token.ADD: "+",
86
- token.SUB: "-",
87
- token.MUL: "*",
88
- token.QUO: "/",
89
- token.REM: "%",
90
- token.AND: "&",
91
- token.OR: "|",
92
- token.XOR: "^",
93
- token.SHL: "<<",
94
- token.SHR: ">>",
85
+ token.ADD: "+",
86
+ token.SUB: "-",
87
+ token.MUL: "*",
88
+ token.QUO: "/",
89
+ token.REM: "%",
90
+ token.AND: "&",
91
+ token.OR: "|",
92
+ token.XOR: "^",
93
+ token.SHL: "<<",
94
+ token.SHR: ">>",
95
+ token.AND_NOT: "& ~", // &^ operator: bitwise AND NOT
95
96
 
96
97
  token.ADD_ASSIGN: "+=",
97
98
  token.SUB_ASSIGN: "-=",
@@ -12,7 +12,7 @@ import (
12
12
  // It handles the generation of:
13
13
  // - The class declaration.
14
14
  // - Getters and setters for all fields (both direct and embedded).
15
- // - The internal `_fields` property, which stores field values in `$.Box` containers
15
+ // - The internal `_fields` property, which stores field values in `$.VarRef` containers
16
16
  // to maintain Go's value semantics.
17
17
  // - A constructor that initializes the `_fields` and allows partial initialization.
18
18
  // - A `clone` method for creating a deep copy of the struct instance.
@@ -83,7 +83,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
83
83
  fieldKeyName = field.Name()
84
84
  }
85
85
  fieldTsType := c.getTypeString(field.Type())
86
- c.tsw.WriteLinef("%s: $.Box<%s>;", fieldKeyName, fieldTsType)
86
+ c.tsw.WriteLinef("%s: $.VarRef<%s>;", fieldKeyName, fieldTsType)
87
87
  }
88
88
  c.tsw.Indent(-1)
89
89
  c.tsw.WriteLine("}")
@@ -111,7 +111,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
111
111
  fieldKeyName = field.Name()
112
112
  }
113
113
 
114
- c.writeBoxedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
114
+ c.writeVarRefedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
115
115
 
116
116
  if i < numFields-1 {
117
117
  c.tsw.WriteLine(",")