Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for specification overloads #107

Merged
merged 15 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ wgl
# Vim
*.swp
*.swo

# GoLand
.idea/
10 changes: 10 additions & 0 deletions functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ type Function struct {
GoName string // Go name of the function with the API prefix stripped
Parameters []Parameter
Return Type
Overloads []Overload
}

// An Overload describes an alternative signature for the same function.
type Overload struct {
Name string // C name of the original function
GoName string // Go name of the original function
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this value be pulled from the parent struct? similarly for Name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name turned out not to be necessary. As for GoName, I was not able to figure out an easy way to access GoName from the parent from the sub-template overloadCall. Hence I kept this field

OverloadName string // Go name of the overload
Parameters []Parameter
Return Type
}

// A Parameter to a Function.
Expand Down
12 changes: 11 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func performRestriction(pkg *Package, jsonPath string) {

func parseSpecifications(xmlDir string) []*Specification {
specDir := filepath.Join(xmlDir, "spec")
overloadDir := filepath.Join(xmlDir, "overload")
specFiles, err := ioutil.ReadDir(specDir)
if err != nil {
log.Fatalln("error reading spec file entries:", err)
Expand All @@ -124,7 +125,16 @@ func parseSpecifications(xmlDir string) []*Specification {
if !strings.HasSuffix(specFile.Name(), "xml") {
continue
}
spec, err := NewSpecification(filepath.Join(specDir, specFile.Name()))

registry, err := readSpecFile(filepath.Join(specDir, specFile.Name()))
if err != nil {
log.Fatalln("error reading XML spec file: ", specFile.Name(), err)
}
overloads, err := readOverloadFile(filepath.Join(overloadDir, specFile.Name()))
if err != nil {
log.Fatalln("error reading XML overload file: ", specFile.Name(), err)
}
spec, err := NewSpecification(*registry, overloads)
if err != nil {
log.Fatalln("error parsing specification:", specFile.Name(), err)
}
Expand Down
53 changes: 53 additions & 0 deletions overload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"encoding/xml"
"os"
)

type xmlOverloads struct {
List []xmlOverload `xml:"overload"`
dertseha marked this conversation as resolved.
Show resolved Hide resolved
}

type xmlOverload struct {
Name string `xml:"name,attr"`
OverloadName string `xml:"overloadName,attr"`

ParameterChanges []xmlParameterChange `xml:"parameterChanges>change"`
}

type xmlParameterChange struct {
// Index is the zero-based index of the parameter list.
Index int `xml:"index,attr"`
// Name describes a change of the parameter name.
Name *xmlNameChange `xml:"name"`
// Type describes a change of the parameter type.
Type *xmlTypeChange `xml:"type"`
}

type xmlNameChange struct {
Value string `xml:"value,attr"`
}

type xmlTypeChange struct {
Name string `xml:"name,attr"`
PointerLevel int `xml:"pointerLevel,attr"`
dertseha marked this conversation as resolved.
Show resolved Hide resolved
}

func readOverloadFile(file string) (xmlOverloads, error) {
var overloads xmlOverloads

_, err := os.Stat(file)
if err != nil {
return overloads, nil
}

f, err := os.Open(file)
if err != nil {
return overloads, err
}
defer f.Close()

err = xml.NewDecoder(f).Decode(&overloads)
return overloads, err
}
57 changes: 53 additions & 4 deletions spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,55 @@ func parseFunctions(commands []xmlCommand) (specFunctions, error) {
return functions, nil
}

func parseOverloads(functions specFunctions, overloads xmlOverloads) (specFunctions, error) {
for _, overloadInfo := range overloads.List {
found := false
for key, function := range functions {
if key.name == overloadInfo.Name {
found = true
err := overloadFunction(function, overloadInfo)
if err != nil {
return nil, err
}
dertseha marked this conversation as resolved.
Show resolved Hide resolved
}
}
if !found {
return nil, fmt.Errorf("function <%s> not found to overload", overloadInfo.Name)
}
}
return functions, nil
}

func overloadFunction(function *Function, info xmlOverload) error {
overload := Overload{
Name: function.Name,
GoName: function.GoName,
OverloadName: info.OverloadName,
Parameters: make([]Parameter, len(function.Parameters)),
Return: function.Return,
}
copy(overload.Parameters, function.Parameters)
for _, change := range info.ParameterChanges {
if (change.Index < 0) || (change.Index >= len(function.Parameters)) {
return fmt.Errorf("overload for <%s> has invalid parameter index", info.Name)
}
param := &overload.Parameters[change.Index]

if change.Type != nil {
// store original type definition as a cast, as this most likely will be needed.
param.Type.Cast = param.Type.CDefinition
param.Type.PointerLevel = change.Type.PointerLevel
param.Type.Name = change.Type.Name
param.Type.CDefinition = change.Type.Name + " " + param.Type.pointers()
}
if change.Name != nil {
param.Name = change.Name.Value
}
}
function.Overloads = append(function.Overloads, overload)
return nil
}

func parseSignature(signature xmlSignature) (name string, ctype Type, err error) {
readingName := false
readingType := false
Expand Down Expand Up @@ -459,14 +508,14 @@ func (addRem *specAddRemSet) shouldInclude(pkgSpec *PackageSpec) bool {
return true
}

// NewSpecification creates a new specification based on an XML file.
func NewSpecification(file string) (*Specification, error) {
registry, err := readSpecFile(file)
// NewSpecification creates a new specification based on an XML registry.
func NewSpecification(registry xmlRegistry, overloads xmlOverloads) (*Specification, error) {
functions, err := parseFunctions(registry.Commands)
if err != nil {
return nil, err
}

functions, err := parseFunctions(registry.Commands)
functions, err = parseOverloads(functions, overloads)
if err != nil {
return nil, err
}
Expand Down
21 changes: 20 additions & 1 deletion tmpl/package.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package {{.Name}}
//glow:rmspace

{{define "paramsCDecl"}}{{range $i, $p := .}}{{if ne $i 0}}, {{end}}{{$p.Type.CType}} {{$p.CName}}{{end}}{{end}}
{{define "paramsCCall"}}{{range $i, $p := .}}{{if ne $i 0}}, {{end}}{{if $p.Type.IsDebugProc}}glowCDebugCallback{{else}}{{$p.CName}}{{end}}{{end}}{{end}}
{{define "paramsCCall"}}{{range $i, $p := .}}{{if ne $i 0}}, {{end}}{{if $p.Type.IsDebugProc}}glowCDebugCallback{{else}}{{if ge (len $p.Type.Cast) 1}}({{$p.Type.Cast}})({{end}}{{$p.CName}}{{if ge (len $p.Type.Cast) 1}}){{end}}{{end}}{{end}}{{end}}

{{define "paramsGoDecl"}}{{range $i, $p := .}}{{if ne $i 0}}, {{end}}{{$p.GoName}} {{$p.Type.GoType}}{{end}}{{end}}
{{define "paramsGoCall"}}{{range $i, $p := .}}{{if ne $i 0}}, {{end}}{{$p.Type.ConvertGoToC $p.GoName}}{{end}}{{end}}
Expand Down Expand Up @@ -66,6 +66,11 @@ package {{.Name}}
// static {{.Return.CType}} glow{{.GoName}}(GP{{toUpper .GoName}} fnptr{{if ge (len .Parameters) 1}}, {{end}}{{template "paramsCDecl" .Parameters}}) {
// {{if not .Return.IsVoid}}return {{end}}(*fnptr)({{template "paramsCCall" .Parameters}});
// }
// {{range .Overloads}}
// static {{.Return.CType}} glow{{.OverloadName}}(GP{{toUpper .GoName}} fnptr{{if ge (len .Parameters) 1}}, {{end}}{{template "paramsCDecl" .Parameters}}) {
// {{if not .Return.IsVoid}}return {{end}}(*fnptr)({{template "paramsCCall" .Parameters}});
// }
// {{end}}
// {{end}}
//
import "C"
Expand Down Expand Up @@ -95,6 +100,7 @@ func boolToInt(b bool) int {
}

{{define "bridgeCall"}}C.glow{{.GoName}}(gp{{.GoName}}{{if ge (len .Parameters) 1}}, {{end}}{{template "paramsGoCall" .Parameters}}){{end}}
{{define "overloadCall"}}C.glow{{.OverloadName}}(gp{{.GoName}}{{if ge (len .Parameters) 1}}, {{end}}{{template "paramsGoCall" .Parameters}}){{end}}
{{range .Functions}}
{{.Comment}}
func {{.GoName}}({{template "paramsGoDecl" .Parameters}}){{if not .Return.IsVoid}} {{.Return.GoType}}{{end}} {
Expand All @@ -107,6 +113,19 @@ func {{.GoName}}({{template "paramsGoDecl" .Parameters}}){{if not .Return.IsVoid
return {{.Return.ConvertCToGo "ret"}}
{{end}}
}
{{range .Overloads}}

func {{.OverloadName}}({{template "paramsGoDecl" .Parameters}}){{if not .Return.IsVoid}} {{.Return.GoType}}{{end}} {
{{range .Parameters}}
{{if .Type.IsDebugProc}}userDebugCallback = {{.GoName}}{{end}}
{{end}}
{{if .Return.IsVoid}}{{template "overloadCall" .}}
{{else}}
ret := {{template "overloadCall" .}}
return {{.Return.ConvertCToGo "ret"}}
{{end}}
}
{{end}}
{{end}}

//glow:keepspace
Expand Down
3 changes: 3 additions & 0 deletions type.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Type struct {
Name string // Name of the type without modifiers
PointerLevel int // Number of levels of declared indirection to the type
CDefinition string // Raw C definition
Cast string // Raw C cast in case conversion is necessary
}

// A Typedef describes a C typedef statement.
Expand Down Expand Up @@ -112,6 +113,8 @@ func (t Type) GoType() string {
case "GLDEBUGPROC", "GLDEBUGPROCARB", "GLDEBUGPROCKHR":
// Special case mapping to the type defined in debug.tmpl
return "DebugProc"
case "uintptr_t":
return t.pointers() + "uintptr"
}
return "unsafe.Pointer"
}
Expand Down
30 changes: 30 additions & 0 deletions type_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package main

import "testing"

func TestGoType(t *testing.T) {
tt := []struct {
in Type
expected string
}{
{
in: Type{
Name: "uintptr_t",
PointerLevel: 1,
CDefinition: "uintptr_t*",
Cast: "void *",
},
expected: "*uintptr",
},
}

for _, tc := range tt {
tc := tc
t.Run(tc.in.String(), func(t *testing.T) {
goType := tc.in.GoType()
if goType != tc.expected {
t.Errorf("expected <%s>, got <%s>", tc.expected, goType)
}
})
}
}
26 changes: 26 additions & 0 deletions xml/overload/gl.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<overloads>
<overload name="glDrawElements" overloadName="DrawElementsWithOffset">
<parameterChanges>
<change index="3">
<type name="uintptr_t" pointerLevel="0" />
</change>
</parameterChanges>
</overload>

<overload name="glVertexAttribPointer" overloadName="VertexAttribPointerWithOffset">
<parameterChanges>
<change index="5">
<name value="offset" />
<type name="uintptr_t" pointerLevel="0" />
</change>
</parameterChanges>
</overload>
<overload name="glGetVertexAttribPointerv" overloadName="GetVertexAttribPointerWithOffsetv">
<parameterChanges>
<change index="2">
<name value="offset" />
<type name="uintptr_t" pointerLevel="2" />
</change>
</parameterChanges>
</overload>
</overloads>