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.
- package/README.md +2 -2
- package/cmd/goscript/cmd_compile.go +18 -2
- package/compiler/analysis.go +74 -132
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +37 -43
- package/compiler/builtin_test.go +90 -0
- package/compiler/compiler.go +307 -22
- package/compiler/composite-lit.go +108 -43
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/decl.go +7 -1
- package/compiler/expr-call.go +212 -2
- 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 +125 -20
- package/compiler/field.go +4 -4
- 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 +314 -1
- package/compiler/stmt.go +52 -0
- package/compiler/type.go +83 -15
- package/dist/gs/builtin/builtin.d.ts +9 -0
- package/dist/gs/builtin/builtin.js +46 -0
- package/dist/gs/builtin/builtin.js.map +1 -0
- package/dist/gs/builtin/channel.d.ts +193 -0
- package/dist/gs/builtin/channel.js +471 -0
- package/dist/gs/builtin/channel.js.map +1 -0
- package/dist/gs/builtin/defer.d.ts +38 -0
- package/dist/gs/builtin/defer.js +54 -0
- package/dist/gs/builtin/defer.js.map +1 -0
- package/dist/gs/builtin/index.d.ts +1 -0
- package/dist/gs/builtin/index.js +2 -0
- package/dist/gs/builtin/index.js.map +1 -0
- package/dist/gs/builtin/io.d.ts +16 -0
- package/dist/gs/builtin/io.js +15 -0
- package/dist/gs/builtin/io.js.map +1 -0
- package/dist/gs/builtin/map.d.ts +33 -0
- package/dist/gs/builtin/map.js +44 -0
- package/dist/gs/builtin/map.js.map +1 -0
- package/dist/gs/builtin/slice.d.ts +173 -0
- package/dist/gs/builtin/slice.js +799 -0
- package/dist/gs/builtin/slice.js.map +1 -0
- package/dist/gs/builtin/type.d.ts +203 -0
- package/dist/gs/builtin/type.js +744 -0
- package/dist/gs/builtin/type.js.map +1 -0
- package/dist/gs/builtin/varRef.d.ts +14 -0
- package/dist/gs/builtin/varRef.js +14 -0
- package/dist/gs/builtin/varRef.js.map +1 -0
- package/dist/gs/cmp/index.d.ts +4 -0
- package/dist/gs/cmp/index.js +27 -0
- package/dist/gs/cmp/index.js.map +1 -0
- package/dist/gs/context/context.d.ts +26 -0
- package/dist/gs/context/context.js +305 -0
- package/dist/gs/context/context.js.map +1 -0
- package/dist/gs/context/index.d.ts +1 -0
- package/dist/gs/context/index.js +2 -0
- package/dist/gs/context/index.js.map +1 -0
- package/dist/gs/internal/goarch/index.d.ts +6 -0
- package/dist/gs/internal/goarch/index.js +14 -0
- package/dist/gs/internal/goarch/index.js.map +1 -0
- package/dist/gs/iter/index.d.ts +1 -0
- package/dist/gs/iter/index.js +2 -0
- package/dist/gs/iter/index.js.map +1 -0
- package/dist/gs/iter/iter.d.ts +4 -0
- package/dist/gs/iter/iter.js +91 -0
- package/dist/gs/iter/iter.js.map +1 -0
- package/dist/gs/math/bits/index.d.ts +47 -0
- package/dist/gs/math/bits/index.js +298 -0
- package/dist/gs/math/bits/index.js.map +1 -0
- package/dist/gs/runtime/index.d.ts +1 -0
- package/dist/gs/runtime/index.js +2 -0
- package/dist/gs/runtime/index.js.map +1 -0
- package/dist/gs/runtime/runtime.d.ts +41 -0
- package/dist/gs/runtime/runtime.js +158 -0
- package/dist/gs/runtime/runtime.js.map +1 -0
- package/dist/gs/slices/index.d.ts +1 -0
- package/dist/gs/slices/index.js +2 -0
- package/dist/gs/slices/index.js.map +1 -0
- package/dist/gs/slices/slices.d.ts +8 -0
- package/dist/gs/slices/slices.js +20 -0
- package/dist/gs/slices/slices.js.map +1 -0
- package/dist/gs/time/index.d.ts +1 -0
- package/dist/gs/time/index.js +2 -0
- package/dist/gs/time/index.js.map +1 -0
- package/dist/gs/time/time.d.ts +57 -0
- package/dist/gs/time/time.js +208 -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
|
-
}
|
package/compiler/decl.go
CHANGED
|
@@ -37,7 +37,7 @@ func (c *GoToTSCompiler) WriteDecls(decls []ast.Decl) error {
|
|
|
37
37
|
c.tsw.WriteLine("") // Add space after spec
|
|
38
38
|
}
|
|
39
39
|
default:
|
|
40
|
-
fmt.
|
|
40
|
+
return fmt.Errorf("unknown decl: %#v", decl)
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
return nil
|
|
@@ -79,6 +79,12 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
|
|
|
79
79
|
if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
|
|
80
80
|
isAsync = c.analysis.IsAsyncFunc(obj)
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
// Always make main function async (only in main package)
|
|
84
|
+
if decl.Name.Name == "main" && c.pkg.Name == "main" {
|
|
85
|
+
isAsync = true
|
|
86
|
+
}
|
|
87
|
+
|
|
82
88
|
if isAsync {
|
|
83
89
|
c.tsw.WriteLiterally("async ")
|
|
84
90
|
}
|
package/compiler/expr-call.go
CHANGED
|
@@ -111,6 +111,15 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
111
111
|
return errors.Errorf("unhandled cap call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
112
112
|
}
|
|
113
113
|
c.tsw.WriteLiterally("$.cap")
|
|
114
|
+
case "new":
|
|
115
|
+
// Translate new(T) to new T_ts()
|
|
116
|
+
if len(exp.Args) != 1 {
|
|
117
|
+
return errors.Errorf("unhandled new call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
118
|
+
}
|
|
119
|
+
c.tsw.WriteLiterally("new ")
|
|
120
|
+
c.WriteTypeExpr(exp.Args[0]) // This should write the TypeScript type T_ts
|
|
121
|
+
c.tsw.WriteLiterally("()")
|
|
122
|
+
return nil // Prevent falling through to generic argument handling
|
|
114
123
|
case "delete":
|
|
115
124
|
// Translate delete(map, key) to $.deleteMapEntry(map, key)
|
|
116
125
|
if len(exp.Args) != 2 {
|
|
@@ -205,6 +214,33 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
205
214
|
return nil // Handled make for []byte
|
|
206
215
|
}
|
|
207
216
|
|
|
217
|
+
// Check if the element type is a generic type parameter
|
|
218
|
+
if _, isTypeParam := goElemType.(*types.TypeParam); isTypeParam {
|
|
219
|
+
// This is make([]E, n) where E is a type parameter
|
|
220
|
+
c.tsw.WriteLiterally("$.makeSlice<")
|
|
221
|
+
c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type parameter
|
|
222
|
+
c.tsw.WriteLiterally(">(")
|
|
223
|
+
|
|
224
|
+
if len(exp.Args) >= 2 {
|
|
225
|
+
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
|
|
226
|
+
return err
|
|
227
|
+
}
|
|
228
|
+
if len(exp.Args) == 3 {
|
|
229
|
+
c.tsw.WriteLiterally(", ")
|
|
230
|
+
if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
|
|
231
|
+
return err
|
|
232
|
+
}
|
|
233
|
+
} else if len(exp.Args) > 3 {
|
|
234
|
+
return errors.New("makeSlice expects 2 or 3 arguments")
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
// If no length is provided, default to 0
|
|
238
|
+
c.tsw.WriteLiterally("0")
|
|
239
|
+
}
|
|
240
|
+
c.tsw.WriteLiterally(")")
|
|
241
|
+
return nil // Handled make for []E where E is type parameter
|
|
242
|
+
}
|
|
243
|
+
|
|
208
244
|
c.tsw.WriteLiterally("$.makeSlice<")
|
|
209
245
|
c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
|
|
210
246
|
c.tsw.WriteLiterally(">(")
|
|
@@ -228,6 +264,71 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
228
264
|
c.tsw.WriteLiterally(")")
|
|
229
265
|
return nil // Handled make for slice
|
|
230
266
|
}
|
|
267
|
+
|
|
268
|
+
// Handle generic type parameter make calls: make(S, len, cap) where S ~[]E
|
|
269
|
+
if ident, ok := exp.Args[0].(*ast.Ident); ok {
|
|
270
|
+
// Check if this identifier refers to a type parameter
|
|
271
|
+
if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
272
|
+
if typeName, isTypeName := obj.(*types.TypeName); isTypeName {
|
|
273
|
+
if typeParam, isTypeParam := typeName.Type().(*types.TypeParam); isTypeParam {
|
|
274
|
+
// Check if the type parameter is constrained to slice types
|
|
275
|
+
constraint := typeParam.Constraint()
|
|
276
|
+
if constraint != nil {
|
|
277
|
+
underlying := constraint.Underlying()
|
|
278
|
+
if iface, isInterface := underlying.(*types.Interface); isInterface {
|
|
279
|
+
// Check if the constraint includes slice types
|
|
280
|
+
// For constraints like ~[]E, we need to look at the type terms
|
|
281
|
+
if hasSliceConstraint(iface) {
|
|
282
|
+
// This is a generic slice type parameter
|
|
283
|
+
// We need to determine the element type from the constraint
|
|
284
|
+
elemType := getSliceElementTypeFromConstraint(iface)
|
|
285
|
+
if elemType != nil {
|
|
286
|
+
// Check if it's make(S, ...) where S constrains to []byte
|
|
287
|
+
if basicElem, isBasic := elemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
|
|
288
|
+
c.tsw.WriteLiterally("new Uint8Array(")
|
|
289
|
+
if len(exp.Args) >= 2 {
|
|
290
|
+
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
|
|
291
|
+
return err
|
|
292
|
+
}
|
|
293
|
+
// Capacity argument for make([]byte, len, cap) is ignored for new Uint8Array(len)
|
|
294
|
+
} else {
|
|
295
|
+
// If no length is provided, default to 0
|
|
296
|
+
c.tsw.WriteLiterally("0")
|
|
297
|
+
}
|
|
298
|
+
c.tsw.WriteLiterally(")")
|
|
299
|
+
return nil // Handled make for generic []byte
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
c.tsw.WriteLiterally("$.makeSlice<")
|
|
303
|
+
c.WriteGoType(elemType, GoTypeContextGeneral) // Write the element type
|
|
304
|
+
c.tsw.WriteLiterally(">(")
|
|
305
|
+
|
|
306
|
+
if len(exp.Args) >= 2 {
|
|
307
|
+
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
|
|
308
|
+
return err
|
|
309
|
+
}
|
|
310
|
+
if len(exp.Args) == 3 {
|
|
311
|
+
c.tsw.WriteLiterally(", ")
|
|
312
|
+
if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
|
|
313
|
+
return err
|
|
314
|
+
}
|
|
315
|
+
} else if len(exp.Args) > 3 {
|
|
316
|
+
return errors.New("makeSlice expects 2 or 3 arguments")
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
// If no length is provided, default to 0
|
|
320
|
+
c.tsw.WriteLiterally("0")
|
|
321
|
+
}
|
|
322
|
+
c.tsw.WriteLiterally(")")
|
|
323
|
+
return nil // Handled make for generic slice
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
231
332
|
}
|
|
232
333
|
// Fallthrough for unhandled make calls (e.g., channels)
|
|
233
334
|
return errors.New("unhandled make call")
|
|
@@ -424,7 +525,17 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
424
525
|
|
|
425
526
|
if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
|
|
426
527
|
if _, ok := funType.Underlying().(*types.Signature); ok {
|
|
427
|
-
if
|
|
528
|
+
// Check if this is a function parameter identifier that needs not-null assertion
|
|
529
|
+
if ident, isIdent := expFun.(*ast.Ident); isIdent {
|
|
530
|
+
// Check if this identifier is a function parameter
|
|
531
|
+
if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
532
|
+
if _, isVar := obj.(*types.Var); isVar {
|
|
533
|
+
// This is a variable (including function parameters)
|
|
534
|
+
// Function parameters that are function types need ! assertion
|
|
535
|
+
c.tsw.WriteLiterally("!")
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
} else if _, isNamed := funType.(*types.Named); isNamed {
|
|
428
539
|
c.tsw.WriteLiterally("!")
|
|
429
540
|
}
|
|
430
541
|
}
|
|
@@ -458,7 +569,17 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
458
569
|
|
|
459
570
|
if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
|
|
460
571
|
if _, ok := funType.Underlying().(*types.Signature); ok {
|
|
461
|
-
if
|
|
572
|
+
// Check if this is a function parameter identifier that needs not-null assertion
|
|
573
|
+
if ident, isIdent := expFun.(*ast.Ident); isIdent {
|
|
574
|
+
// Check if this identifier is a function parameter
|
|
575
|
+
if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
576
|
+
if _, isVar := obj.(*types.Var); isVar {
|
|
577
|
+
// This is a variable (including function parameters)
|
|
578
|
+
// Function parameters that are function types need ! assertion
|
|
579
|
+
c.tsw.WriteLiterally("!")
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
} else if _, isNamed := funType.(*types.Named); isNamed {
|
|
462
583
|
c.tsw.WriteLiterally("!")
|
|
463
584
|
}
|
|
464
585
|
}
|
|
@@ -477,3 +598,92 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
477
598
|
c.tsw.WriteLiterally(")")
|
|
478
599
|
return nil
|
|
479
600
|
}
|
|
601
|
+
|
|
602
|
+
// hasSliceConstraint checks if an interface constraint includes slice types
|
|
603
|
+
// For constraints like ~[]E, this returns true
|
|
604
|
+
func hasSliceConstraint(iface *types.Interface) bool {
|
|
605
|
+
// Check if the interface has type terms that include slice types
|
|
606
|
+
for i := 0; i < iface.NumEmbeddeds(); i++ {
|
|
607
|
+
embedded := iface.EmbeddedType(i)
|
|
608
|
+
if union, ok := embedded.(*types.Union); ok {
|
|
609
|
+
for j := 0; j < union.Len(); j++ {
|
|
610
|
+
term := union.Term(j)
|
|
611
|
+
if _, isSlice := term.Type().Underlying().(*types.Slice); isSlice {
|
|
612
|
+
return true
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
} else if _, isSlice := embedded.Underlying().(*types.Slice); isSlice {
|
|
616
|
+
return true
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return false
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// getSliceElementTypeFromConstraint extracts the element type from a slice constraint
|
|
623
|
+
// For constraints like ~[]E, this returns E
|
|
624
|
+
func getSliceElementTypeFromConstraint(iface *types.Interface) types.Type {
|
|
625
|
+
// Check if the interface has type terms that include slice types
|
|
626
|
+
for i := 0; i < iface.NumEmbeddeds(); i++ {
|
|
627
|
+
embedded := iface.EmbeddedType(i)
|
|
628
|
+
if union, ok := embedded.(*types.Union); ok {
|
|
629
|
+
for j := 0; j < union.Len(); j++ {
|
|
630
|
+
term := union.Term(j)
|
|
631
|
+
if sliceType, isSlice := term.Type().Underlying().(*types.Slice); isSlice {
|
|
632
|
+
return sliceType.Elem()
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
} else if sliceType, isSlice := embedded.Underlying().(*types.Slice); isSlice {
|
|
636
|
+
return sliceType.Elem()
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return nil
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// hasMixedStringByteConstraint checks if an interface constraint includes both string and []byte types
|
|
643
|
+
// For constraints like string | []byte, this returns true
|
|
644
|
+
// For pure slice constraints like ~[]E, this returns false
|
|
645
|
+
func hasMixedStringByteConstraint(iface *types.Interface) bool {
|
|
646
|
+
hasString := false
|
|
647
|
+
hasByteSlice := false
|
|
648
|
+
|
|
649
|
+
// Check if the interface has type terms that include both string and []byte
|
|
650
|
+
for i := 0; i < iface.NumEmbeddeds(); i++ {
|
|
651
|
+
embedded := iface.EmbeddedType(i)
|
|
652
|
+
if union, ok := embedded.(*types.Union); ok {
|
|
653
|
+
for j := 0; j < union.Len(); j++ {
|
|
654
|
+
term := union.Term(j)
|
|
655
|
+
termType := term.Type().Underlying()
|
|
656
|
+
|
|
657
|
+
// Check for string type
|
|
658
|
+
if basicType, isBasic := termType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
|
|
659
|
+
hasString = true
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Check for []byte type
|
|
663
|
+
if sliceType, isSlice := termType.(*types.Slice); isSlice {
|
|
664
|
+
if elemType, isBasic := sliceType.Elem().(*types.Basic); isBasic && elemType.Kind() == types.Uint8 {
|
|
665
|
+
hasByteSlice = true
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
} else {
|
|
670
|
+
// Handle non-union embedded types
|
|
671
|
+
termType := embedded.Underlying()
|
|
672
|
+
|
|
673
|
+
// Check for string type
|
|
674
|
+
if basicType, isBasic := termType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
|
|
675
|
+
hasString = true
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Check for []byte type
|
|
679
|
+
if sliceType, isSlice := termType.(*types.Slice); isSlice {
|
|
680
|
+
if elemType, isBasic := sliceType.Elem().(*types.Basic); isBasic && elemType.Kind() == types.Uint8 {
|
|
681
|
+
hasByteSlice = true
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Return true only if we have both string and []byte in the constraint
|
|
688
|
+
return hasString && hasByteSlice
|
|
689
|
+
}
|