goscript 0.0.47 → 0.0.49

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 (152) hide show
  1. package/cmd/goscript/deps.go +1 -4
  2. package/compiler/analysis.go +224 -63
  3. package/compiler/analysis_test.go +112 -0
  4. package/compiler/compiler.go +19 -87
  5. package/compiler/expr-call-type-conversion.go +148 -59
  6. package/compiler/expr-call.go +202 -10
  7. package/compiler/expr.go +5 -82
  8. package/compiler/gs_dependencies_test.go +60 -1
  9. package/compiler/spec-value.go +73 -51
  10. package/compiler/spec.go +293 -151
  11. package/compiler/stmt.go +192 -81
  12. package/dist/gs/builtin/builtin.d.ts +1 -5
  13. package/dist/gs/builtin/builtin.js +1 -34
  14. package/dist/gs/builtin/builtin.js.map +1 -1
  15. package/dist/gs/builtin/slice.js.map +1 -1
  16. package/dist/gs/fmt/fmt.js +20 -4
  17. package/dist/gs/fmt/fmt.js.map +1 -1
  18. package/dist/gs/io/fs/fs.d.ts +6 -12
  19. package/dist/gs/io/fs/fs.js +52 -67
  20. package/dist/gs/io/fs/fs.js.map +1 -1
  21. package/dist/gs/os/index.d.ts +2 -1
  22. package/dist/gs/os/index.js +1 -1
  23. package/dist/gs/os/index.js.map +1 -1
  24. package/dist/gs/os/types_js.gs.d.ts +7 -1
  25. package/dist/gs/os/types_js.gs.js +16 -1
  26. package/dist/gs/os/types_js.gs.js.map +1 -1
  27. package/dist/gs/os/types_unix.gs.js +2 -2
  28. package/dist/gs/os/types_unix.gs.js.map +1 -1
  29. package/dist/gs/reflect/index.d.ts +3 -3
  30. package/dist/gs/reflect/index.js +2 -2
  31. package/dist/gs/reflect/index.js.map +1 -1
  32. package/dist/gs/reflect/map.js +2 -2
  33. package/dist/gs/reflect/map.js.map +1 -1
  34. package/dist/gs/reflect/type.d.ts +8 -9
  35. package/dist/gs/reflect/type.js +101 -103
  36. package/dist/gs/reflect/type.js.map +1 -1
  37. package/dist/gs/reflect/types.d.ts +1 -10
  38. package/dist/gs/reflect/types.js +3 -26
  39. package/dist/gs/reflect/types.js.map +1 -1
  40. package/dist/gs/reflect/value.js +23 -23
  41. package/dist/gs/reflect/value.js.map +1 -1
  42. package/dist/gs/reflect/visiblefields.js +3 -3
  43. package/dist/gs/reflect/visiblefields.js.map +1 -1
  44. package/dist/gs/time/time.d.ts +11 -22
  45. package/dist/gs/time/time.js +29 -57
  46. package/dist/gs/time/time.js.map +1 -1
  47. package/gs/TODO.md +129 -0
  48. package/gs/builtin/builtin.ts +3 -41
  49. package/gs/builtin/slice.ts +1 -1
  50. package/gs/bytes/meta.json +10 -0
  51. package/gs/fmt/fmt.ts +18 -4
  52. package/gs/fmt/meta.json +5 -0
  53. package/gs/internal/meta.json +5 -0
  54. package/gs/io/fs/fs.ts +58 -73
  55. package/gs/io/meta.json +9 -0
  56. package/gs/maps/meta.json +6 -0
  57. package/gs/math/meta.json +5 -0
  58. package/gs/os/index.ts +8 -1
  59. package/gs/os/meta.json +15 -0
  60. package/gs/os/types_js.gs.ts +22 -1
  61. package/gs/os/types_unix.gs.ts +2 -2
  62. package/gs/path/meta.json +6 -0
  63. package/gs/reflect/function-types.test.ts +10 -10
  64. package/gs/reflect/index.ts +6 -6
  65. package/gs/reflect/map.ts +2 -2
  66. package/gs/reflect/meta.json +5 -0
  67. package/gs/reflect/type.ts +108 -103
  68. package/gs/reflect/types.ts +2 -28
  69. package/gs/reflect/value.ts +23 -23
  70. package/gs/reflect/visiblefields.ts +3 -3
  71. package/gs/strconv/meta.json +5 -0
  72. package/gs/strings/meta.json +9 -0
  73. package/gs/sync/meta.json +19 -0
  74. package/gs/time/time.ts +32 -65
  75. package/package.json +1 -1
  76. package/dist/gs/builtin/io.d.ts +0 -16
  77. package/dist/gs/builtin/io.js +0 -15
  78. package/dist/gs/builtin/io.js.map +0 -1
  79. package/dist/gs/internal/testlog/index.d.ts +0 -1
  80. package/dist/gs/internal/testlog/index.js +0 -5
  81. package/dist/gs/internal/testlog/index.js.map +0 -1
  82. package/dist/gs/maps/iter.gs.d.ts +0 -7
  83. package/dist/gs/maps/iter.gs.js +0 -65
  84. package/dist/gs/maps/iter.gs.js.map +0 -1
  85. package/dist/gs/maps/maps.gs.d.ts +0 -7
  86. package/dist/gs/maps/maps.gs.js +0 -79
  87. package/dist/gs/maps/maps.gs.js.map +0 -1
  88. package/dist/gs/reflect/abi.d.ts +0 -59
  89. package/dist/gs/reflect/abi.gs.d.ts +0 -59
  90. package/dist/gs/reflect/abi.gs.js +0 -79
  91. package/dist/gs/reflect/abi.gs.js.map +0 -1
  92. package/dist/gs/reflect/abi.js +0 -79
  93. package/dist/gs/reflect/abi.js.map +0 -1
  94. package/dist/gs/reflect/badlinkname.d.ts +0 -52
  95. package/dist/gs/reflect/badlinkname.gs.d.ts +0 -52
  96. package/dist/gs/reflect/badlinkname.gs.js +0 -72
  97. package/dist/gs/reflect/badlinkname.gs.js.map +0 -1
  98. package/dist/gs/reflect/badlinkname.js +0 -72
  99. package/dist/gs/reflect/badlinkname.js.map +0 -1
  100. package/dist/gs/reflect/deepequal.gs.d.ts +0 -25
  101. package/dist/gs/reflect/deepequal.gs.js +0 -308
  102. package/dist/gs/reflect/deepequal.gs.js.map +0 -1
  103. package/dist/gs/reflect/float32reg_generic.gs.d.ts +0 -2
  104. package/dist/gs/reflect/float32reg_generic.gs.js +0 -10
  105. package/dist/gs/reflect/float32reg_generic.gs.js.map +0 -1
  106. package/dist/gs/reflect/index.gs.d.ts +0 -1
  107. package/dist/gs/reflect/index.gs.js +0 -3
  108. package/dist/gs/reflect/index.gs.js.map +0 -1
  109. package/dist/gs/reflect/iter.gs.d.ts +0 -3
  110. package/dist/gs/reflect/iter.gs.js +0 -24
  111. package/dist/gs/reflect/iter.gs.js.map +0 -1
  112. package/dist/gs/reflect/makefunc.gs.d.ts +0 -34
  113. package/dist/gs/reflect/makefunc.gs.js +0 -288
  114. package/dist/gs/reflect/makefunc.gs.js.map +0 -1
  115. package/dist/gs/reflect/map_swiss.gs.d.ts +0 -14
  116. package/dist/gs/reflect/map_swiss.gs.js +0 -70
  117. package/dist/gs/reflect/map_swiss.gs.js.map +0 -1
  118. package/dist/gs/reflect/reflect.gs.d.ts +0 -132
  119. package/dist/gs/reflect/reflect.gs.js +0 -437
  120. package/dist/gs/reflect/reflect.gs.js.map +0 -1
  121. package/dist/gs/reflect/swapper.gs.d.ts +0 -1
  122. package/dist/gs/reflect/swapper.gs.js +0 -32
  123. package/dist/gs/reflect/swapper.gs.js.map +0 -1
  124. package/dist/gs/reflect/type.gs.d.ts +0 -4
  125. package/dist/gs/reflect/type.gs.js +0 -21
  126. package/dist/gs/reflect/type.gs.js.map +0 -1
  127. package/dist/gs/reflect/value.gs.d.ts +0 -4
  128. package/dist/gs/reflect/value.gs.js +0 -12
  129. package/dist/gs/reflect/value.gs.js.map +0 -1
  130. package/dist/gs/reflect/visiblefields.gs.d.ts +0 -3
  131. package/dist/gs/reflect/visiblefields.gs.js +0 -123
  132. package/dist/gs/reflect/visiblefields.gs.js.map +0 -1
  133. package/dist/gs/stringslite/index.d.ts +0 -1
  134. package/dist/gs/stringslite/index.js +0 -2
  135. package/dist/gs/stringslite/index.js.map +0 -1
  136. package/dist/gs/stringslite/strings.d.ts +0 -11
  137. package/dist/gs/stringslite/strings.js +0 -67
  138. package/dist/gs/stringslite/strings.js.map +0 -1
  139. package/gs/bytes/metadata.go +0 -12
  140. package/gs/fmt/metadata.go +0 -7
  141. package/gs/internal/metadata.go +0 -7
  142. package/gs/io/io.go +0 -75
  143. package/gs/io/metadata.go +0 -11
  144. package/gs/maps/metadata.go +0 -8
  145. package/gs/math/metadata.go +0 -7
  146. package/gs/os/metadata.go +0 -17
  147. package/gs/path/metadata.go +0 -8
  148. package/gs/reflect/metadata.go +0 -7
  149. package/gs/strconv/metadata.go +0 -7
  150. package/gs/strings/metadata.go +0 -11
  151. package/gs/sync/metadata.go +0 -7
  152. package/gs/sync/sync.go +0 -64
