Skip to content

Commit ee70b03

Browse files
committed
fix: remove cycle reference to marshal json (cloudwego#16)
1 parent 5061c66 commit ee70b03

4 files changed

Lines changed: 133 additions & 55 deletions

File tree

src/compress/golang/plugin/file.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type Function struct {
3838
IsMethod bool // If the function is a method
3939
Name string // Name of the function
4040
PkgPath // import path to the package where the function is defined
41-
FilePath string // File where the function is defined, empty if the function declaration is not scanned
41+
FilePath string `json:"-"` // File where the function is defined, empty if the function declaration is not scanned
4242
Content string // Content of the function, including functiion signature and body
4343
AssociatedStruct *Struct // Method receiver
4444

@@ -302,7 +302,7 @@ type Struct struct {
302302
IsInterface bool //maybe a interface type decl
303303
Name string // Name of the struct
304304
PkgPath // Path to the package where the struct is defined
305-
FilePath string // File where the struct is defined
305+
FilePath string `json:"-"` // File where the struct is defined
306306
Content string // struct declaration content
307307

308308
// related local structs in fields, key is {{pkgName.typName}} or {{typeName}}, val is declaration of the struct

src/compress/golang/plugin/go_ast.go

Lines changed: 32 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -142,40 +142,14 @@ type MainStream struct {
142142
RelatedStruct []SingleStruct
143143
}
144144

145-
func (m *MainStream) Dedup() {
146-
fs := map[string]string{}
147-
for _, f := range m.RelatedFunctions {
148-
fs[f.CallName] = f.Content
149-
}
150-
m.RelatedFunctions = m.RelatedFunctions[:len(fs)]
151-
i := 0
152-
for k, v := range fs {
153-
m.RelatedFunctions[i].CallName = k
154-
m.RelatedFunctions[i].Content = v
155-
i++
156-
}
157-
158-
fs = map[string]string{}
159-
for _, f := range m.RelatedStruct {
160-
fs[f.Name] = f.Content
161-
}
162-
m.RelatedStruct = m.RelatedStruct[:len(fs)]
163-
i = 0
164-
for k, v := range fs {
165-
m.RelatedStruct[i].Name = k
166-
m.RelatedStruct[i].Content = v
167-
i++
168-
}
169-
}
170-
171145
type SingleFunction struct {
172146
CallName string
173147
Content string
174148
}
175149

176150
type SingleStruct struct {
177-
Name string
178-
Content string
151+
CallName string
152+
Content string
179153
}
180154

181155
func (p *goParser) getMain(depth int) (*MainStream, *Function) {
@@ -194,61 +168,70 @@ func (p *goParser) getMain(depth int) (*MainStream, *Function) {
194168
}
195169
}
196170
}
197-
visited := map[string]map[string]bool{}
171+
visited := cache(map[interface{}]bool{})
198172
p.fillRelatedContent(depth, mainFunc, &m.RelatedFunctions, &m.RelatedStruct, visited)
199173
return m, mainFunc
200174
}
201175

202-
func (p *goParser) fillRelatedContent(depth int, f *Function, fl *[]SingleFunction, sl *[]SingleStruct, visited map[string]map[string]bool) {
176+
func (p *goParser) fillRelatedContent(depth int, f *Function, fl *[]SingleFunction, sl *[]SingleStruct, visited cache) {
203177
if depth == 0 {
204178
return
205179
}
206-
if f == nil || (visited[f.PkgPath] != nil && visited[f.PkgPath][f.Name]) {
180+
if f == nil {
207181
return
208-
} else {
209-
if visited[f.PkgPath] == nil {
210-
visited[f.PkgPath] = map[string]bool{}
211-
}
212-
visited[f.PkgPath][f.Name] = true
213182
}
214-
for call, ff := range f.InternalFunctionCalls {
183+
184+
//BFS
185+
var next []Function
186+
187+
for call, v := range f.InternalFunctionCalls {
188+
if visited.Visited(v) {
189+
continue
190+
}
191+
ff := *v
215192
s := SingleFunction{
216193
CallName: call,
217-
Content: ff.Content,
194+
// Name: ff.PkgPath + "." + ff.Name,
195+
Content: ff.Content,
218196
}
219197
*fl = append(*fl, s)
220-
p.fillRelatedContent(depth-1, ff, fl, sl, visited)
198+
next = append(next, ff)
221199
}
222200

223-
for call, ff := range f.InternalMethodCalls {
201+
for call, v := range f.InternalMethodCalls {
202+
if visited.Visited(v) {
203+
continue
204+
}
205+
ff := *v
224206
content := ff.Content
225207
if ff.AssociatedStruct != nil && ff.AssociatedStruct.IsInterface {
226208
content = ff.AssociatedStruct.Content
227209
}
228210
s := SingleFunction{
229211
CallName: call,
230-
Content: content,
212+
// Name: ff.PkgPath + "." + ff.Name,
213+
Content: content,
231214
}
232215
*fl = append(*fl, s)
233-
p.fillRelatedContent(depth-1, ff, fl, sl, visited)
216+
next = append(next, ff)
234217

235218
// for method which has been associated with struct, push the struct
236219
if ff.AssociatedStruct != nil && ff.AssociatedStruct.Content != "" {
237220
st := ff.AssociatedStruct
238-
if visited[st.PkgPath] != nil && visited[st.PkgPath][st.Name] {
221+
if visited.Visited(st) {
239222
continue
240-
} else if visited[st.PkgPath] == nil {
241-
visited[st.PkgPath] = map[string]bool{}
242223
}
243-
visited[st.PkgPath][st.Name] = true
244224
ss := SingleStruct{
245-
Name: ff.PkgPath + "." + st.Name,
225+
CallName: call,
226+
// Name: (*st).PkgPath + "." + st.Name,
246227
Content: st.Content,
247228
}
248229
*sl = append(*sl, ss)
249230
}
231+
}
250232

251-
p.fillRelatedContent(depth-1, ff, fl, sl, visited)
233+
for _, ff := range next {
234+
p.fillRelatedContent(depth-1, &ff, fl, sl, visited)
252235
}
253236
}
254237

@@ -294,8 +277,7 @@ func main() {
294277
}
295278

296279
// p.generateStruct()
297-
m, _ := p.getMain(100)
298-
m.Dedup()
280+
m, _ := p.getMain(-1)
299281

300282
out := bytes.NewBuffer(nil)
301283
encoder := json.NewEncoder(out)

src/compress/golang/plugin/go_ast_test.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,17 @@ func Test_goParser_ParseTilTheEnd(t *testing.T) {
4040
if fun.Name != "main" {
4141
t.Fail()
4242
}
43-
out.Dedup()
44-
if out, err := json.MarshalIndent(out, "", " "); err != nil {
43+
fun.RemoveCycle()
44+
if out, err := json.Marshal(fun); err != nil {
45+
t.Fatal(err)
46+
} else {
47+
println("func size:", len(out), string(out))
48+
}
49+
if out, err := json.Marshal(out); err != nil {
4550
t.Fatalf("json.Marshal() error = %v", err)
4651
} else {
47-
println("size:", len(out))
52+
println("stream size:", len(out))
53+
println(string(out))
4854
}
4955
})
5056
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Copyright 2024 ByteDance Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package main
18+
19+
type cache map[interface{}]bool
20+
21+
func (c cache) Visited(val interface{}) bool {
22+
ok := c[val]
23+
if !ok {
24+
c[val] = true
25+
}
26+
return ok
27+
}
28+
29+
// RemoveCycle remove cycle reference to repeated pointer, in case of marshalling fail
30+
func (f *Function) RemoveCycle() {
31+
var visited = cache(map[interface{}]bool{})
32+
f.deLoop(visited)
33+
if f.AssociatedStruct != nil {
34+
f.AssociatedStruct.deLoop(visited)
35+
}
36+
}
37+
38+
func (f *Function) deLoop(visited cache) {
39+
for k, ff := range f.InternalFunctionCalls {
40+
if visited.Visited(ff) {
41+
var nf = new(Function)
42+
nf.PkgPath = ff.PkgPath
43+
nf.Name = ff.Name
44+
f.InternalFunctionCalls[k] = nf
45+
} else {
46+
ff.deLoop(visited)
47+
}
48+
}
49+
for k, ff := range f.InternalMethodCalls {
50+
if visited.Visited(ff) {
51+
var nf = new(Function)
52+
nf.PkgPath = ff.PkgPath
53+
nf.Name = ff.Name
54+
nf.IsMethod = ff.IsMethod
55+
f.InternalMethodCalls[k] = nf
56+
} else {
57+
ff.deLoop(visited)
58+
if ff.AssociatedStruct != nil {
59+
ff.AssociatedStruct.deLoop(visited)
60+
}
61+
}
62+
}
63+
}
64+
65+
func (f *Struct) deLoop(visited cache) {
66+
for k, ff := range f.InternalStructs {
67+
if visited.Visited(ff) {
68+
var nf = new(Struct)
69+
nf.PkgPath = ff.PkgPath
70+
nf.Name = ff.Name
71+
f.InternalStructs[k] = nf
72+
} else {
73+
ff.deLoop(visited)
74+
}
75+
}
76+
for k, ff := range f.Methods {
77+
if visited.Visited(ff) {
78+
var nf = new(Function)
79+
nf.PkgPath = ff.PkgPath
80+
nf.Name = ff.Name
81+
nf.IsMethod = ff.IsMethod
82+
f.Methods[k] = nf
83+
} else {
84+
ff.deLoop(visited)
85+
if ff.AssociatedStruct != nil {
86+
ff.AssociatedStruct.deLoop(visited)
87+
}
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)