317 lines
6.5 KiB
Go
317 lines
6.5 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build ignore
|
|
|
|
// The gendebug program takes gl.go and generates a version of it
|
|
// where each function includes tracing code that writes its arguments
|
|
// to the standard log.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/format"
|
|
"go/parser"
|
|
"go/printer"
|
|
"go/token"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
var outfile = flag.String("o", "", "result will be written to the file instead of stdout.")
|
|
|
|
var fset = new(token.FileSet)
|
|
|
|
func typeString(t ast.Expr) string {
|
|
buf := new(bytes.Buffer)
|
|
printer.Fprint(buf, fset, t)
|
|
return buf.String()
|
|
}
|
|
|
|
func typePrinter(t string) string {
|
|
switch t {
|
|
case "[]float32", "[]byte":
|
|
return "len(%d)"
|
|
}
|
|
return "%v"
|
|
}
|
|
|
|
func typePrinterArg(t, name string) string {
|
|
switch t {
|
|
case "[]float32", "[]byte":
|
|
return "len(" + name + ")"
|
|
}
|
|
return name
|
|
}
|
|
|
|
func die(err error) {
|
|
fmt.Fprintf(os.Stderr, err.Error())
|
|
os.Exit(1)
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
f, err := parser.ParseFile(fset, "consts.go", nil, parser.ParseComments)
|
|
if err != nil {
|
|
die(err)
|
|
}
|
|
entries := enum(f)
|
|
|
|
f, err = parser.ParseFile(fset, "gl.go", nil, parser.ParseComments)
|
|
if err != nil {
|
|
die(err)
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
fmt.Fprint(buf, preamble)
|
|
|
|
fmt.Fprintf(buf, "func (v Enum) String() string {\n")
|
|
fmt.Fprintf(buf, "\tswitch v {\n")
|
|
for _, e := range dedup(entries) {
|
|
fmt.Fprintf(buf, "\tcase 0x%x: return %q\n", e.value, e.name)
|
|
}
|
|
fmt.Fprintf(buf, "\t%s\n", `default: return fmt.Sprintf("gl.Enum(0x%x)", uint32(v))`)
|
|
fmt.Fprintf(buf, "\t}\n")
|
|
fmt.Fprintf(buf, "}\n\n")
|
|
|
|
for _, d := range f.Decls {
|
|
// Before:
|
|
// func StencilMask(mask uint32) {
|
|
// C.glStencilMask(C.GLuint(mask))
|
|
// }
|
|
//
|
|
// After:
|
|
// func StencilMask(mask uint32) {
|
|
// defer func() {
|
|
// errstr := errDrain()
|
|
// log.Printf("gl.StencilMask(%v) %v", mask, errstr)
|
|
// }()
|
|
// C.glStencilMask(C.GLuint(mask))
|
|
// }
|
|
fn, ok := d.(*ast.FuncDecl)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if fn.Recv != nil {
|
|
continue
|
|
}
|
|
|
|
var (
|
|
params []string
|
|
paramTypes []string
|
|
results []string
|
|
resultTypes []string
|
|
)
|
|
|
|
// Print function signature.
|
|
fmt.Fprintf(buf, "func %s(", fn.Name.Name)
|
|
for i, p := range fn.Type.Params.List {
|
|
if i > 0 {
|
|
fmt.Fprint(buf, ", ")
|
|
}
|
|
ty := typeString(p.Type)
|
|
for i, n := range p.Names {
|
|
if i > 0 {
|
|
fmt.Fprint(buf, ", ")
|
|
}
|
|
fmt.Fprintf(buf, "%s ", n.Name)
|
|
params = append(params, n.Name)
|
|
paramTypes = append(paramTypes, ty)
|
|
}
|
|
fmt.Fprint(buf, ty)
|
|
}
|
|
fmt.Fprintf(buf, ") (")
|
|
if fn.Type.Results != nil {
|
|
for i, r := range fn.Type.Results.List {
|
|
if i > 0 {
|
|
fmt.Fprint(buf, ", ")
|
|
}
|
|
ty := typeString(r.Type)
|
|
if len(r.Names) == 0 {
|
|
name := fmt.Sprintf("r%d", i)
|
|
fmt.Fprintf(buf, "%s ", name)
|
|
results = append(results, name)
|
|
resultTypes = append(resultTypes, ty)
|
|
}
|
|
for i, n := range r.Names {
|
|
if i > 0 {
|
|
fmt.Fprint(buf, ", ")
|
|
}
|
|
fmt.Fprintf(buf, "%s ", n.Name)
|
|
results = append(results, n.Name)
|
|
resultTypes = append(resultTypes, ty)
|
|
}
|
|
fmt.Fprint(buf, ty)
|
|
}
|
|
}
|
|
fmt.Fprintf(buf, ") {\n")
|
|
|
|
// gl.GetError is used by errDrain, which will be made part of
|
|
// all functions. So do not apply it to gl.GetError to avoid
|
|
// infinite recursion.
|
|
skip := fn.Name.Name == "GetError"
|
|
|
|
if !skip {
|
|
// Insert a defer block for tracing.
|
|
fmt.Fprintf(buf, "defer func() {\n")
|
|
fmt.Fprintf(buf, "\terrstr := errDrain()\n")
|
|
switch fn.Name.Name {
|
|
case "GetUniformLocation", "GetAttribLocation":
|
|
fmt.Fprintf(buf, "\tr0.name = name\n")
|
|
}
|
|
fmt.Fprintf(buf, "\tlog.Printf(\"gl.%s(", fn.Name.Name)
|
|
for i, p := range paramTypes {
|
|
if i > 0 {
|
|
fmt.Fprint(buf, ", ")
|
|
}
|
|
fmt.Fprint(buf, typePrinter(p))
|
|
}
|
|
fmt.Fprintf(buf, ") ")
|
|
if len(resultTypes) > 1 {
|
|
fmt.Fprint(buf, "(")
|
|
}
|
|
for i, r := range resultTypes {
|
|
if i > 0 {
|
|
fmt.Fprint(buf, ", ")
|
|
}
|
|
fmt.Fprint(buf, typePrinter(r))
|
|
}
|
|
if len(resultTypes) > 1 {
|
|
fmt.Fprint(buf, ") ")
|
|
}
|
|
fmt.Fprintf(buf, "%%v\"")
|
|
for i, p := range paramTypes {
|
|
fmt.Fprintf(buf, ", %s", typePrinterArg(p, params[i]))
|
|
}
|
|
for i, r := range resultTypes {
|
|
fmt.Fprintf(buf, ", %s", typePrinterArg(r, results[i]))
|
|
}
|
|
fmt.Fprintf(buf, ", errstr)\n")
|
|
fmt.Fprintf(buf, "}()\n")
|
|
}
|
|
|
|
// Print original body of function.
|
|
for _, s := range fn.Body.List {
|
|
printer.Fprint(buf, fset, s)
|
|
fmt.Fprintf(buf, "\n")
|
|
}
|
|
fmt.Fprintf(buf, "}\n\n")
|
|
}
|
|
|
|
b, err := format.Source(buf.Bytes())
|
|
if err != nil {
|
|
os.Stdout.Write(buf.Bytes())
|
|
die(err)
|
|
}
|
|
|
|
if *outfile == "" {
|
|
os.Stdout.Write(b)
|
|
return
|
|
}
|
|
if err := ioutil.WriteFile(*outfile, b, 0666); err != nil {
|
|
die(err)
|
|
}
|
|
}
|
|
|
|
const preamble = `// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Generated from gl.go using go generate. DO NOT EDIT.
|
|
// See doc.go for details.
|
|
|
|
// +build linux darwin
|
|
// +build gldebug
|
|
|
|
package gl
|
|
|
|
// #include "work.h"
|
|
import "C"
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"math"
|
|
"unsafe"
|
|
)
|
|
|
|
func errDrain() string {
|
|
var errs []Enum
|
|
for {
|
|
e := GetError()
|
|
if e == 0 {
|
|
break
|
|
}
|
|
errs = append(errs, e)
|
|
}
|
|
if len(errs) > 0 {
|
|
return fmt.Sprintf(" error: %v", errs)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
`
|
|
|
|
type entry struct {
|
|
name string
|
|
value int
|
|
}
|
|
|
|
// enum builds a list of all GL constants that make up the gl.Enum type.
|
|
func enum(f *ast.File) []entry {
|
|
var entries []entry
|
|
for _, d := range f.Decls {
|
|
gendecl, ok := d.(*ast.GenDecl)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if gendecl.Tok != token.CONST {
|
|
continue
|
|
}
|
|
for _, s := range gendecl.Specs {
|
|
v, ok := s.(*ast.ValueSpec)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if len(v.Names) != 1 || len(v.Values) != 1 {
|
|
continue
|
|
}
|
|
val, err := strconv.ParseInt(v.Values[0].(*ast.BasicLit).Value, 0, 32)
|
|
if err != nil {
|
|
log.Fatalf("enum %s: %v", v.Names[0].Name, err)
|
|
}
|
|
entries = append(entries, entry{v.Names[0].Name, int(val)})
|
|
}
|
|
}
|
|
return entries
|
|
}
|
|
|
|
func dedup(entries []entry) []entry {
|
|
// Find all duplicates. Use "%d" as the name of any value with duplicates.
|
|
seen := make(map[int]int)
|
|
for _, e := range entries {
|
|
seen[e.value]++
|
|
}
|
|
var dedup []entry
|
|
for _, e := range entries {
|
|
switch seen[e.value] {
|
|
case 0: // skip, already here
|
|
case 1:
|
|
dedup = append(dedup, e)
|
|
default:
|
|
// value is duplicated
|
|
dedup = append(dedup, entry{fmt.Sprintf("%d", e.value), e.value})
|
|
seen[e.value] = 0
|
|
}
|
|
}
|
|
return dedup
|
|
}
|