@@ -3,9 +3,6 @@ package main
3
3
  // This file has _ imports to ensure we include these in the go module.
4
4
 
5
5
  import (
6
- // _ ensure we include the gs package
6
+ // _ ensure we include the root package
7
7
  _ "github.com/aperturerobotics/goscript"
8
- // _ ensure we include the gs metadata packages
9
- _ "github.com/aperturerobotics/goscript/gs/sync"
10
- _ "github.com/aperturerobotics/goscript/gs/unicode"
11
8
  )
@@ -1,12 +1,14 @@
1
1
  package compiler
2
2
 
3
3
  import (
4
+ "encoding/json"
4
5
  "go/ast"
5
6
  "go/token"
6
7
  "go/types"
7
8
  "path/filepath"
8
9
  "strings"
9
10
 
11
+ "github.com/aperturerobotics/goscript"
10
12
  "golang.org/x/tools/go/packages"
11
13
  )
12
14
 
@@ -81,6 +83,7 @@ type NodeInfo struct {
81
83
  IsInsideFunction bool // true if this declaration is inside a function body
82
84
  IsMethodValue bool // true if this SelectorExpr is a method value that needs binding
83
85
  ShadowingInfo *ShadowingInfo // variable shadowing information for if statements
86
+ IdentifierMapping string // replacement name for this identifier (e.g., receiver -> "receiver")
84
87
  }
