fix(scanner): construct mapped core children through public factories
- commit
- f69b87f26558a01f960906accbba208adb1159b1
- author
- brice · · 2026-09-28 07:26 UTC
- parent
- c47d0a5
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +46 -3
M internal/scanner/scanner.go +14 -3
| @@ -194,6 +194,7 @@ type requestData struct { | |||
| 194 | 194 | CreatesChild bool | |
| 195 | 195 | ChildVar string | |
| 196 | 196 | ChildType string | |
| 197 | + | ChildFactory string | |
| 197 | 198 | ErrorReturn string | |
| 198 | 199 | FDArgs []string | |
| 199 | 200 | HasFDs bool | |
| @@ -470,6 +471,9 @@ func (s *Scanner) processRequest(iface Interface, req Request, opcode int) (requ | |||
| 470 | 471 | } | |
| 471 | 472 | data.CreatesChild = true | |
| 472 | 473 | data.ChildType = childType | |
| 474 | + | if strings.Contains(childType, ".") { | |
| 475 | + | data.ChildFactory = childType[:strings.LastIndex(childType, ".")+1] + "New" + s.toGoName(newIDArg.Interface) | |
| 476 | + | } | |
| 473 | 477 | } | |
| 474 | 478 | ||
| 475 | 479 | for _, slot := range argSlots { | |
| @@ -526,10 +530,17 @@ func (s *Scanner) processEvent(event Event, opcode int) (eventData, error) { | |||
| 526 | 530 | return data, fmt.Errorf("%s.%s: %w", event.Name, arg.Name, err) | |
| 527 | 531 | } | |
| 528 | 532 | child := name + "Object" | |
| 533 | + | data.DecodeLines = append(data.DecodeLines, fmt.Sprintf("%sID := event.Uint32()", name)) | |
| 534 | + | if strings.Contains(childType, ".") { | |
| 535 | + | factory := childType[:strings.LastIndex(childType, ".")+1] + "New" + s.toGoName(arg.Interface) | |
| 536 | + | data.DecodeLines = append(data.DecodeLines, fmt.Sprintf("%s := %s(o.Context())", child, factory)) | |
| 537 | + | } else { | |
| 538 | + | data.DecodeLines = append(data.DecodeLines, | |
| 539 | + | fmt.Sprintf("%s := &%s{}", child, childType), | |
| 540 | + | fmt.Sprintf("%s.SetContext(o.Context())", child), | |
| 541 | + | ) | |
| 542 | + | } | |
| 529 | 543 | data.DecodeLines = append(data.DecodeLines, | |
| 530 | - | fmt.Sprintf("%sID := event.Uint32()", name), | |
| 531 | - | fmt.Sprintf("%s := &%s{}", child, childType), | |
| 532 | - | fmt.Sprintf("%s.SetContext(o.Context())", child), | |
| 533 | 544 | fmt.Sprintf("%s.SetID(%sID)", child, name), | |
| 534 | 545 | fmt.Sprintf("o.Context().Register(%s)", child), | |
| 535 | 546 | ) | |
| @@ -194,6 +194,7 @@ type requestData struct { | |||||
| 194 | CreatesChild bool | 194 | CreatesChild bool | ||
| 195 | ChildVar string | 195 | ChildVar string | ||
| 196 | ChildType string | 196 | ChildType string | ||
| 197 | + | ChildFactory string | |||
| 197 | ErrorReturn string | 198 | ErrorReturn string | ||
| 198 | FDArgs []string | 199 | FDArgs []string | ||
| 199 | HasFDs bool | 200 | HasFDs bool | ||
| @@ -470,6 +471,9 @@ func (s *Scanner) processRequest(iface Interface, req Request, opcode int) (requ | |||||
| 470 | } | 471 | } | ||
| 471 | data.CreatesChild = true | 472 | data.CreatesChild = true | ||
| 472 | data.ChildType = childType | 473 | data.ChildType = childType | ||
| 474 | + | if strings.Contains(childType, ".") { | |||
| 475 | + | data.ChildFactory = childType[:strings.LastIndex(childType, ".")+1] + "New" + s.toGoName(newIDArg.Interface) | |||
| 476 | + | } | |||
| 473 | } | 477 | } | ||
| 474 | 478 | ||||
| 475 | for _, slot := range argSlots { | 479 | for _, slot := range argSlots { | ||
| @@ -526,10 +530,17 @@ func (s *Scanner) processEvent(event Event, opcode int) (eventData, error) { | |||||
| 526 | return data, fmt.Errorf("%s.%s: %w", event.Name, arg.Name, err) | 530 | return data, fmt.Errorf("%s.%s: %w", event.Name, arg.Name, err) | ||
| 527 | } | 531 | } | ||
| 528 | child := name + "Object" | 532 | child := name + "Object" | ||
| 533 | + | data.DecodeLines = append(data.DecodeLines, fmt.Sprintf("%sID := event.Uint32()", name)) | |||
| 534 | + | if strings.Contains(childType, ".") { | |||
| 535 | + | factory := childType[:strings.LastIndex(childType, ".")+1] + "New" + s.toGoName(arg.Interface) | |||
| 536 | + | data.DecodeLines = append(data.DecodeLines, fmt.Sprintf("%s := %s(o.Context())", child, factory)) | |||
| 537 | + | } else { | |||
| 538 | + | data.DecodeLines = append(data.DecodeLines, | |||
| 539 | + | fmt.Sprintf("%s := &%s{}", child, childType), | |||
| 540 | + | fmt.Sprintf("%s.SetContext(o.Context())", child), | |||
| 541 | + | ) | |||
| 542 | + | } | |||
| 529 | data.DecodeLines = append(data.DecodeLines, | 543 | data.DecodeLines = append(data.DecodeLines, | ||
| 530 | - | fmt.Sprintf("%sID := event.Uint32()", name), | |||
| 531 | - | fmt.Sprintf("%s := &%s{}", child, childType), | |||
| 532 | - | fmt.Sprintf("%s.SetContext(o.Context())", child), | |||
| 533 | fmt.Sprintf("%s.SetID(%sID)", child, name), | 544 | fmt.Sprintf("%s.SetID(%sID)", child, name), | ||
| 534 | fmt.Sprintf("o.Context().Register(%s)", child), | 545 | fmt.Sprintf("o.Context().Register(%s)", child), | ||
| 535 | ) | 546 | ) | ||
M internal/scanner/scanner_test.go +28 -0
| @@ -45,3 +45,31 @@ func TestCoreGeneration(t *testing.T) { | |||
| 45 | 45 | t.Fatal("bootstrap generated twice") | |
| 46 | 46 | } | |
| 47 | 47 | } | |
| 48 | + | ||
| 49 | + | // P3 uses wl_buffer as both an event-created child and a request-created child. | |
| 50 | + | // Verify the generated code uses the public constructor instead of embedding | |
| 51 | + | // an inaccessible BaseProxy context field across package boundaries. | |
| 52 | + | func TestExternalChildFactory(t *testing.T) { | |
| 53 | + | path := filepath.Join(t.TempDir(), "external.xml") | |
| 54 | + | xml := `<protocol name="fixture"><interface name="ext_params" version="1"><request name="create"><arg name="id" type="new_id" interface="wl_buffer"/></request><event name="created"><arg name="id" type="new_id" interface="wl_buffer"/></event></interface></protocol>` | |
| 55 | + | if err := os.WriteFile(path, []byte(xml), 0600); err != nil { | |
| 56 | + | t.Fatal(err) | |
| 57 | + | } | |
| 58 | + | s := NewScanner() | |
| 59 | + | if err := s.ParseXML(path); err != nil { | |
| 60 | + | t.Fatal(err) | |
| 61 | + | } | |
| 62 | + | s.CrossPackage = map[string]string{"wl_buffer": "github.com/bnema/wlturbo/protocol/core"} | |
| 63 | + | generated, err := s.Generate("ext") | |
| 64 | + | if err != nil { | |
| 65 | + | t.Fatal(err) | |
| 66 | + | } | |
| 67 | + | for _, part := range []string{"cross_wl_buffer.NewBuffer(o.Context())", "(*cross_wl_buffer.Buffer, error)", "idObject.SetID(idID)"} { | |
| 68 | + | if !strings.Contains(string(generated), part) { | |
| 69 | + | t.Errorf("generated code missing %q", part) | |
| 70 | + | } | |
| 71 | + | } | |
| 72 | + | if strings.Contains(string(generated), "&cross_wl_buffer.Buffer{}") { | |
| 73 | + | t.Fatal("foreign child was constructed without its package factory") | |
| 74 | + | } | |
| 75 | + | } | |
| @@ -45,3 +45,31 @@ func TestCoreGeneration(t *testing.T) { | |||||
| 45 | t.Fatal("bootstrap generated twice") | 45 | t.Fatal("bootstrap generated twice") | ||
| 46 | } | 46 | } | ||
| 47 | } | 47 | } | ||
| 48 | + | ||||
| 49 | + | // P3 uses wl_buffer as both an event-created child and a request-created child. | |||
| 50 | + | // Verify the generated code uses the public constructor instead of embedding | |||
| 51 | + | // an inaccessible BaseProxy context field across package boundaries. | |||
| 52 | + | func TestExternalChildFactory(t *testing.T) { | |||
| 53 | + | path := filepath.Join(t.TempDir(), "external.xml") | |||
| 54 | + | xml := `<protocol name="fixture"><interface name="ext_params" version="1"><request name="create"><arg name="id" type="new_id" interface="wl_buffer"/></request><event name="created"><arg name="id" type="new_id" interface="wl_buffer"/></event></interface></protocol>` | |||
| 55 | + | if err := os.WriteFile(path, []byte(xml), 0600); err != nil { | |||
| 56 | + | t.Fatal(err) | |||
| 57 | + | } | |||
| 58 | + | s := NewScanner() | |||
| 59 | + | if err := s.ParseXML(path); err != nil { | |||
| 60 | + | t.Fatal(err) | |||
| 61 | + | } | |||
| 62 | + | s.CrossPackage = map[string]string{"wl_buffer": "github.com/bnema/wlturbo/protocol/core"} | |||
| 63 | + | generated, err := s.Generate("ext") | |||
| 64 | + | if err != nil { | |||
| 65 | + | t.Fatal(err) | |||
| 66 | + | } | |||
| 67 | + | for _, part := range []string{"cross_wl_buffer.NewBuffer(o.Context())", "(*cross_wl_buffer.Buffer, error)", "idObject.SetID(idID)"} { | |||
| 68 | + | if !strings.Contains(string(generated), part) { | |||
| 69 | + | t.Errorf("generated code missing %q", part) | |||
| 70 | + | } | |||
| 71 | + | } | |||
| 72 | + | if strings.Contains(string(generated), "&cross_wl_buffer.Buffer{}") { | |||
| 73 | + | t.Fatal("foreign child was constructed without its package factory") | |||
| 74 | + | } | |||
| 75 | + | } | |||
M internal/scanner/template.go +4 -0
| @@ -54,8 +54,12 @@ func (o *{{$iface.GoName}}) {{.GoName}}({{.Params}}) {{.Results}} { | |||
| 54 | 54 | {{.}} | |
| 55 | 55 | {{- end}} | |
| 56 | 56 | {{- if .CreatesChild}} | |
| 57 | + | {{- if .ChildFactory}} | |
| 58 | + | {{.ChildVar}} := {{.ChildFactory}}(o.Context()) | |
| 59 | + | {{- else}} | |
| 57 | 60 | {{.ChildVar}} := &{{.ChildType}}{} | |
| 58 | 61 | {{.ChildVar}}.SetContext(o.Context()) | |
| 62 | + | {{- end}} | |
| 59 | 63 | {{.ChildVar}}.SetID(o.Context().AllocateID()) | |
| 60 | 64 | o.Context().Register({{.ChildVar}}) | |
| 61 | 65 | {{- end}} | |
| @@ -54,8 +54,12 @@ func (o *{{$iface.GoName}}) {{.GoName}}({{.Params}}) {{.Results}} { | |||||
| 54 | {{.}} | 54 | {{.}} | ||
| 55 | {{- end}} | 55 | {{- end}} | ||
| 56 | {{- if .CreatesChild}} | 56 | {{- if .CreatesChild}} | ||
| 57 | + | {{- if .ChildFactory}} | |||
| 58 | + | {{.ChildVar}} := {{.ChildFactory}}(o.Context()) | |||
| 59 | + | {{- else}} | |||
| 57 | {{.ChildVar}} := &{{.ChildType}}{} | 60 | {{.ChildVar}} := &{{.ChildType}}{} | ||
| 58 | {{.ChildVar}}.SetContext(o.Context()) | 61 | {{.ChildVar}}.SetContext(o.Context()) | ||
| 62 | + | {{- end}} | |||
| 59 | {{.ChildVar}}.SetID(o.Context().AllocateID()) | 63 | {{.ChildVar}}.SetID(o.Context().AllocateID()) | ||
| 60 | o.Context().Register({{.ChildVar}}) | 64 | o.Context().Register({{.ChildVar}}) | ||
| 61 | {{- end}} | 65 | {{- end}} | ||