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.
- package/cmd/goscript/deps.go +1 -4
- package/compiler/analysis.go +224 -63
- package/compiler/analysis_test.go +112 -0
- package/compiler/compiler.go +19 -87
- package/compiler/expr-call-type-conversion.go +148 -59
- package/compiler/expr-call.go +202 -10
- package/compiler/expr.go +5 -82
- package/compiler/gs_dependencies_test.go +60 -1
- package/compiler/spec-value.go +73 -51
- package/compiler/spec.go +293 -151
- package/compiler/stmt.go +192 -81
- package/dist/gs/builtin/builtin.d.ts +1 -5
- package/dist/gs/builtin/builtin.js +1 -34
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/fmt/fmt.js +20 -4
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/io/fs/fs.d.ts +6 -12
- package/dist/gs/io/fs/fs.js +52 -67
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/os/index.d.ts +2 -1
- package/dist/gs/os/index.js +1 -1
- package/dist/gs/os/index.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +7 -1
- package/dist/gs/os/types_js.gs.js +16 -1
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js +2 -2
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +3 -3
- package/dist/gs/reflect/index.js +2 -2
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js +2 -2
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +8 -9
- package/dist/gs/reflect/type.js +101 -103
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.d.ts +1 -10
- package/dist/gs/reflect/types.js +3 -26
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/value.js +23 -23
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js +3 -3
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/time/time.d.ts +11 -22
- package/dist/gs/time/time.js +29 -57
- package/dist/gs/time/time.js.map +1 -1
- package/gs/TODO.md +129 -0
- package/gs/builtin/builtin.ts +3 -41
- package/gs/builtin/slice.ts +1 -1
- package/gs/bytes/meta.json +10 -0
- package/gs/fmt/fmt.ts +18 -4
- package/gs/fmt/meta.json +5 -0
- package/gs/internal/meta.json +5 -0
- package/gs/io/fs/fs.ts +58 -73
- package/gs/io/meta.json +9 -0
- package/gs/maps/meta.json +6 -0
- package/gs/math/meta.json +5 -0
- package/gs/os/index.ts +8 -1
- package/gs/os/meta.json +15 -0
- package/gs/os/types_js.gs.ts +22 -1
- package/gs/os/types_unix.gs.ts +2 -2
- package/gs/path/meta.json +6 -0
- package/gs/reflect/function-types.test.ts +10 -10
- package/gs/reflect/index.ts +6 -6
- package/gs/reflect/map.ts +2 -2
- package/gs/reflect/meta.json +5 -0
- package/gs/reflect/type.ts +108 -103
- package/gs/reflect/types.ts +2 -28
- package/gs/reflect/value.ts +23 -23
- package/gs/reflect/visiblefields.ts +3 -3
- package/gs/strconv/meta.json +5 -0
- package/gs/strings/meta.json +9 -0
- package/gs/sync/meta.json +19 -0
- package/gs/time/time.ts +32 -65
- package/package.json +1 -1
- package/dist/gs/builtin/io.d.ts +0 -16
- package/dist/gs/builtin/io.js +0 -15
- package/dist/gs/builtin/io.js.map +0 -1
- package/dist/gs/internal/testlog/index.d.ts +0 -1
- package/dist/gs/internal/testlog/index.js +0 -5
- package/dist/gs/internal/testlog/index.js.map +0 -1
- package/dist/gs/maps/iter.gs.d.ts +0 -7
- package/dist/gs/maps/iter.gs.js +0 -65
- package/dist/gs/maps/iter.gs.js.map +0 -1
- package/dist/gs/maps/maps.gs.d.ts +0 -7
- package/dist/gs/maps/maps.gs.js +0 -79
- package/dist/gs/maps/maps.gs.js.map +0 -1
- package/dist/gs/reflect/abi.d.ts +0 -59
- package/dist/gs/reflect/abi.gs.d.ts +0 -59
- package/dist/gs/reflect/abi.gs.js +0 -79
- package/dist/gs/reflect/abi.gs.js.map +0 -1
- package/dist/gs/reflect/abi.js +0 -79
- package/dist/gs/reflect/abi.js.map +0 -1
- package/dist/gs/reflect/badlinkname.d.ts +0 -52
- package/dist/gs/reflect/badlinkname.gs.d.ts +0 -52
- package/dist/gs/reflect/badlinkname.gs.js +0 -72
- package/dist/gs/reflect/badlinkname.gs.js.map +0 -1
- package/dist/gs/reflect/badlinkname.js +0 -72
- package/dist/gs/reflect/badlinkname.js.map +0 -1
- package/dist/gs/reflect/deepequal.gs.d.ts +0 -25
- package/dist/gs/reflect/deepequal.gs.js +0 -308
- package/dist/gs/reflect/deepequal.gs.js.map +0 -1
- package/dist/gs/reflect/float32reg_generic.gs.d.ts +0 -2
- package/dist/gs/reflect/float32reg_generic.gs.js +0 -10
- package/dist/gs/reflect/float32reg_generic.gs.js.map +0 -1
- package/dist/gs/reflect/index.gs.d.ts +0 -1
- package/dist/gs/reflect/index.gs.js +0 -3
- package/dist/gs/reflect/index.gs.js.map +0 -1
- package/dist/gs/reflect/iter.gs.d.ts +0 -3
- package/dist/gs/reflect/iter.gs.js +0 -24
- package/dist/gs/reflect/iter.gs.js.map +0 -1
- package/dist/gs/reflect/makefunc.gs.d.ts +0 -34
- package/dist/gs/reflect/makefunc.gs.js +0 -288
- package/dist/gs/reflect/makefunc.gs.js.map +0 -1
- package/dist/gs/reflect/map_swiss.gs.d.ts +0 -14
- package/dist/gs/reflect/map_swiss.gs.js +0 -70
- package/dist/gs/reflect/map_swiss.gs.js.map +0 -1
- package/dist/gs/reflect/reflect.gs.d.ts +0 -132
- package/dist/gs/reflect/reflect.gs.js +0 -437
- package/dist/gs/reflect/reflect.gs.js.map +0 -1
- package/dist/gs/reflect/swapper.gs.d.ts +0 -1
- package/dist/gs/reflect/swapper.gs.js +0 -32
- package/dist/gs/reflect/swapper.gs.js.map +0 -1
- package/dist/gs/reflect/type.gs.d.ts +0 -4
- package/dist/gs/reflect/type.gs.js +0 -21
- package/dist/gs/reflect/type.gs.js.map +0 -1
- package/dist/gs/reflect/value.gs.d.ts +0 -4
- package/dist/gs/reflect/value.gs.js +0 -12
- package/dist/gs/reflect/value.gs.js.map +0 -1
- package/dist/gs/reflect/visiblefields.gs.d.ts +0 -3
- package/dist/gs/reflect/visiblefields.gs.js +0 -123
- package/dist/gs/reflect/visiblefields.gs.js.map +0 -1
- package/dist/gs/stringslite/index.d.ts +0 -1
- package/dist/gs/stringslite/index.js +0 -2
- package/dist/gs/stringslite/index.js.map +0 -1
- package/dist/gs/stringslite/strings.d.ts +0 -11
- package/dist/gs/stringslite/strings.js +0 -67
- package/dist/gs/stringslite/strings.js.map +0 -1
- package/gs/bytes/metadata.go +0 -12
- package/gs/fmt/metadata.go +0 -7
- package/gs/internal/metadata.go +0 -7
- package/gs/io/io.go +0 -75
- package/gs/io/metadata.go +0 -11
- package/gs/maps/metadata.go +0 -8
- package/gs/math/metadata.go +0 -7
- package/gs/os/metadata.go +0 -17
- package/gs/path/metadata.go +0 -8
- package/gs/reflect/metadata.go +0 -7
- package/gs/strconv/metadata.go +0 -7
- package/gs/strings/metadata.go +0 -11
- package/gs/sync/metadata.go +0 -7
- package/gs/sync/sync.go +0 -64
package/cmd/goscript/deps.go
CHANGED
|
@@ -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
|
|
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
|
)
|
package/compiler/analysis.go
CHANGED
|
@@ -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
|
|
1316
|
+
// LoadPackageMetadata loads metadata from gs packages using embedded JSON files
|
|
1303
1317
|
func (a *Analysis) LoadPackageMetadata() {
|
|
1304
|
-
//
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
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
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}
|
|
1349
|
+
// discoverEmbeddedGsPackages finds all packages in the embedded gs/ directory
|
|
1350
|
+
func (a *Analysis) discoverEmbeddedGsPackages() []string {
|
|
1351
|
+
var packageList []string
|
|
1319
1352
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
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
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1360
|
+
// Iterate through all entries in gs/
|
|
1361
|
+
for _, entry := range entries {
|
|
1362
|
+
if entry.IsDir() {
|
|
1363
|
+
packageName := entry.Name()
|
|
1328
1364
|
|
|
1329
|
-
|
|
1330
|
-
|
|
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
|
-
|
|
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 "
|
|
1353
|
-
//
|
|
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
|
-
|
|
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
|
+
}
|
package/compiler/compiler.go
CHANGED
|
@@ -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:
|
|
658
|
-
pkg:
|
|
659
|
-
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
|
|
683
|
-
if
|
|
684
|
-
c.tsw.WriteLiterally(c.sanitizeIdentifier(
|
|
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 .
|
|
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
|
-
//
|
|
1051
|
-
|
|
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
|
-
|
|
1050
|
+
// No meta.json file found, return empty metadata
|
|
1051
|
+
return metadata, nil
|
|
1054
1052
|
}
|
|
1055
1053
|
|
|
1056
|
-
//
|
|
1057
|
-
|
|
1058
|
-
|
|
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 {
|