85
88
 
86
89
  // Analysis holds information gathered during the analysis phase of the Go code compilation.
@@ -115,6 +118,10 @@ type Analysis struct {
115
118
 
116
119
  // PackageMetadata holds package-level metadata
117
120
  PackageMetadata map[string]interface{}
121
+
122
+ // WrapperTypes tracks types that should be implemented as wrapper classes
123
+ // This includes both local types with methods and imported types that are known wrapper types
124
+ WrapperTypes map[types.Type]bool
118
125
  }
119
126
 
120
127
  // PackageAnalysis holds cross-file analysis data for a package
@@ -136,6 +143,12 @@ type PackageAnalysis struct {
136
143
  TypeCalls map[string]map[string][]string
137
144
  }
138
145
 
146
+ // GsMetadata represents the structure of a meta.json file in gs/ packages
147
+ type GsMetadata struct {
148
+ Dependencies []string `json:"dependencies,omitempty"`
149
+ AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
150
+ }
151
+
139
152
  // NewAnalysis creates a new Analysis instance.
140
153
  func NewAnalysis() *Analysis {
141
154
  return &Analysis{
@@ -147,6 +160,7 @@ func NewAnalysis() *Analysis {
147
160
  ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
148
161
  FunctionAssignments: make(map[types.Object]ast.Node),
149
162
  PackageMetadata: make(map[string]interface{}),
163
+ WrapperTypes: make(map[types.Type]bool),
150
164
  }
151
165
  }
152
166
 
@@ -1299,85 +1313,91 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
1299
1313
  return analysis
1300
1314
  }
1301
1315
 
