Skip to content

Commit 71da690

Browse files
dagoodalexbrainman
authored andcommitted
windows/mkwinsyscall: support "." and "-" in DLL name
This change adds "." and "-" support for DLL filenames in "//sys". Supporting "." requires a change in how mkwinsyscall handles the "= <filename>.<function>" syntax. Instead of assuming that only one "." can appear in this string, now mkwinsyscall assumes that any additional "." belongs to the filename. Supporting "." also requires changing how Go identifiers are created for each DLL. This change also allows mkwinsyscall to support "-". When creating a Go identifier, "." and "-" in the DLL filename are replaced with "_". Otherwise, mkwinsyscall would produce invalid Go code, causing "format.Source" to fail. Includes a test for the new behavior. There aren't yet any cases where this code is executed while generating the x/sys/windows syscalls. The syscalls "SetSocketMediaStreamingMode" from "windows.networking.dll" and "WslRegisterDistribution" from "api-ms-win-wsl-api-l1-1-0.dll" can be successfully called using this change, but these syscalls have no known use in Go so they are not included in this change. Fixes golang/go#57913 Change-Id: If64deeb8c7738d61520e7392fd2d81ef8920f08d Reviewed-on: https://go-review.googlesource.com/c/sys/+/463215 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Alex Brainman <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> Run-TryBot: Alex Brainman <[email protected]> Reviewed-by: Quim Muntal <[email protected]> Reviewed-by: Bryan Mills <[email protected]>
1 parent b829a39 commit 71da690

File tree

2 files changed

+93
-19
lines changed

2 files changed

+93
-19
lines changed

windows/mkwinsyscall/mkwinsyscall.go

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -480,15 +480,14 @@ func newFn(s string) (*Fn, error) {
480480
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
481481
}
482482
s = trim(s[1:])
483-
a := strings.Split(s, ".")
484-
switch len(a) {
485-
case 1:
486-
f.dllfuncname = a[0]
487-
case 2:
488-
f.dllname = a[0]
489-
f.dllfuncname = a[1]
490-
default:
491-
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
483+
if i := strings.LastIndex(s, "."); i >= 0 {
484+
f.dllname = s[:i]
485+
f.dllfuncname = s[i+1:]
486+
} else {
487+
f.dllfuncname = s
488+
}
489+
if f.dllfuncname == "" {
490+
return nil, fmt.Errorf("function name is not specified in %q", s)
492491
}
493492
if n := f.dllfuncname; strings.HasSuffix(n, "?") {
494493
f.dllfuncname = n[:len(n)-1]
@@ -505,7 +504,23 @@ func (f *Fn) DLLName() string {
505504
return f.dllname
506505
}
507506

508-
// DLLName returns DLL function name for function f.
507+
// DLLVar returns a valid Go identifier that represents DLLName.
508+
func (f *Fn) DLLVar() string {
509+
id := strings.Map(func(r rune) rune {
510+
switch r {
511+
case '.', '-':
512+
return '_'
513+
default:
514+
return r
515+
}
516+
}, f.DLLName())
517+
if !token.IsIdentifier(id) {
518+
panic(fmt.Errorf("could not create Go identifier for DLLName %q", f.DLLName()))
519+
}
520+
return id
521+
}
522+
523+
// DLLFuncName returns DLL function name for function f.
509524
func (f *Fn) DLLFuncName() string {
510525
if f.dllfuncname == "" {
511526
return f.Name
@@ -650,6 +665,13 @@ func (f *Fn) HelperName() string {
650665
return "_" + f.Name
651666
}
652667

668+
// DLL is a DLL's filename and a string that is valid in a Go identifier that should be used when
669+
// naming a variable that refers to the DLL.
670+
type DLL struct {
671+
Name string
672+
Var string
673+
}
674+
653675
// Source files and functions.
654676
type Source struct {
655677
Funcs []*Fn
@@ -699,17 +721,19 @@ func ParseFiles(fs []string) (*Source, error) {
699721
}
700722

701723
// DLLs return dll names for a source set src.
702-
func (src *Source) DLLs() []string {
724+
func (src *Source) DLLs() []DLL {
703725
uniq := make(map[string]bool)
704-
r := make([]string, 0)
726+
r := make([]DLL, 0)
705727
for _, f := range src.Funcs {
706-
name := f.DLLName()
707-
if _, found := uniq[name]; !found {
708-
uniq[name] = true
709-
r = append(r, name)
728+
id := f.DLLVar()
729+
if _, found := uniq[id]; !found {
730+
uniq[id] = true
731+
r = append(r, DLL{f.DLLName(), id})
710732
}
711733
}
712-
sort.Strings(r)
734+
sort.Slice(r, func(i, j int) bool {
735+
return r[i].Var < r[j].Var
736+
})
713737
return r
714738
}
715739

@@ -936,10 +960,10 @@ var (
936960
937961
{{/* help functions */}}
938962
939-
{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{newlazydll .}}
963+
{{define "dlls"}}{{range .DLLs}} mod{{.Var}} = {{newlazydll .Name}}
940964
{{end}}{{end}}
941965
942-
{{define "funcnames"}}{{range .DLLFuncNames}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}")
966+
{{define "funcnames"}}{{range .DLLFuncNames}} proc{{.DLLFuncName}} = mod{{.DLLVar}}.NewProc("{{.DLLFuncName}}")
943967
{{end}}{{end}}
944968
945969
{{define "helperbody"}}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"bytes"
9+
"go/format"
10+
"os"
11+
"path/filepath"
12+
"testing"
13+
)
14+
15+
func TestDLLFilenameEscaping(t *testing.T) {
16+
tests := []struct {
17+
name string
18+
filename string
19+
}{
20+
{"no escaping necessary", "kernel32"},
21+
{"escape period", "windows.networking"},
22+
{"escape dash", "api-ms-win-wsl-api-l1-1-0"},
23+
}
24+
for _, tt := range tests {
25+
t.Run(tt.name, func(t *testing.T) {
26+
// Write a made-up syscall into a temp file for testing.
27+
const prefix = "package windows\n//sys Example() = "
28+
const suffix = ".Example"
29+
name := filepath.Join(t.TempDir(), "syscall.go")
30+
if err := os.WriteFile(name, []byte(prefix+tt.filename+suffix), 0666); err != nil {
31+
t.Fatal(err)
32+
}
33+
34+
// Ensure parsing, generating, and formatting run without errors.
35+
// This is good enough to show that escaping is working.
36+
src, err := ParseFiles([]string{name})
37+
if err != nil {
38+
t.Fatal(err)
39+
}
40+
var buf bytes.Buffer
41+
if err := src.Generate(&buf); err != nil {
42+
t.Fatal(err)
43+
}
44+
if _, err := format.Source(buf.Bytes()); err != nil {
45+
t.Log(buf.String())
46+
t.Fatal(err)
47+
}
48+
})
49+
}
50+
}

0 commit comments

Comments
 (0)