goscript 0.0.23 → 0.0.24
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.
- package/README.md +1 -1
- package/cmd/goscript/cmd_compile.go +1 -1
- package/compiler/analysis.go +73 -131
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +37 -43
- package/compiler/builtin_test.go +102 -0
- package/compiler/compiler.go +79 -14
- package/compiler/composite-lit.go +108 -43
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/expr-selector.go +66 -41
- package/compiler/expr-star.go +57 -65
- package/compiler/expr-type.go +1 -1
- package/compiler/expr-value.go +1 -1
- package/compiler/expr.go +79 -18
- package/compiler/primitive.go +11 -10
- package/compiler/spec-struct.go +3 -3
- package/compiler/spec-value.go +75 -29
- package/compiler/spec.go +9 -3
- package/compiler/stmt-assign.go +36 -2
- package/compiler/stmt-for.go +11 -0
- package/compiler/stmt-range.go +110 -0
- package/compiler/stmt.go +52 -0
- package/compiler/type.go +36 -11
- package/dist/gs/builtin/builtin.js +37 -0
- package/dist/gs/builtin/builtin.js.map +1 -0
- package/dist/gs/builtin/channel.js +471 -0
- package/dist/gs/builtin/channel.js.map +1 -0
- package/dist/gs/builtin/defer.js +54 -0
- package/dist/gs/builtin/defer.js.map +1 -0
- package/dist/gs/builtin/io.js +15 -0
- package/dist/gs/builtin/io.js.map +1 -0
- package/dist/gs/builtin/map.js +44 -0
- package/dist/gs/builtin/map.js.map +1 -0
- package/dist/gs/builtin/slice.js +799 -0
- package/dist/gs/builtin/slice.js.map +1 -0
- package/dist/gs/builtin/type.js +745 -0
- package/dist/gs/builtin/type.js.map +1 -0
- package/dist/gs/builtin/varRef.js +14 -0
- package/dist/gs/builtin/varRef.js.map +1 -0
- package/dist/gs/context/context.js +55 -0
- package/dist/gs/context/context.js.map +1 -0
- package/dist/gs/context/index.js +2 -0
- package/dist/gs/context/index.js.map +1 -0
- package/dist/gs/runtime/index.js +2 -0
- package/dist/gs/runtime/index.js.map +1 -0
- package/dist/gs/runtime/runtime.js +158 -0
- package/dist/gs/runtime/runtime.js.map +1 -0
- package/dist/gs/time/index.js +2 -0
- package/dist/gs/time/index.js.map +1 -0
- package/dist/gs/time/time.js +115 -0
- package/dist/gs/time/time.js.map +1 -0
- package/package.json +3 -2
|
@@ -13,27 +13,28 @@ import (
|
|
|
13
13
|
//
|
|
14
14
|
// It handles several types of composite literals:
|
|
15
15
|
// - Map literals (e.g., `map[K]V{k1: v1}`): Translated to `new Map([[k1_ts, v1_ts]])`.
|
|
16
|
-
// Values are processed by `
|
|
16
|
+
// Values are processed by `WriteVarRefedValue`.
|
|
17
17
|
// - Array/Slice literals (e.g., `[]T{e1, e2}`, `[N]T{idx: val}`):
|
|
18
18
|
// - For `[]byte{...}`, translated to `new Uint8Array([...])`.
|
|
19
19
|
// - For other `[]T` or `[N]T`, translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
|
|
20
20
|
// It handles both keyed and unkeyed elements, infers length if necessary,
|
|
21
21
|
// and uses zero values for uninitialized array elements.
|
|
22
22
|
// Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
|
|
23
|
-
// Element values are processed by `
|
|
23
|
+
// Element values are processed by `WriteVarRefedValue`.
|
|
24
24
|
// - Struct literals:
|
|
25
25
|
// - Named structs (e.g., `MyStruct{F: v}` or `&MyStruct{F: v}`): Translated to
|
|
26
26
|
// `new MyStruct_ts({ F: v_ts, ... })`. The constructor typically uses an `_init` method.
|
|
27
27
|
// - Anonymous structs (e.g., `struct{F int}{F: v}`): Translated to TypeScript
|
|
28
28
|
// object literals `{ F: v_ts, ... }`.
|
|
29
29
|
// It processes keyed elements (`FieldName: Value`) and unkeyed elements (for anonymous
|
|
30
|
-
// structs or arrays). Field values are processed by `
|
|
30
|
+
// structs or arrays). Field values are processed by `WriteVarRefedValue`.
|
|
31
31
|
// Embedded struct fields are initialized, and explicit initializers for embedded
|
|
32
32
|
// structs (e.g. `Outer{InnerField: InnerType{...}}`) are handled.
|
|
33
|
-
// The function uses `c.analysis` to determine correct value access (e.g., `.value` for
|
|
33
|
+
// The function uses `c.analysis` to determine correct value access (e.g., `.value` for var-refed fields).
|
|
34
34
|
func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
35
35
|
// Get the type of the composite literal
|
|
36
36
|
litType := c.pkg.TypesInfo.TypeOf(exp)
|
|
37
|
+
|
|
37
38
|
if exp.Type != nil {
|
|
38
39
|
// Handle map literals: map[K]V{k1: v1, k2: v2}
|
|
39
40
|
if _, isMapType := exp.Type.(*ast.MapType); isMapType {
|
|
@@ -47,11 +48,11 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
47
48
|
|
|
48
49
|
if kv, ok := elm.(*ast.KeyValueExpr); ok {
|
|
49
50
|
c.tsw.WriteLiterally("[")
|
|
50
|
-
if err := c.
|
|
51
|
+
if err := c.WriteVarRefedValue(kv.Key); err != nil {
|
|
51
52
|
return fmt.Errorf("failed to write map literal key: %w", err)
|
|
52
53
|
}
|
|
53
54
|
c.tsw.WriteLiterally(", ")
|
|
54
|
-
if err := c.
|
|
55
|
+
if err := c.WriteVarRefedValue(kv.Value); err != nil {
|
|
55
56
|
return fmt.Errorf("failed to write map literal value: %w", err)
|
|
56
57
|
}
|
|
57
58
|
c.tsw.WriteLiterally("]")
|
|
@@ -139,7 +140,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
139
140
|
hasKeyedElements = true
|
|
140
141
|
} else {
|
|
141
142
|
c.tsw.WriteCommentInline("unhandled keyed array literal key type")
|
|
142
|
-
if err := c.
|
|
143
|
+
if err := c.WriteVarRefedValue(elm); err != nil {
|
|
143
144
|
return fmt.Errorf("failed to write keyed array literal element with unhandled key type: %w", err)
|
|
144
145
|
}
|
|
145
146
|
}
|
|
@@ -167,7 +168,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
167
168
|
c.tsw.WriteLiterally(", ")
|
|
168
169
|
}
|
|
169
170
|
if elm, ok := elements[i]; ok && elm != nil {
|
|
170
|
-
if err := c.
|
|
171
|
+
if err := c.WriteVarRefedValue(elm); err != nil {
|
|
171
172
|
return fmt.Errorf("failed to write array literal element: %w", err)
|
|
172
173
|
}
|
|
173
174
|
} else {
|
|
@@ -340,7 +341,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
340
341
|
}
|
|
341
342
|
c.tsw.WriteLiterally(keyName)
|
|
342
343
|
c.tsw.WriteLiterally(": ")
|
|
343
|
-
if err := c.
|
|
344
|
+
if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
|
|
344
345
|
return err
|
|
345
346
|
}
|
|
346
347
|
firstFieldWritten = true
|
|
@@ -368,14 +369,14 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
368
369
|
if i > 0 {
|
|
369
370
|
c.tsw.WriteLiterally(", ")
|
|
370
371
|
}
|
|
371
|
-
if err := c.
|
|
372
|
+
if err := c.WriteVarRefedValue(elem); err != nil {
|
|
372
373
|
return err
|
|
373
374
|
}
|
|
374
375
|
}
|
|
375
376
|
c.tsw.WriteLiterally("}")
|
|
376
377
|
} else {
|
|
377
378
|
// Not a composite literal, write it normally
|
|
378
|
-
if err := c.
|
|
379
|
+
if err := c.WriteVarRefedValue(explicitEmbedded[embeddedName]); err != nil {
|
|
379
380
|
return err
|
|
380
381
|
}
|
|
381
382
|
}
|
|
@@ -414,7 +415,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
414
415
|
}
|
|
415
416
|
c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
|
|
416
417
|
c.tsw.WriteLiterally(": ")
|
|
417
|
-
if err := c.
|
|
418
|
+
if err := c.WriteVarRefedValue(fieldsMap[keyName]); err != nil {
|
|
418
419
|
return err
|
|
419
420
|
}
|
|
420
421
|
}
|
|
@@ -436,7 +437,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
436
437
|
if i != 0 {
|
|
437
438
|
c.tsw.WriteLiterally(", ")
|
|
438
439
|
}
|
|
439
|
-
if err := c.
|
|
440
|
+
if err := c.WriteVarRefedValue(elm); err != nil {
|
|
440
441
|
return fmt.Errorf("failed to write literal field: %w", err)
|
|
441
442
|
}
|
|
442
443
|
}
|
|
@@ -448,60 +449,124 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
448
449
|
|
|
449
450
|
// Untyped composite literal. Let's use type information to determine what it is.
|
|
450
451
|
// First try to get the type information for the expression
|
|
451
|
-
isObject := false
|
|
452
452
|
if tv, ok := c.pkg.TypesInfo.Types[exp]; ok && tv.Type != nil {
|
|
453
453
|
underlying := tv.Type.Underlying()
|
|
454
454
|
switch underlying.(type) {
|
|
455
455
|
case *types.Map, *types.Struct:
|
|
456
|
-
|
|
456
|
+
// Handle struct directly with the struct literal logic
|
|
457
|
+
if structType, ok := underlying.(*types.Struct); ok {
|
|
458
|
+
return c.writeUntypedStructLiteral(exp, structType) // true = anonymous
|
|
459
|
+
}
|
|
460
|
+
// Map case would be handled here
|
|
461
|
+
return fmt.Errorf("untyped map composite literals not yet supported")
|
|
457
462
|
case *types.Array, *types.Slice:
|
|
458
|
-
|
|
463
|
+
// Handle array/slice
|
|
464
|
+
return c.writeUntypedArrayLiteral(exp)
|
|
465
|
+
case *types.Pointer:
|
|
466
|
+
// Handle pointer to composite literal
|
|
467
|
+
ptrType := underlying.(*types.Pointer)
|
|
468
|
+
elemType := ptrType.Elem().Underlying()
|
|
469
|
+
switch elemType.(type) {
|
|
470
|
+
case *types.Struct:
|
|
471
|
+
// This is an anonymous struct literal with inferred pointer type
|
|
472
|
+
// Just create the struct object directly - no var-refing needed
|
|
473
|
+
// Anonymous literals are not variables, so they don't get var-refed
|
|
474
|
+
structType := elemType.(*types.Struct)
|
|
475
|
+
return c.writeUntypedStructLiteral(exp, structType) // true = anonymous
|
|
476
|
+
default:
|
|
477
|
+
return fmt.Errorf("unhandled pointer composite literal element type: %T", elemType)
|
|
478
|
+
}
|
|
459
479
|
default:
|
|
460
480
|
return fmt.Errorf("unhandled composite literal type: %T", underlying)
|
|
461
481
|
}
|
|
462
482
|
} else {
|
|
463
483
|
return fmt.Errorf("could not determine composite literal type from type information")
|
|
464
484
|
}
|
|
485
|
+
}
|
|
465
486
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
c.tsw.WriteLiterally("[ ")
|
|
470
|
-
}
|
|
471
|
-
|
|
487
|
+
// writeUntypedArrayLiteral handles untyped composite literals that are arrays/slices
|
|
488
|
+
func (c *GoToTSCompiler) writeUntypedArrayLiteral(exp *ast.CompositeLit) error {
|
|
489
|
+
c.tsw.WriteLiterally("[ ")
|
|
472
490
|
for i, elm := range exp.Elts {
|
|
473
491
|
if i != 0 {
|
|
474
492
|
c.tsw.WriteLiterally(", ")
|
|
475
493
|
}
|
|
476
|
-
if err := c.
|
|
477
|
-
return fmt.Errorf("failed to write untyped
|
|
494
|
+
if err := c.WriteVarRefedValue(elm); err != nil {
|
|
495
|
+
return fmt.Errorf("failed to write untyped array literal element: %w", err)
|
|
478
496
|
}
|
|
479
497
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
498
|
+
c.tsw.WriteLiterally(" ]")
|
|
499
|
+
return nil
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// writeUntypedStructLiteral handles untyped composite literals that are structs or pointers to structs
|
|
503
|
+
func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, structType *types.Struct) error {
|
|
504
|
+
// Create field mapping like the typed struct case
|
|
505
|
+
directFields := make(map[string]ast.Expr)
|
|
506
|
+
|
|
507
|
+
// Handle elements that are key-value pairs
|
|
508
|
+
for _, elt := range exp.Elts {
|
|
509
|
+
if kv, ok := elt.(*ast.KeyValueExpr); ok {
|
|
510
|
+
if keyIdent, ok := kv.Key.(*ast.Ident); ok {
|
|
511
|
+
directFields[keyIdent.Name] = kv.Value
|
|
512
|
+
}
|
|
513
|
+
}
|
|
484
514
|
}
|
|
515
|
+
|
|
516
|
+
// Handle elements that are positional (no key specified)
|
|
517
|
+
if len(directFields) == 0 {
|
|
518
|
+
// If no key-value pairs, try to match positional values to struct fields
|
|
519
|
+
for i, elt := range exp.Elts {
|
|
520
|
+
if _, isKV := elt.(*ast.KeyValueExpr); !isKV && i < structType.NumFields() {
|
|
521
|
+
field := structType.Field(i)
|
|
522
|
+
directFields[field.Name()] = elt
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Write the object literal (always anonymous for untyped)
|
|
528
|
+
c.tsw.WriteLiterally("{")
|
|
529
|
+
|
|
530
|
+
firstFieldWritten := false
|
|
531
|
+
// Write fields in order
|
|
532
|
+
directKeys := make([]string, 0, len(directFields))
|
|
533
|
+
for k := range directFields {
|
|
534
|
+
directKeys = append(directKeys, k)
|
|
535
|
+
}
|
|
536
|
+
slices.Sort(directKeys)
|
|
537
|
+
for _, keyName := range directKeys {
|
|
538
|
+
if firstFieldWritten {
|
|
539
|
+
c.tsw.WriteLiterally(", ")
|
|
540
|
+
}
|
|
541
|
+
c.tsw.WriteLiterally(keyName)
|
|
542
|
+
c.tsw.WriteLiterally(": ")
|
|
543
|
+
if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
|
|
544
|
+
return err
|
|
545
|
+
}
|
|
546
|
+
firstFieldWritten = true
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
c.tsw.WriteLiterally("}")
|
|
485
550
|
return nil
|
|
486
551
|
}
|
|
487
552
|
|
|
488
|
-
//
|
|
553
|
+
// WriteVarRefedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
|
|
489
554
|
// specifically for use as a value within a composite literal (e.g., struct fields,
|
|
490
555
|
// map keys/values, or array/slice elements). Its primary goal is to ensure that the
|
|
491
|
-
// actual
|
|
556
|
+
// actual un-refed value of the expression is used.
|
|
492
557
|
//
|
|
493
558
|
// How it works:
|
|
494
559
|
// - Identifiers (`*ast.Ident`): Delegates to `c.WriteIdent(ident, true)`, forcing
|
|
495
560
|
// the `accessValue` flag to `true`. This ensures that if `ident` refers to a
|
|
496
|
-
// GoScript
|
|
561
|
+
// GoScript var-refed variable, the generated TypeScript accesses its underlying `.value`
|
|
497
562
|
// (e.g., `myVar.value`).
|
|
498
563
|
// - Selector Expressions (`*ast.SelectorExpr`, e.g., `obj.Field`): Delegates to
|
|
499
564
|
// `c.WriteSelectorExpr(e)`. This function handles the necessary logic for
|
|
500
|
-
// accessing fields or methods, including any required
|
|
501
|
-
// itself or the object it's accessed on is
|
|
565
|
+
// accessing fields or methods, including any required un-var-refing if the field
|
|
566
|
+
// itself or the object it's accessed on is var-refed (e.g., `obj.value.field` or
|
|
502
567
|
// `obj.field.value`).
|
|
503
568
|
// - Star Expressions (`*ast.StarExpr`, e.g., `*ptr`): Delegates to `c.WriteStarExpr(e)`.
|
|
504
|
-
// This function handles pointer dereferencing, which in GoScript's
|
|
569
|
+
// This function handles pointer dereferencing, which in GoScript's var-refing model
|
|
505
570
|
// often translates to accessing the `.value` field of the pointer (e.g., `ptr.value`).
|
|
506
571
|
// - Basic Literals (`*ast.BasicLit`, e.g., `123`, `"hello"`): Delegates to
|
|
507
572
|
// `c.WriteBasicLit(e)` for direct translation.
|
|
@@ -511,25 +576,25 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
511
576
|
//
|
|
512
577
|
// Necessity and Distinction from `WriteValueExpr`:
|
|
513
578
|
// While `WriteValueExpr` is a general-purpose function for translating Go expressions
|
|
514
|
-
// and also
|
|
515
|
-
// `
|
|
579
|
+
// and also un-var-refes identifiers (by calling `WriteIdent` with `accessValue: true`),
|
|
580
|
+
// `WriteVarRefedValue` serves a specific and crucial role when called from `WriteCompositeLit`:
|
|
516
581
|
// 1. Clarity of Intent: It explicitly signals that for the constituents of a composite
|
|
517
|
-
// literal, the *
|
|
582
|
+
// literal, the *un-var-refed value* is mandatory.
|
|
518
583
|
// 2. Contract for `WriteCompositeLit`: It ensures that `WriteCompositeLit` receives
|
|
519
584
|
// the correct values for initialization, insulating it from potential changes in
|
|
520
|
-
// the default behavior of `WriteValueExpr` regarding
|
|
585
|
+
// the default behavior of `WriteValueExpr` regarding un-var-refing.
|
|
521
586
|
// 3. Prevents Recursion: `WriteValueExpr` handles `*ast.CompositeLit` nodes by
|
|
522
587
|
// calling `WriteCompositeLit`. If `WriteCompositeLit` were to directly call
|
|
523
588
|
// `WriteValueExpr` for its elements, it could lead to unintended recursion or
|
|
524
|
-
// behavior if an element itself was another composite literal. `
|
|
589
|
+
// behavior if an element itself was another composite literal. `WriteVarRefedValue`
|
|
525
590
|
// acts as a specific intermediary for the *elements*.
|
|
526
591
|
//
|
|
527
|
-
// In summary, `
|
|
592
|
+
// In summary, `WriteVarRefedValue` is a specialized dispatcher used by `WriteCompositeLit`
|
|
528
593
|
// to guarantee that all parts of a Go composite literal are initialized with their
|
|
529
|
-
// proper,
|
|
530
|
-
func (c *GoToTSCompiler)
|
|
594
|
+
// proper, unrefed TypeScript values.
|
|
595
|
+
func (c *GoToTSCompiler) WriteVarRefedValue(expr ast.Expr) error {
|
|
531
596
|
if expr == nil {
|
|
532
|
-
return fmt.Errorf("nil expression passed to
|
|
597
|
+
return fmt.Errorf("nil expression passed to write var refed value")
|
|
533
598
|
}
|
|
534
599
|
|
|
535
600
|
// Handle different expression types
|
package/compiler/config.go
CHANGED
|
@@ -13,13 +13,17 @@ type Config struct {
|
|
|
13
13
|
|
|
14
14
|
// Dir is the working directory for the compiler. If empty, uses the current working directory.
|
|
15
15
|
Dir string
|
|
16
|
-
//
|
|
17
|
-
|
|
16
|
+
// OutputPath is the output path root.
|
|
17
|
+
OutputPath string
|
|
18
18
|
// BuildFlags are the Go build flags (tags) to use during analysis.
|
|
19
19
|
BuildFlags []string
|
|
20
20
|
// AllDependencies controls whether to compile all dependencies of the requested packages.
|
|
21
21
|
// If true, all dependencies will be compiled; if false, only the requested packages are compiled.
|
|
22
22
|
AllDependencies bool
|
|
23
|
+
// DisableEmitBuiltin controls whether to emit builtin packages when they are referenced.
|
|
24
|
+
// If true, builtin packages will not be emitted; if false, they will be emitted if referenced.
|
|
25
|
+
// Default is false (emit builtin packages).
|
|
26
|
+
DisableEmitBuiltin bool
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
// Validate checks the config.
|
|
@@ -30,7 +34,7 @@ func (c *Config) Validate() error {
|
|
|
30
34
|
if c.fset == nil {
|
|
31
35
|
c.fset = token.NewFileSet()
|
|
32
36
|
}
|
|
33
|
-
if c.
|
|
37
|
+
if c.OutputPath == "" {
|
|
34
38
|
return errors.New("output path root must be specified")
|
|
35
39
|
}
|
|
36
40
|
return nil
|
package/compiler/config_test.go
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package compiler
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"reflect"
|
|
5
4
|
"testing"
|
|
6
5
|
)
|
|
7
6
|
|
|
@@ -15,9 +14,9 @@ func TestConfigValidate(t *testing.T) {
|
|
|
15
14
|
{
|
|
16
15
|
name: "valid config",
|
|
17
16
|
config: &Config{
|
|
18
|
-
Dir:
|
|
19
|
-
|
|
20
|
-
BuildFlags:
|
|
17
|
+
Dir: "/some/dir",
|
|
18
|
+
OutputPath: "/output/path",
|
|
19
|
+
BuildFlags: []string{"-tags", "sometag"},
|
|
21
20
|
},
|
|
22
21
|
wantErr: false,
|
|
23
22
|
},
|
|
@@ -33,9 +32,9 @@ func TestConfigValidate(t *testing.T) {
|
|
|
33
32
|
{
|
|
34
33
|
name: "nil fset gets initialized",
|
|
35
34
|
config: &Config{
|
|
36
|
-
fset:
|
|
37
|
-
Dir:
|
|
38
|
-
|
|
35
|
+
fset: nil,
|
|
36
|
+
Dir: "/some/dir",
|
|
37
|
+
OutputPath: "/output/path",
|
|
39
38
|
},
|
|
40
39
|
wantErr: false,
|
|
41
40
|
},
|
|
@@ -61,29 +60,3 @@ func TestConfigValidate(t *testing.T) {
|
|
|
61
60
|
})
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
func TestConfigFields(t *testing.T) {
|
|
66
|
-
// Verify that Config has the expected fields
|
|
67
|
-
config := Config{}
|
|
68
|
-
configType := reflect.TypeOf(config)
|
|
69
|
-
|
|
70
|
-
expectedFields := map[string]string{
|
|
71
|
-
"fset": "*token.FileSet",
|
|
72
|
-
"Dir": "string",
|
|
73
|
-
"OutputPathRoot": "string",
|
|
74
|
-
"BuildFlags": "[]string",
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
for fieldName, expectedType := range expectedFields {
|
|
78
|
-
field, exists := configType.FieldByName(fieldName)
|
|
79
|
-
if !exists {
|
|
80
|
-
t.Errorf("Expected Config to have field %s", fieldName)
|
|
81
|
-
continue
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
actualType := field.Type.String()
|
|
85
|
-
if actualType != expectedType {
|
|
86
|
-
t.Errorf("Field %s has type %s, expected %s", fieldName, actualType, expectedType)
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
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 '
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
|
|
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
|
}
|