1302
- // LoadPackageMetadata loads metadata from gs packages to determine which functions are async
1316
+ // LoadPackageMetadata loads metadata from gs packages using embedded JSON files
1303
1317
  func (a *Analysis) LoadPackageMetadata() {
1304
- // List of gs packages that have metadata
1305
- metadataPackages := []string{
1306
- "github.com/aperturerobotics/goscript/gs/sync",
1307
- "github.com/aperturerobotics/goscript/gs/unicode",
1308
- }
1309
-
1310
- for _, pkgPath := range metadataPackages {
1311
- cfg := &packages.Config{
1312
- Mode: packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
1318
+ // Use the embedded filesystem from gs.go
1319
+ // The GsOverrides embed.FS should be available but we need to import it
1320
+ // For now, let's iterate through known packages and load their meta.json files
1321
+
1322
+ // Discover all packages in the embedded gs/ directory
1323
+ packagePaths := a.discoverEmbeddedGsPackages()
1324
+
1325
+ for _, pkgPath := range packagePaths {
1326
+ metaFilePath := filepath.Join("gs", pkgPath, "meta.json")
1327
+
1328
+ // Try to read the meta.json file from embedded filesystem
1329
+ // We need access to the embedded FS, which should be imported from the parent package
1330
+ if metadata := a.loadGsMetadata(metaFilePath); metadata != nil {
1331
+ // Store async method information
1332
+ for methodKey, isAsync := range metadata.AsyncMethods {
1333
+ // Convert "Type.Method" format to our internal key format
1334
+ parts := strings.Split(methodKey, ".")
1335
+ if len(parts) == 2 {
1336
+ typeName := parts[0]
1337
+ methodName := parts[1]
1338
+ // The key format is "pkgName.TypeNameMethodNameInfo"
1339
+ key := pkgPath + "." + typeName + methodName + "Info"
1340
+
1341
+ // Store the async value directly in PackageMetadata
1342
+ a.PackageMetadata[key] = isAsync
1343
+ }
1344
+ }
1313
1345
  }
1346
+ }
1347
+ }
1314
1348
 
1315
- pkgs, err := packages.Load(cfg, pkgPath)
1316
- if err != nil || len(pkgs) == 0 {
1317
- continue // Skip if package can't be loaded
1318
- }
1349
+ // discoverEmbeddedGsPackages finds all packages in the embedded gs/ directory
1350
+ func (a *Analysis) discoverEmbeddedGsPackages() []string {
1351
+ var packageList []string
1319
1352
 
1320
- pkg := pkgs[0]
1321
- if pkg.Types == nil {
1322
- continue
1323
- }
1353
+ // Read the gs/ directory from the embedded filesystem
1354
+ entries, err := goscript.GsOverrides.ReadDir("gs")
1355
+ if err != nil {
1356
+ // If we can't read the gs/ directory, return empty list
1357
+ return packageList
1358
+ }
1324
1359
 
1325
- // Extract the package name (e.g., "sync" from "github.com/aperturerobotics/goscript/gs/sync")
1326
- parts := strings.Split(pkgPath, "/")
1327
- pkgName := parts[len(parts)-1]
1360
+ // Iterate through all entries in gs/
1361
+ for _, entry := range entries {
1362
+ if entry.IsDir() {
1363
+ packageName := entry.Name()
1328
1364
 
1329
- // Look for metadata variables in the package scope
1330
- scope := pkg.Types.Scope()
1331
- for _, name := range scope.Names() {
1332
- obj := scope.Lookup(name)
1333
- if obj == nil {
1365
+ // Skip special directories like github.com
1366
+ if strings.Contains(packageName, ".") {
1334
1367
  continue
1335
1368
  }
1336
1369
 
1337
- // Check if this is a metadata variable (ends with "Info")
1338
- if strings.HasSuffix(name, "Info") {
1339
- if varObj, ok := obj.(*types.Var); ok {
1340
- // Store the metadata with a key like "sync.MutexLock"
1341
- methodName := strings.TrimSuffix(name, "Info")
1342
- key := pkgName + "." + methodName
1343
- a.PackageMetadata[key] = varObj
1344
- }
1345
- }
1370
+ packageList = append(packageList, packageName)
1346
1371
  }
1347
1372
  }
1373
+
1374
+ return packageList
1375
+ }
1376
+
1377
+ // loadGsMetadata loads metadata from a meta.json file in the embedded filesystem
1378
+ func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
1379
+ // Read the meta.json file from the embedded filesystem
1380
+ content, err := goscript.GsOverrides.ReadFile(metaFilePath)
1381
+ if err != nil {
1382
+ return nil // No metadata file found
1383
+ }
1384
+
1385
+ var metadata GsMetadata
1386
+ if err := json.Unmarshal(content, &metadata); err != nil {
1387
+ return nil // Invalid JSON
1388
+ }
1389
+
1390
+ return &metadata
1348
1391
  }
1349
1392
 
1350
1393
  // IsMethodAsync checks if a method call is async based on package metadata
1351
1394
  func (a *Analysis) IsMethodAsync(pkgName, typeName, methodName string) bool {
1352
- // The metadata keys are stored as "sync.MutexLock", "sync.WaitGroupWait", etc.
1353
- // We need to match "sync.Mutex.Lock" -> "sync.MutexLock"
1354
- key := pkgName + "." + typeName + methodName
1355
-
1356
- if metaObj, exists := a.PackageMetadata[key]; exists {
1357
- if varObj, ok := metaObj.(*types.Var); ok {
1358
- // Try to get the actual value of the variable
1359
- // For now, we'll use the variable name to determine if it's async
1360
- // The variable names follow the pattern: MutexLockInfo, WaitGroupWaitInfo, etc.
1361
- // We can check if the corresponding metadata indicates IsAsync: true
1362
- varName := varObj.Name()
1363
-
1364
- // Based on our metadata definitions, these should be async:
1365
- asyncMethods := map[string]bool{
1366
- "MutexLockInfo": true,
1367
- "RWMutexLockInfo": true,
1368
- "RWMutexRLockInfo": true,
1369
- "WaitGroupWaitInfo": true,
1370
- "OnceDoInfo": true,
1371
- "CondWaitInfo": true,
1372
- "MapDeleteInfo": true,
1373
- "MapLoadInfo": true,
1374
- "MapLoadAndDeleteInfo": true,
1375
- "MapLoadOrStoreInfo": true,
1376
- "MapRangeInfo": true,
1377
- "MapStoreInfo": true,
1378
- }
1395
+ // The metadata keys are stored as "pkgName.TypeNameMethodNameInfo"
1396
+ // e.g., "sync.MutexLockInfo", "sync.WaitGroupWaitInfo", etc.
1397
+ key := pkgName + "." + typeName + methodName + "Info"
1379
1398
 
1380
- isAsync := asyncMethods[varName]
1399
+ if asyncValue, exists := a.PackageMetadata[key]; exists {
1400
+ if isAsync, ok := asyncValue.(bool); ok {
1381
1401
  return isAsync
1382
1402
  }
1383
1403
  }
@@ -1581,3 +1601,144 @@ func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map
1581
1601
  // For now, we'll ignore them as they're less common in shadowing scenarios
1582
1602
  }
1583
1603
  }
1604
+
1605
+ // IsWrapperType returns whether the given type should be implemented as a wrapper class
1606
+ // This includes both local types with methods and imported types that are known wrapper types
1607
+ func (a *Analysis) IsWrapperType(t types.Type) bool {
1608
+ if t == nil {
1609
+ return false
1610
+ }
1611
+
1612
+ // Check if we've already determined this type is a wrapper type
1613
+ if isWrapper, exists := a.WrapperTypes[t]; exists {
1614
+ return isWrapper
1615
+ }
1616
+
1617
+ // For named types, check if they have methods and underlying primitive types
1618
+ if namedType, ok := t.(*types.Named); ok {
1619
+ // Exclude struct types - they should remain as classes, not wrapper types
1620
+ if _, isStruct := namedType.Underlying().(*types.Struct); isStruct {
1621
+ a.WrapperTypes[t] = false
1622
+ return false
1623
+ }
1624
+
1625
+ // Exclude interface types
1626
+ if _, isInterface := namedType.Underlying().(*types.Interface); isInterface {
1627
+ a.WrapperTypes[t] = false
1628
+ return false
1629
+ }
1630
+
1631
+ // Check if this type has methods defined on it
1632
+ if namedType.NumMethods() > 0 {
1633
+ // Additional check: the underlying type should be a basic type (primitive)
1634
+ // This distinguishes wrapper types from complex types with methods
1635
+ underlying := namedType.Underlying()
1636
+
1637
+ if isBasicType(underlying) {
1638
+ a.WrapperTypes[t] = true
1639
+ return true
1640
+ }
1641
+
1642
+ // For non-basic underlying types with methods, still consider them wrapper types
1643
+ // if they're not structs or interfaces (already excluded above)
1644
+ a.WrapperTypes[t] = true
1645
+ return true
1646
+ }
1647
+ }
1648
+
1649
+ // Handle type aliases (Go 1.9+)
1650
+ if aliasType, ok := t.(*types.Alias); ok {
1651
+ // For type aliases, we need to check the RHS (right-hand side) type
1652
+ // For example, if we have: type FileMode = fs.FileMode
1653
+ // We need to check if fs.FileMode has methods
1654
+ rhs := aliasType.Rhs()
1655
+
1656
+ if rhsNamed, ok := rhs.(*types.Named); ok {
1657
+ // Check if the RHS named type has methods and a basic underlying type
1658
+ if rhsNamed.NumMethods() > 0 {
1659
+ underlying := rhsNamed.Underlying()
1660
+
1661
+ // Exclude struct types
1662
+ if _, isStruct := underlying.(*types.Struct); isStruct {
1663
+ a.WrapperTypes[t] = false
1664
+ return false
1665
+ }
1666
+
1667
+ // Exclude interface types
1668
+ if _, isInterface := underlying.(*types.Interface); isInterface {
1669
+ a.WrapperTypes[t] = false
1670
+ return false
1671
+ }
1672
+
1673
+ if isBasicType(underlying) {
1674
+ a.WrapperTypes[t] = true
1675
+ return true
1676
+ }
1677
+
1678
+ // For non-basic underlying types with methods, still consider them wrapper types
1679
+ // if they're not structs or interfaces (already excluded above)
1680
+ a.WrapperTypes[t] = true
1681
+ return true
1682
+ }
1683
+ }
1684
+
1685
+ // Fallback: check the underlying type for older Go versions or different alias patterns
1686
+ underlying := aliasType.Underlying()
1687
+ if namedUnderlying, ok := underlying.(*types.Named); ok {
1688
+ if namedUnderlying.NumMethods() > 0 && isBasicType(namedUnderlying.Underlying()) {
1689
+ a.WrapperTypes[t] = true
1690
+ return true
1691
+ }
1692
+ }
1693
+ }
1694
+
1695
+ // Cache negative result
1696
+ a.WrapperTypes[t] = false
1697
+ return false
1698
+ }
1699
+
1700
+ // isBasicType checks if a type is a basic/primitive type
1701
+ func isBasicType(t types.Type) bool {
1702
+ if t == nil {
1703
+ return false
1704
+ }
1705
+
1706
+ switch underlying := t.(type) {
1707
+ case *types.Basic:
1708
+ // Basic types like int, string, bool, etc.
1709
+ return true
1710
+ case *types.Pointer:
1711
+ // Pointers to basic types could also be considered for wrapper types
1712
+ return isBasicType(underlying.Elem())
1713
+ default:
1714
+ return false
1715
+ }
1716
+ }
1717
+
1718
+ // GetReceiverMapping returns the receiver variable mapping for a function declaration
1719
+ func (a *Analysis) GetReceiverMapping(funcDecl *ast.FuncDecl) string {
1720
+ if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
1721
+ for _, field := range funcDecl.Recv.List {
1722
+ for _, name := range field.Names {
1723
+ if name != nil && name.Name != "_" {
1724
+ return "receiver"
1725
+ }
1726
+ }
1727
+ }
1728
+ }
1729
+ return ""
1730
+ }
1731
+
1732
+ // GetIdentifierMapping returns the replacement name for an identifier
1733
+ func (a *Analysis) GetIdentifierMapping(ident *ast.Ident) string {
1734
+ if ident == nil {
1735
+ return ""
1736
+ }
1737
+
1738
+ // Check if this identifier has a mapping in NodeData
1739
+ if nodeInfo := a.NodeData[ident]; nodeInfo != nil {
1740
+ return nodeInfo.IdentifierMapping
1741
+ }
1742
+
1743
+ return ""
1744
+ }
@@ -5,6 +5,7 @@ import (
5
5
  "go/parser"
6
6
  "go/token"
7
7
  "go/types"
8
+ "os"
8
9
  "testing"
9
10
 
10
11
  "golang.org/x/tools/go/packages"
@@ -232,3 +233,114 @@ func main() {
232
233
  t.Log("")
233
234
  }
234
235
  }
236
+
237
+ // TestWrapperTypeDetection verifies that the analysis correctly identifies wrapper types
238
+ func TestWrapperTypeDetection(t *testing.T) {
239
+ // Use the actual compliance test case
240
+ testPath := "../compliance/tests/wrapper_type_args"
241
+
242
+ // Load the package using the packages config like the main compiler does
243
+ cfg := &packages.Config{
244
+ Mode: packages.NeedName |
245
+ packages.NeedFiles |
246
+ packages.NeedCompiledGoFiles |
247
+ packages.NeedImports |
248
+ packages.NeedDeps |
249
+ packages.NeedExportFile |
250
+ packages.NeedTypes |
251
+ packages.NeedSyntax |
252
+ packages.NeedTypesInfo |
253
+ packages.NeedTypesSizes,
254
+ Tests: false,
255
+ Env: append(os.Environ(), "GOOS=js", "GOARCH=wasm"),
256
+ }
257
+
258
+ pkgs, err := packages.Load(cfg, testPath)
259
+ if err != nil {
260
+ t.Fatalf("Failed to load test package: %v", err)
261
+ }
262
+
263
+ if len(pkgs) != 1 {
264
+ t.Fatalf("Expected 1 package, got %d", len(pkgs))
265
+ }
266
+
267
+ pkg := pkgs[0]
268
+ if len(pkg.Errors) > 0 {
269
+ t.Fatalf("Package has errors: %v", pkg.Errors[0])
270
+ }
271
+
272
+ // Run analysis on the first file
273
+ if len(pkg.Syntax) == 0 {
274
+ t.Fatal("No syntax files found")
275
+ }
276
+
277
+ analysis := NewAnalysis()
278
+ cmap := ast.NewCommentMap(pkg.Fset, pkg.Syntax[0], pkg.Syntax[0].Comments)
279
+ AnalyzeFile(pkg.Syntax[0], pkg, analysis, cmap)
280
+
281
+ // Verify the WrapperTypes map was initialized
282
+ if analysis.WrapperTypes == nil {
283
+ t.Error("WrapperTypes map was not initialized")
284
+ }
285
+
286
+ // Test some type lookups to verify wrapper type detection works
287
+ // We'll check if MyMode (which has methods) is detected as a wrapper type
288
+ scope := pkg.Types.Scope()
289
+
290
+ // Check if MyMode is detected as a wrapper type
291
+ if obj := scope.Lookup("MyMode"); obj != nil {
292
+ if typeName, ok := obj.(*types.TypeName); ok {
293
+ isWrapper := analysis.IsWrapperType(typeName.Type())
294
+ if !isWrapper {
295
+ t.Errorf("MyMode should be detected as wrapper type, got %v", isWrapper)
296
+ }
297
+ t.Logf("MyMode wrapper detection: %v (correct)", isWrapper)
298
+ }
299
+ }
300
+
301
+ // Test that regular struct types are not detected as wrapper types
302
+ if obj := scope.Lookup("MyDir"); obj != nil {
303
+ if typeName, ok := obj.(*types.TypeName); ok {
304
+ isWrapper := analysis.IsWrapperType(typeName.Type())
305
+ if isWrapper {
306
+ t.Errorf("MyDir should not be detected as wrapper type, got %v", isWrapper)
307
+ }
308
+ t.Logf("MyDir wrapper detection: %v (correct)", isWrapper)
309
+ }
310
+ }
311
+
312
+ t.Logf("Analysis completed successfully with %d wrapper types tracked", len(analysis.WrapperTypes))
313
+ }
314
+
315
+ // TestDiscoverGsPackages verifies that the discoverEmbeddedGsPackages function
316
+ // can find packages in the embedded gs/ directory
317
+ func TestDiscoverGsPackages(t *testing.T) {
318
+ analysis := NewAnalysis()
319
+
320
+ // Test package discovery using the embedded filesystem
321
+ packages := analysis.discoverEmbeddedGsPackages()
322
+ t.Logf("Discovered %d packages:", len(packages))
323
+ for _, pkg := range packages {
324
+ t.Logf(" - %s", pkg)
325
+ }
326
+
327
+ // We should find at least some packages
328
+ if len(packages) == 0 {
329
+ t.Errorf("Expected to find at least one package in gs/ directory")
330
+ }
331
+
332
+ // Check for some known packages that should exist
333
+ expectedPackages := []string{"sync", "bytes", "strings"}
334
+ for _, expected := range expectedPackages {
335
+ found := false
336
+ for _, pkg := range packages {
337
+ if pkg == expected {
338
+ found = true
339
+ break
340
+ }
341
+ }
342
+ if !found {
343
+ t.Logf("Expected package '%s' not found in discovered packages: %v", expected, packages)
344
+ }
345
+ }
346
+ }
@@ -2,10 +2,10 @@ package compiler
2
2
 
3
3
  import (
4
4
  "context"
5
+ "encoding/json"
5
6
  "fmt"
6
7
  "go/ast"
7
8
  "go/constant"
8
- "go/parser"
9
9
  "go/token"
10
10
  "go/types"
11
11
  "os"
@@ -644,9 +644,6 @@ type GoToTSCompiler struct {
644
644
  pkg *packages.Package
645
645
 
646
646
  analysis *Analysis
647
-
648
- // shadowingContext tracks temporary variable mappings when we're in a shadowing context
649
- shadowingContext map[string]string
650
647
  }
651
648
 
652
649
  // It initializes the compiler with a `TSCodeWriter` for output,
@@ -654,10 +651,9 @@ type GoToTSCompiler struct {
654
651
  // analysis results (`Analysis`) to guide the translation process.
655
652
  func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis) *GoToTSCompiler {
656
653
  return &GoToTSCompiler{
657
- tsw: tsw,
658
- pkg: pkg,
659
- analysis: analysis,
660
- shadowingContext: make(map[string]string),
654
+ tsw: tsw,
655
+ pkg: pkg,
656
+ analysis: analysis,
661
657
  }
662
658
  }
663
659
 
@@ -679,9 +675,9 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
679
675
  return
680
676
  }
681
677
 
682
- // Check if we're in a shadowing context and should use a temporary variable
683
- if tempVarName, exists := c.shadowingContext[exp.Name]; exists {
684
- c.tsw.WriteLiterally(c.sanitizeIdentifier(tempVarName))
678
+ // Check if this identifier has a pre-computed mapping (e.g., wrapper function receiver)
679
+ if mappedName := c.analysis.GetIdentifierMapping(exp); mappedName != "" {
680
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(mappedName))
685
681
  return
686
682
  }
687
683
 
@@ -1036,97 +1032,33 @@ func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) e
1036
1032
  // GsPackageMetadata holds metadata about a gs/ package
1037
1033
  type GsPackageMetadata struct {
1038
1034
  // Dependencies lists the import paths that this gs/ package requires
1039
- Dependencies []string
1035
+ Dependencies []string `json:"dependencies,omitempty"`
1036
+ AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
1040
1037
  }
1041
1038
 
1042
- // ReadGsPackageMetadata reads dependency metadata from .go files in a gs/ package
1043
- // It looks for a var variable named "GsDependencies" which should be a slice of strings
1044
- // containing the import paths that this package depends on.
1039
+ // ReadGsPackageMetadata reads dependency metadata from meta.json file in a gs/ package
1045
1040
  func (c *Compiler) ReadGsPackageMetadata(gsSourcePath string) (*GsPackageMetadata, error) {
1046
1041
  metadata := &GsPackageMetadata{
1047
1042
  Dependencies: []string{},
1043
+ AsyncMethods: make(map[string]bool),
1048
1044
  }
1049
1045
 
1050
- // Check if there are any .go files in the gs package directory
1051
- entries, err := gs.GsOverrides.ReadDir(gsSourcePath)
1046
+ // Try to read meta.json file
1047
+ metaFilePath := filepath.Join(gsSourcePath, "meta.json")
1048
+ content, err := gs.GsOverrides.ReadFile(metaFilePath)
1052
1049
  if err != nil {
1053
- return metadata, nil // No metadata files, return empty metadata
1050
+ // No meta.json file found, return empty metadata
1051
+ return metadata, nil
1054
1052
  }
1055
1053
 
1056
- // Look for .go files containing metadata
1057
- for _, entry := range entries {
1058
- if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".go") {
1059
- metadataFilePath := filepath.Join(gsSourcePath, entry.Name())
1060
-
1061
- // Read the .go file content
1062
- content, err := gs.GsOverrides.ReadFile(metadataFilePath)
1063
- if err != nil {
1064
- continue // Skip files we can't read
1065
- }
1066
-
1067
- // Parse the file to extract metadata
1068
- if deps, err := c.extractDependenciesFromGoFile(content); err == nil {
1069
- metadata.Dependencies = append(metadata.Dependencies, deps...)
1070
- }
1071
- }
1054
+ // Parse the JSON content
1055
+ if err := json.Unmarshal(content, metadata); err != nil {
1056
+ return metadata, fmt.Errorf("failed to parse meta.json in %s: %w", gsSourcePath, err)
1072
1057
  }
1073
1058
 
1074
1059
  return metadata, nil
1075
1060
  }
1076
1061
 
1077
- // extractDependenciesFromGoFile parses a .go file and extracts the GsDependencies var
1078
- func (c *Compiler) extractDependenciesFromGoFile(content []byte) ([]string, error) {
1079
- // Parse the Go file
1080
- fset := token.NewFileSet()
1081
- file, err := parser.ParseFile(fset, "metadata.go", content, 0)
1082
- if err != nil {
1083
- return nil, err
1084
- }
1085
-
1086
- var dependencies []string
1087
-
1088
- // Look for var declarations
1089
- for _, decl := range file.Decls {
1090
- if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.VAR {
1091
- for _, spec := range genDecl.Specs {
1092
- if valueSpec, ok := spec.(*ast.ValueSpec); ok {
1093
- for i, name := range valueSpec.Names {
1094
- if name.Name == "GsDependencies" {
1095
- // Found the GsDependencies var, extract its value
1096
- if i < len(valueSpec.Values) {
1097
- if deps := c.extractStringSliceFromExpr(valueSpec.Values[i]); deps != nil {
1098
- dependencies = append(dependencies, deps...)
1099
- }
1100
- }
1101
- }
1102
- }
1103
- }
1104
- }
1105
- }
1106
- }
1107
-
1108
- return dependencies, nil
1109
- }
1110
-
1111
- // extractStringSliceFromExpr extracts string values from a composite literal expression
1112
- func (c *Compiler) extractStringSliceFromExpr(expr ast.Expr) []string {
1113
- var result []string
1114
-
1115
- if compLit, ok := expr.(*ast.CompositeLit); ok {
1116
- for _, elt := range compLit.Elts {
1117
- if basicLit, ok := elt.(*ast.BasicLit); ok && basicLit.Kind == token.STRING {
1118
- // Remove quotes from string literal
1119
- value := basicLit.Value
1120
- if len(value) >= 2 && value[0] == '"' && value[len(value)-1] == '"' {
1121
- result = append(result, value[1:len(value)-1])
1122
- }
1123
- }
1124
- }
1125
- }
1126
-
1127
- return result
1128
- }
1129
-
1130
1062
  // copyGsPackageWithDependencies copies a gs/ package and all its dependencies recursively
1131
1063
  // It tracks already processed packages to avoid infinite loops and duplicate work
1132
1064
  func (c *Compiler) copyGsPackageWithDependencies(packagePath string, processedPackages map[string]bool, result *CompilationResult) error {