goscript 0.0.22 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cmd/goscript/cmd_compile.go +3 -3
- package/compiler/analysis.go +302 -182
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +42 -43
- package/compiler/builtin_test.go +102 -0
- package/compiler/compiler.go +117 -29
- package/compiler/compiler_test.go +36 -8
- package/compiler/composite-lit.go +133 -53
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/decl.go +36 -0
- package/compiler/expr-call.go +116 -60
- package/compiler/expr-selector.go +88 -43
- package/compiler/expr-star.go +57 -65
- package/compiler/expr-type.go +132 -5
- package/compiler/expr-value.go +8 -38
- package/compiler/expr.go +326 -30
- package/compiler/field.go +3 -3
- package/compiler/lit.go +34 -2
- package/compiler/primitive.go +19 -12
- package/compiler/spec-struct.go +140 -9
- package/compiler/spec-value.go +119 -41
- package/compiler/spec.go +21 -6
- package/compiler/stmt-assign.go +65 -3
- package/compiler/stmt-for.go +11 -0
- package/compiler/stmt-range.go +119 -11
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +175 -238
- package/compiler/type-assert.go +125 -379
- package/compiler/type.go +216 -129
- package/dist/gs/builtin/builtin.js +37 -0
- package/dist/gs/builtin/builtin.js.map +1 -0
- package/dist/gs/builtin/channel.js +471 -0
- package/dist/gs/builtin/channel.js.map +1 -0
- package/dist/gs/builtin/defer.js +54 -0
- package/dist/gs/builtin/defer.js.map +1 -0
- package/dist/gs/builtin/io.js +15 -0
- package/dist/gs/builtin/io.js.map +1 -0
- package/dist/gs/builtin/map.js +44 -0
- package/dist/gs/builtin/map.js.map +1 -0
- package/dist/gs/builtin/slice.js +799 -0
- package/dist/gs/builtin/slice.js.map +1 -0
- package/dist/gs/builtin/type.js +745 -0
- package/dist/gs/builtin/type.js.map +1 -0
- package/dist/gs/builtin/varRef.js +14 -0
- package/dist/gs/builtin/varRef.js.map +1 -0
- package/dist/gs/context/context.js +55 -0
- package/dist/gs/context/context.js.map +1 -0
- package/dist/gs/context/index.js +2 -0
- package/dist/gs/context/index.js.map +1 -0
- package/dist/gs/runtime/index.js +2 -0
- package/dist/gs/runtime/index.js.map +1 -0
- package/dist/gs/runtime/runtime.js +158 -0
- package/dist/gs/runtime/runtime.js.map +1 -0
- package/dist/gs/time/index.js +2 -0
- package/dist/gs/time/index.js.map +1 -0
- package/dist/gs/time/time.js +115 -0
- package/dist/gs/time/time.js.map +1 -0
- package/package.json +7 -6
- package/builtin/builtin.go +0 -11
- package/builtin/builtin.ts +0 -2379
- package/dist/builtin/builtin.d.ts +0 -513
- package/dist/builtin/builtin.js +0 -1686
- package/dist/builtin/builtin.js.map +0 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"go/ast"
|
|
5
|
+
"go/parser"
|
|
6
|
+
"go/token"
|
|
7
|
+
"go/types"
|
|
8
|
+
"testing"
|
|
9
|
+
|
|
10
|
+
"golang.org/x/tools/go/packages"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// TestAnalysisVarRefLogic verifies that the analysis correctly identifies
|
|
14
|
+
// which variables need variable references based on actual compliance test cases
|
|
15
|
+
func TestAnalysisVarRefLogic(t *testing.T) {
|
|
16
|
+
tests := []struct {
|
|
17
|
+
name string
|
|
18
|
+
code string
|
|
19
|
+
expected map[string]AnalysisExpectation
|
|
20
|
+
}{
|
|
21
|
+
{
|
|
22
|
+
name: "pointer_range_loop",
|
|
23
|
+
code: `package main
|
|
24
|
+
func main() {
|
|
25
|
+
arr := [3]int{1, 2, 3}
|
|
26
|
+
arrPtr := &arr
|
|
27
|
+
for i, v := range arrPtr {
|
|
28
|
+
println("index:", i, "value:", v)
|
|
29
|
+
}
|
|
30
|
+
}`,
|
|
31
|
+
expected: map[string]AnalysisExpectation{
|
|
32
|
+
"arr": {NeedsVarRef: true, NeedsVarRefAccess: true}, // varrefed because &arr is taken
|
|
33
|
+
"arrPtr": {NeedsVarRef: false, NeedsVarRefAccess: true}, // NOT varrefed, but points to varrefed value
|
|
34
|
+
"i": {NeedsVarRef: false, NeedsVarRefAccess: false}, // regular loop variable
|
|
35
|
+
"v": {NeedsVarRef: false, NeedsVarRefAccess: false}, // regular loop variable
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "simple_pointers",
|
|
40
|
+
code: `package main
|
|
41
|
+
type MyStruct struct {
|
|
42
|
+
Val int
|
|
43
|
+
}
|
|
44
|
+
func main() {
|
|
45
|
+
s1 := MyStruct{Val: 1}
|
|
46
|
+
p1 := &s1
|
|
47
|
+
pp1 := &p1
|
|
48
|
+
p4 := &s1
|
|
49
|
+
_ = p4
|
|
50
|
+
_ = pp1
|
|
51
|
+
}`,
|
|
52
|
+
expected: map[string]AnalysisExpectation{
|
|
53
|
+
"s1": {NeedsVarRef: true, NeedsVarRefAccess: true}, // varrefed because &s1 is taken
|
|
54
|
+
"p1": {NeedsVarRef: true, NeedsVarRefAccess: true}, // varrefed because &p1 is taken
|
|
55
|
+
"pp1": {NeedsVarRef: false, NeedsVarRefAccess: true}, // NOT varrefed, points to varrefed value
|
|
56
|
+
"p4": {NeedsVarRef: false, NeedsVarRefAccess: true}, // NOT varrefed, points to varrefed value
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "varref_deref_struct",
|
|
61
|
+
code: `package main
|
|
62
|
+
type MyStruct struct {
|
|
63
|
+
MyInt int
|
|
64
|
+
}
|
|
65
|
+
func main() {
|
|
66
|
+
myStruct := &MyStruct{}
|
|
67
|
+
(*myStruct).MyInt = 5
|
|
68
|
+
println((*myStruct).MyInt)
|
|
69
|
+
}`,
|
|
70
|
+
expected: map[string]AnalysisExpectation{
|
|
71
|
+
"myStruct": {NeedsVarRef: false, NeedsVarRefAccess: false}, // NOT varrefed, direct pointer to struct
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "pointer_composite_literal_untyped",
|
|
76
|
+
code: `package main
|
|
77
|
+
func main() {
|
|
78
|
+
var ptr *struct{ x int }
|
|
79
|
+
ptr = &struct{ x int }{42}
|
|
80
|
+
println("Pointer value x:", ptr.x)
|
|
81
|
+
|
|
82
|
+
data := []*struct{ x int }{{42}, {43}}
|
|
83
|
+
println("First element x:", data[0].x)
|
|
84
|
+
println("Second element x:", data[1].x)
|
|
85
|
+
}`,
|
|
86
|
+
expected: map[string]AnalysisExpectation{
|
|
87
|
+
"ptr": {NeedsVarRef: false, NeedsVarRefAccess: false}, // Should NOT be varrefed
|
|
88
|
+
"data": {NeedsVarRef: false, NeedsVarRefAccess: false}, // Should NOT be varrefed
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for _, tt := range tests {
|
|
94
|
+
t.Run(tt.name, func(t *testing.T) {
|
|
95
|
+
analysis, objects := parseAndAnalyze(t, tt.code)
|
|
96
|
+
|
|
97
|
+
for varName, expected := range tt.expected {
|
|
98
|
+
obj, exists := objects[varName]
|
|
99
|
+
if !exists {
|
|
100
|
+
t.Errorf("Variable %q not found in parsed objects", varName)
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
actualNeedsVarRef := analysis.NeedsVarRef(obj)
|
|
105
|
+
actualNeedsVarRefAccess := analysis.NeedsVarRefAccess(obj)
|
|
106
|
+
|
|
107
|
+
if actualNeedsVarRef != expected.NeedsVarRef {
|
|
108
|
+
t.Errorf("Variable %q: NeedsVarRef = %v, want %v",
|
|
109
|
+
varName, actualNeedsVarRef, expected.NeedsVarRef)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if actualNeedsVarRefAccess != expected.NeedsVarRefAccess {
|
|
113
|
+
t.Errorf("Variable %q: NeedsVarRefAccess = %v, want %v",
|
|
114
|
+
varName, actualNeedsVarRefAccess, expected.NeedsVarRefAccess)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Print debug info
|
|
118
|
+
t.Logf("Variable %q: NeedsVarRef=%v, NeedsVarRefAccess=%v",
|
|
119
|
+
varName, actualNeedsVarRef, actualNeedsVarRefAccess)
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// AnalysisExpectation defines what we expect from the analysis for a variable
|
|
126
|
+
type AnalysisExpectation struct {
|
|
127
|
+
NeedsVarRef bool
|
|
128
|
+
NeedsVarRefAccess bool
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// parseAndAnalyze parses Go code and runs the analysis, returning the analysis and variable objects
|
|
132
|
+
func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Object) {
|
|
133
|
+
// Parse the code
|
|
134
|
+
fset := token.NewFileSet()
|
|
135
|
+
file, err := parser.ParseFile(fset, "test.go", code, parser.ParseComments)
|
|
136
|
+
if err != nil {
|
|
137
|
+
t.Fatalf("Failed to parse code: %v", err)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Create a minimal package for type checking
|
|
141
|
+
pkg := &packages.Package{
|
|
142
|
+
Syntax: []*ast.File{file},
|
|
143
|
+
TypesInfo: &types.Info{
|
|
144
|
+
Types: make(map[ast.Expr]types.TypeAndValue),
|
|
145
|
+
Defs: make(map[*ast.Ident]types.Object),
|
|
146
|
+
Uses: make(map[*ast.Ident]types.Object),
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Type check the package
|
|
151
|
+
typeConfig := &types.Config{}
|
|
152
|
+
typePkg, err := typeConfig.Check("main", fset, pkg.Syntax, pkg.TypesInfo)
|
|
153
|
+
if err != nil {
|
|
154
|
+
t.Fatalf("Failed to type check: %v", err)
|
|
155
|
+
}
|
|
156
|
+
pkg.Types = typePkg
|
|
157
|
+
|
|
158
|
+
// Run analysis
|
|
159
|
+
analysis := NewAnalysis()
|
|
160
|
+
cmap := ast.NewCommentMap(fset, file, file.Comments)
|
|
161
|
+
AnalyzeFile(file, pkg, analysis, cmap)
|
|
162
|
+
|
|
163
|
+
// Collect variable objects
|
|
164
|
+
objects := make(map[string]types.Object)
|
|
165
|
+
for ident, obj := range pkg.TypesInfo.Defs {
|
|
166
|
+
if obj != nil && ident.Name != "_" {
|
|
167
|
+
if _, isVar := obj.(*types.Var); isVar {
|
|
168
|
+
objects[ident.Name] = obj
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return analysis, objects
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// TestAnalysisDebugInfo prints debug information about variable usage for manual inspection
|
|
177
|
+
func TestAnalysisDebugInfo(t *testing.T) {
|
|
178
|
+
code := `package main
|
|
179
|
+
func main() {
|
|
180
|
+
arr := [3]int{1, 2, 3}
|
|
181
|
+
arrPtr := &arr
|
|
182
|
+
for i, v := range arrPtr {
|
|
183
|
+
println("index:", i, "value:", v)
|
|
184
|
+
}
|
|
185
|
+
}`
|
|
186
|
+
|
|
187
|
+
analysis, objects := parseAndAnalyze(t, code)
|
|
188
|
+
|
|
189
|
+
t.Log("=== Analysis Debug Information ===")
|
|
190
|
+
for varName, obj := range objects {
|
|
191
|
+
needsVarRef := analysis.NeedsVarRef(obj)
|
|
192
|
+
needsVarRefAccess := analysis.NeedsVarRefAccess(obj)
|
|
193
|
+
|
|
194
|
+
t.Logf("Variable: %s", varName)
|
|
195
|
+
t.Logf(" Type: %s", obj.Type())
|
|
196
|
+
t.Logf(" NeedsVarRef: %v", needsVarRef)
|
|
197
|
+
t.Logf(" NeedsVarRefAccess: %v", needsVarRefAccess)
|
|
198
|
+
|
|
199
|
+
// Print usage info if available
|
|
200
|
+
if usage, exists := analysis.VariableUsage[obj]; exists {
|
|
201
|
+
t.Logf(" Sources: %d", len(usage.Sources))
|
|
202
|
+
for i, src := range usage.Sources {
|
|
203
|
+
srcName := "nil"
|
|
204
|
+
if src.Object != nil {
|
|
205
|
+
srcName = src.Object.Name()
|
|
206
|
+
}
|
|
207
|
+
t.Logf(" [%d] %s (type: %v)", i, srcName, src.Type)
|
|
208
|
+
}
|
|
209
|
+
t.Logf(" Destinations: %d", len(usage.Destinations))
|
|
210
|
+
for i, dst := range usage.Destinations {
|
|
211
|
+
dstName := "nil"
|
|
212
|
+
if dst.Object != nil {
|
|
213
|
+
dstName = dst.Object.Name()
|
|
214
|
+
}
|
|
215
|
+
t.Logf(" [%d] %s (type: %v)", i, dstName, dst.Type)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
t.Log("")
|
|
219
|
+
}
|
|
220
|
+
}
|
package/compiler/assignment.go
CHANGED
|
@@ -21,7 +21,7 @@ import (
|
|
|
21
21
|
// to `$.mapSet(myMap_ts, key_ts, value_ts)`.
|
|
22
22
|
// - Other single-variable assignments (`variable = value`):
|
|
23
23
|
// - The LHS expression is written (caller typically ensures `.value` is appended
|
|
24
|
-
// if assigning to a
|
|
24
|
+
// if assigning to a VarRefed variable's content).
|
|
25
25
|
// - The Go assignment token (`tok`, e.g., `=`, `+=`) is translated to its
|
|
26
26
|
// TypeScript equivalent using `TokenToTs`.
|
|
27
27
|
// - The RHS expression(s) are written. If `shouldApplyClone` indicates the RHS
|
|
@@ -68,10 +68,10 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
68
68
|
return nil
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
// Special handling for
|
|
71
|
+
// Special handling for variable referenced variables in declarations
|
|
72
72
|
if addDeclaration && tok == token.DEFINE {
|
|
73
|
-
// Determine if LHS is
|
|
74
|
-
|
|
73
|
+
// Determine if LHS is variable referenced
|
|
74
|
+
isLHSVarRefed := false
|
|
75
75
|
var lhsIdent *ast.Ident
|
|
76
76
|
var lhsObj types.Object
|
|
77
77
|
|
|
@@ -84,30 +84,22 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
84
84
|
lhsObj = def
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
// Check if this variable needs to be
|
|
88
|
-
if lhsObj != nil && c.analysis.
|
|
89
|
-
|
|
87
|
+
// Check if this variable needs to be variable referenced
|
|
88
|
+
if lhsObj != nil && c.analysis.NeedsVarRef(lhsObj) {
|
|
89
|
+
isLHSVarRefed = true
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
// Special handling for short declaration of
|
|
94
|
-
if
|
|
93
|
+
// Special handling for short declaration of variable referenced variables
|
|
94
|
+
if isLHSVarRefed && lhsIdent != nil {
|
|
95
95
|
c.tsw.WriteLiterally("let ")
|
|
96
96
|
// Just write the identifier name without .value
|
|
97
97
|
c.tsw.WriteLiterally(lhsIdent.Name)
|
|
98
|
-
|
|
99
|
-
// Add type annotation for boxed variables in declarations
|
|
100
|
-
if lhsObj != nil {
|
|
101
|
-
c.tsw.WriteLiterally(": ")
|
|
102
|
-
c.tsw.WriteLiterally("$.Box<")
|
|
103
|
-
c.WriteGoType(lhsObj.Type())
|
|
104
|
-
c.tsw.WriteLiterally(">")
|
|
105
|
-
}
|
|
106
|
-
|
|
98
|
+
// No type annotation, allow TypeScript to infer it from varRef.
|
|
107
99
|
c.tsw.WriteLiterally(" = ")
|
|
108
100
|
|
|
109
|
-
//
|
|
110
|
-
c.tsw.WriteLiterally("$.
|
|
101
|
+
// Create the variable reference for the initializer
|
|
102
|
+
c.tsw.WriteLiterally("$.varRef(")
|
|
111
103
|
if err := c.WriteValueExpr(rhs[0]); err != nil {
|
|
112
104
|
return err
|
|
113
105
|
}
|
|
@@ -177,6 +169,11 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
177
169
|
return err
|
|
178
170
|
}
|
|
179
171
|
c.tsw.WriteLiterally("]")
|
|
172
|
+
} else if callExpr, isCallExpr := r.(*ast.CallExpr); isCallExpr {
|
|
173
|
+
// If the RHS is a function call, write it as a call
|
|
174
|
+
if err := c.WriteCallExpr(callExpr); err != nil {
|
|
175
|
+
return err
|
|
176
|
+
}
|
|
180
177
|
} else {
|
|
181
178
|
// Normal case - write the entire expression
|
|
182
179
|
if err := c.WriteValueExpr(r); err != nil {
|
|
@@ -221,12 +218,12 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
221
218
|
}
|
|
222
219
|
|
|
223
220
|
if !currentIsMapIndex {
|
|
224
|
-
// For single assignments, handle
|
|
221
|
+
// For single assignments, handle variable referenced variables specially
|
|
225
222
|
if len(lhs) == 1 && len(rhs) == 1 {
|
|
226
223
|
lhsExprIdent, lhsExprIsIdent := l.(*ast.Ident)
|
|
227
224
|
if lhsExprIsIdent {
|
|
228
|
-
// Determine if LHS is
|
|
229
|
-
|
|
225
|
+
// Determine if LHS is variable referenced
|
|
226
|
+
isLHSVarRefed := false
|
|
230
227
|
var lhsObj types.Object
|
|
231
228
|
|
|
232
229
|
// Get the types.Object from the identifier
|
|
@@ -236,13 +233,13 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
236
233
|
lhsObj = def
|
|
237
234
|
}
|
|
238
235
|
|
|
239
|
-
// Check if this variable needs to be
|
|
240
|
-
if lhsObj != nil && c.analysis.
|
|
241
|
-
|
|
236
|
+
// Check if this variable needs to be variable referenced
|
|
237
|
+
if lhsObj != nil && c.analysis.NeedsVarRef(lhsObj) {
|
|
238
|
+
isLHSVarRefed = true
|
|
242
239
|
}
|
|
243
240
|
|
|
244
|
-
// prevent writing .value unless lhs is
|
|
245
|
-
c.WriteIdent(lhsExprIdent,
|
|
241
|
+
// prevent writing .value unless lhs is variable referenced
|
|
242
|
+
c.WriteIdent(lhsExprIdent, isLHSVarRefed)
|
|
246
243
|
continue
|
|
247
244
|
}
|
|
248
245
|
}
|
|
@@ -274,15 +271,16 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
274
271
|
if i != 0 {
|
|
275
272
|
c.tsw.WriteLiterally(", ")
|
|
276
273
|
}
|
|
277
|
-
|
|
274
|
+
|
|
275
|
+
// Check if we need to access a variable referenced source value and apply clone
|
|
278
276
|
// For struct value assignments, we need to handle:
|
|
279
|
-
// 1.
|
|
280
|
-
// 2.
|
|
281
|
-
// 3.
|
|
282
|
-
// 4.
|
|
277
|
+
// 1. UnVarRefed source, unVarRefed target: source.clone()
|
|
278
|
+
// 2. Variable referenced source, unVarRefed target: source.value.clone()
|
|
279
|
+
// 3. UnVarRefed source, variable referenced target: $.varRef(source)
|
|
280
|
+
// 4. Variable referenced source, variable referenced target: source (straight assignment of the variable reference)
|
|
283
281
|
|
|
284
|
-
// Determine if RHS is a
|
|
285
|
-
|
|
282
|
+
// Determine if RHS is a variable referenced variable (could be a struct or other variable)
|
|
283
|
+
needsVarRefedAccessRHS := false
|
|
286
284
|
var rhsObj types.Object
|
|
287
285
|
|
|
288
286
|
// Check if RHS is an identifier (variable name)
|
|
@@ -293,17 +291,17 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
293
291
|
rhsObj = c.pkg.TypesInfo.Defs[rhsIdent]
|
|
294
292
|
}
|
|
295
293
|
|
|
296
|
-
// Important: For struct copying, we need to check if the variable itself is
|
|
297
|
-
// Important: For struct copying, we need to check if the variable needs
|
|
298
|
-
// This is more comprehensive than just checking if it's
|
|
294
|
+
// Important: For struct copying, we need to check if the variable itself is variable referenced
|
|
295
|
+
// Important: For struct copying, we need to check if the variable needs variable referenced access
|
|
296
|
+
// This is more comprehensive than just checking if it's variable referenced
|
|
299
297
|
if rhsObj != nil {
|
|
300
|
-
|
|
298
|
+
needsVarRefedAccessRHS = c.analysis.NeedsVarRefAccess(rhsObj)
|
|
301
299
|
}
|
|
302
300
|
}
|
|
303
301
|
|
|
304
302
|
// Handle different cases for struct cloning
|
|
305
303
|
if shouldApplyClone(c.pkg, r) {
|
|
306
|
-
// For other expressions, we need to handle
|
|
304
|
+
// For other expressions, we need to handle variable referenced access differently
|
|
307
305
|
if _, isIdent := r.(*ast.Ident); isIdent {
|
|
308
306
|
// For identifiers, WriteValueExpr already adds .value if needed
|
|
309
307
|
if err := c.WriteValueExpr(r); err != nil {
|
|
@@ -314,14 +312,15 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
314
312
|
if err := c.WriteValueExpr(r); err != nil {
|
|
315
313
|
return err
|
|
316
314
|
}
|
|
317
|
-
// Only add .value for non-identifiers that need
|
|
318
|
-
if
|
|
319
|
-
c.tsw.WriteLiterally(".value") // Access the
|
|
315
|
+
// Only add .value for non-identifiers that need variable referenced access
|
|
316
|
+
if needsVarRefedAccessRHS {
|
|
317
|
+
c.tsw.WriteLiterally(".value") // Access the variable referenced value
|
|
320
318
|
}
|
|
321
319
|
}
|
|
322
320
|
|
|
323
321
|
c.tsw.WriteLiterally(".clone()") // Always add clone for struct values
|
|
324
322
|
} else {
|
|
323
|
+
// Non-struct case: write RHS normally
|
|
325
324
|
if err := c.WriteValueExpr(r); err != nil { // RHS is a non-struct value
|
|
326
325
|
return err
|
|
327
326
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"testing"
|
|
8
|
+
|
|
9
|
+
"github.com/sirupsen/logrus"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func TestEmitBuiltinOption(t *testing.T) {
|
|
13
|
+
// Create a temporary directory for the test output
|
|
14
|
+
tempDir, err := os.MkdirTemp("", "goscript-test-emit-builtin")
|
|
15
|
+
if err != nil {
|
|
16
|
+
t.Fatalf("Failed to create temp dir: %v", err)
|
|
17
|
+
}
|
|
18
|
+
defer os.RemoveAll(tempDir)
|
|
19
|
+
|
|
20
|
+
// Setup logger
|
|
21
|
+
log := logrus.New()
|
|
22
|
+
log.SetLevel(logrus.DebugLevel)
|
|
23
|
+
le := logrus.NewEntry(log)
|
|
24
|
+
|
|
25
|
+
// Case 1: DisableEmitBuiltin = true (default behavior in compliance tests)
|
|
26
|
+
t.Run("DisableEmitBuiltin=true", func(t *testing.T) {
|
|
27
|
+
outputDir := filepath.Join(tempDir, "disabled")
|
|
28
|
+
config := &Config{
|
|
29
|
+
OutputPath: outputDir,
|
|
30
|
+
AllDependencies: true,
|
|
31
|
+
DisableEmitBuiltin: true,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
compiler, err := NewCompiler(config, le, nil)
|
|
35
|
+
if err != nil {
|
|
36
|
+
t.Fatalf("Failed to create compiler: %v", err)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Compile a package that depends on builtin (time)
|
|
40
|
+
err = compiler.CompilePackages(context.Background(), "time")
|
|
41
|
+
if err != nil {
|
|
42
|
+
t.Fatalf("Compilation failed: %v", err)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check that the unsafe package wasn't emitted (we know it's handwritten)
|
|
46
|
+
unsafePath := filepath.Join(outputDir, "@goscript/unsafe")
|
|
47
|
+
if _, err := os.Stat(unsafePath); !os.IsNotExist(err) {
|
|
48
|
+
t.Errorf("unsafe package was emitted when DisableEmitBuiltin=true")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Also check for runtime package
|
|
52
|
+
runtimePath := filepath.Join(outputDir, "@goscript/runtime")
|
|
53
|
+
if _, err := os.Stat(runtimePath); !os.IsNotExist(err) {
|
|
54
|
+
t.Errorf("runtime package was emitted when DisableEmitBuiltin=true")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// But time package should have been emitted
|
|
58
|
+
timePath := filepath.Join(outputDir, "@goscript/time")
|
|
59
|
+
if _, err := os.Stat(timePath); os.IsNotExist(err) {
|
|
60
|
+
t.Errorf("time package was not emitted")
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// Case 2: DisableEmitBuiltin = false (new behavior for third-party projects)
|
|
65
|
+
t.Run("DisableEmitBuiltin=false", func(t *testing.T) {
|
|
66
|
+
outputDir := filepath.Join(tempDir, "enabled")
|
|
67
|
+
config := &Config{
|
|
68
|
+
OutputPath: outputDir,
|
|
69
|
+
AllDependencies: true,
|
|
70
|
+
DisableEmitBuiltin: false,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
compiler, err := NewCompiler(config, le, nil)
|
|
74
|
+
if err != nil {
|
|
75
|
+
t.Fatalf("Failed to create compiler: %v", err)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Compile a package that depends on builtin (time)
|
|
79
|
+
err = compiler.CompilePackages(context.Background(), "time")
|
|
80
|
+
if err != nil {
|
|
81
|
+
t.Fatalf("Compilation failed: %v", err)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check that the unsafe package was copied to the output
|
|
85
|
+
unsafePath := filepath.Join(outputDir, "@goscript/unsafe")
|
|
86
|
+
if _, err := os.Stat(unsafePath); os.IsNotExist(err) {
|
|
87
|
+
t.Errorf("unsafe package was not emitted when DisableEmitBuiltin=false")
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Also check for runtime package
|
|
91
|
+
runtimePath := filepath.Join(outputDir, "@goscript/runtime")
|
|
92
|
+
if _, err := os.Stat(runtimePath); os.IsNotExist(err) {
|
|
93
|
+
t.Errorf("runtime package was not emitted when DisableEmitBuiltin=false")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Time package should also have been emitted
|
|
97
|
+
timePath := filepath.Join(outputDir, "@goscript/time")
|
|
98
|
+
if _, err := os.Stat(timePath); os.IsNotExist(err) {
|
|
99
|
+
t.Errorf("time package was not emitted")
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
}
|