mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
289 lines
6.5 KiB
Go
289 lines
6.5 KiB
Go
|
|
// Copyright 2009 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.
|
||
|
|
|
||
|
|
// Parse input AST and prepare Prog structure.
|
||
|
|
|
||
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"debug/dwarf";
|
||
|
|
"fmt";
|
||
|
|
"go/ast";
|
||
|
|
"go/doc";
|
||
|
|
"go/parser";
|
||
|
|
"go/scanner";
|
||
|
|
"os";
|
||
|
|
)
|
||
|
|
|
||
|
|
// A Cref refers to an expression of the form C.xxx in the AST.
|
||
|
|
type Cref struct {
|
||
|
|
Name string;
|
||
|
|
Expr *ast.Expr;
|
||
|
|
Context string; // "type", "expr", or "call"
|
||
|
|
TypeName bool; // whether xxx is a C type name
|
||
|
|
DebugType dwarf.Type; // the type of xxx
|
||
|
|
}
|
||
|
|
|
||
|
|
// A Prog collects information about a cgo program.
|
||
|
|
type Prog struct {
|
||
|
|
AST *ast.File; // parsed AST
|
||
|
|
Preamble string; // C preamble (doc comment on import "C")
|
||
|
|
Crefs []*Cref;
|
||
|
|
}
|
||
|
|
|
||
|
|
func openProg(name string) *Prog {
|
||
|
|
p := new(Prog);
|
||
|
|
var err os.Error;
|
||
|
|
p.AST, err = parser.ParsePkgFile("", name, parser.ParseComments);
|
||
|
|
if err != nil {
|
||
|
|
if list, ok := err.(scanner.ErrorList); ok {
|
||
|
|
// If err is a scanner.ErrorList, its String will print just
|
||
|
|
// the first error and then (+n more errors).
|
||
|
|
// Instead, turn it into a new Error that will return
|
||
|
|
// details for all the errors.
|
||
|
|
for _, e := range list {
|
||
|
|
fmt.Fprintln(os.Stderr, e);
|
||
|
|
}
|
||
|
|
os.Exit(2);
|
||
|
|
}
|
||
|
|
fatal("parsing %s: %s", name, err);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Find the import "C" line and get any extra C preamble.
|
||
|
|
found := false;
|
||
|
|
for _, d := range p.AST.Decls {
|
||
|
|
d, ok := d.(*ast.GenDecl);
|
||
|
|
if !ok {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
for _, s := range d.Specs {
|
||
|
|
s, ok := s.(*ast.ImportSpec);
|
||
|
|
if !ok {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
found = true;
|
||
|
|
if s.Name != nil {
|
||
|
|
error(s.Path[0].Pos(), `cannot rename import "C"`);
|
||
|
|
}
|
||
|
|
if s.Doc != nil {
|
||
|
|
p.Preamble += doc.CommentText(s.Doc) + "\n";
|
||
|
|
}
|
||
|
|
else if len(d.Specs) == 1 && d.Doc != nil {
|
||
|
|
p.Preamble += doc.CommentText(d.Doc) + "\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if !found {
|
||
|
|
error(noPos, `cannot find import "C"`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Accumulate pointers to uses of C.x.
|
||
|
|
p.Crefs = make([]*Cref, 0, 8);
|
||
|
|
walk(p.AST, p, "prog");
|
||
|
|
return p;
|
||
|
|
}
|
||
|
|
|
||
|
|
func walk(x interface{}, p *Prog, context string) {
|
||
|
|
switch n := x.(type) {
|
||
|
|
case *ast.Expr:
|
||
|
|
if sel, ok := (*n).(*ast.SelectorExpr); ok {
|
||
|
|
// For now, assume that the only instance of capital C is
|
||
|
|
// when used as the imported package identifier.
|
||
|
|
// The parser should take care of scoping in the future,
|
||
|
|
// so that we will be able to distinguish a "top-level C"
|
||
|
|
// from a local C.
|
||
|
|
if l, ok := sel.X.(*ast.Ident); ok && l.Value == "C" {
|
||
|
|
i := len(p.Crefs);
|
||
|
|
if i >= cap(p.Crefs) {
|
||
|
|
new := make([]*Cref, 2*i);
|
||
|
|
for j, v := range p.Crefs {
|
||
|
|
new[j] = v;
|
||
|
|
}
|
||
|
|
p.Crefs = new;
|
||
|
|
}
|
||
|
|
p.Crefs = p.Crefs[0:i+1];
|
||
|
|
p.Crefs[i] = &Cref{
|
||
|
|
Name: sel.Sel.Value,
|
||
|
|
Expr: n,
|
||
|
|
Context: context
|
||
|
|
};
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
walk(*n, p, context);
|
||
|
|
|
||
|
|
// everything else just recurs
|
||
|
|
default:
|
||
|
|
error(noPos, "unexpected type %T in walk", x);
|
||
|
|
panic();
|
||
|
|
|
||
|
|
case nil:
|
||
|
|
|
||
|
|
// These are ordered and grouped to match ../../pkg/go/ast/ast.go
|
||
|
|
case *ast.Field:
|
||
|
|
walk(&n.Type, p, "type");
|
||
|
|
case *ast.BadExpr:
|
||
|
|
case *ast.Ident:
|
||
|
|
case *ast.Ellipsis:
|
||
|
|
case *ast.BasicLit:
|
||
|
|
case *ast.StringList:
|
||
|
|
case *ast.FuncLit:
|
||
|
|
walk(n.Type, p, "type");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
case *ast.CompositeLit:
|
||
|
|
walk(&n.Type, p, "type");
|
||
|
|
walk(n.Elts, p, "expr");
|
||
|
|
case *ast.ParenExpr:
|
||
|
|
walk(&n.X, p, context);
|
||
|
|
case *ast.SelectorExpr:
|
||
|
|
walk(&n.X, p, "selector");
|
||
|
|
case *ast.IndexExpr:
|
||
|
|
walk(&n.X, p, "expr");
|
||
|
|
walk(&n.Index, p, "expr");
|
||
|
|
if n.End != nil {
|
||
|
|
walk(&n.End, p, "expr");
|
||
|
|
}
|
||
|
|
case *ast.TypeAssertExpr:
|
||
|
|
walk(&n.X, p, "expr");
|
||
|
|
walk(&n.Type, p, "type");
|
||
|
|
case *ast.CallExpr:
|
||
|
|
walk(&n.Fun, p, "call");
|
||
|
|
walk(n.Args, p, "expr");
|
||
|
|
case *ast.StarExpr:
|
||
|
|
walk(&n.X, p, context);
|
||
|
|
case *ast.UnaryExpr:
|
||
|
|
walk(&n.X, p, "expr");
|
||
|
|
case *ast.BinaryExpr:
|
||
|
|
walk(&n.X, p, "expr");
|
||
|
|
walk(&n.Y, p, "expr");
|
||
|
|
case *ast.KeyValueExpr:
|
||
|
|
walk(&n.Key, p, "expr");
|
||
|
|
walk(&n.Value, p, "expr");
|
||
|
|
|
||
|
|
case *ast.ArrayType:
|
||
|
|
walk(&n.Len, p, "expr");
|
||
|
|
walk(&n.Elt, p, "type");
|
||
|
|
case *ast.StructType:
|
||
|
|
walk(n.Fields, p, "field");
|
||
|
|
case *ast.FuncType:
|
||
|
|
walk(n.Params, p, "field");
|
||
|
|
walk(n.Results, p, "field");
|
||
|
|
case *ast.InterfaceType:
|
||
|
|
walk(n.Methods, p, "field");
|
||
|
|
case *ast.MapType:
|
||
|
|
walk(&n.Key, p, "type");
|
||
|
|
walk(&n.Value, p, "type");
|
||
|
|
case *ast.ChanType:
|
||
|
|
walk(&n.Value, p, "type");
|
||
|
|
|
||
|
|
case *ast.BadStmt:
|
||
|
|
case *ast.DeclStmt:
|
||
|
|
walk(n.Decl, p, "decl");
|
||
|
|
case *ast.EmptyStmt:
|
||
|
|
case *ast.LabeledStmt:
|
||
|
|
walk(n.Stmt, p, "stmt");
|
||
|
|
case *ast.ExprStmt:
|
||
|
|
walk(&n.X, p, "expr");
|
||
|
|
case *ast.IncDecStmt:
|
||
|
|
walk(&n.X, p, "expr");
|
||
|
|
case *ast.AssignStmt:
|
||
|
|
walk(n.Lhs, p, "expr");
|
||
|
|
walk(n.Rhs, p, "expr");
|
||
|
|
case *ast.GoStmt:
|
||
|
|
walk(&n.Call, p, "expr");
|
||
|
|
case *ast.DeferStmt:
|
||
|
|
walk(&n.Call, p, "expr");
|
||
|
|
case *ast.ReturnStmt:
|
||
|
|
walk(n.Results, p, "expr");
|
||
|
|
case *ast.BranchStmt:
|
||
|
|
case *ast.BlockStmt:
|
||
|
|
walk(n.List, p, "stmt");
|
||
|
|
case *ast.IfStmt:
|
||
|
|
walk(n.Init, p, "stmt");
|
||
|
|
walk(&n.Cond, p, "expr");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
walk(n.Else, p, "stmt");
|
||
|
|
case *ast.CaseClause:
|
||
|
|
walk(n.Values, p, "expr");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
case *ast.SwitchStmt:
|
||
|
|
walk(n.Init, p, "stmt");
|
||
|
|
walk(&n.Tag, p, "expr");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
case *ast.TypeCaseClause:
|
||
|
|
walk(n.Types, p, "type");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
case *ast.TypeSwitchStmt:
|
||
|
|
walk(n.Init, p, "stmt");
|
||
|
|
walk(n.Assign, p, "stmt");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
case *ast.CommClause:
|
||
|
|
walk(n.Lhs, p, "expr");
|
||
|
|
walk(n.Rhs, p, "expr");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
case *ast.SelectStmt:
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
case *ast.ForStmt:
|
||
|
|
walk(n.Init, p, "stmt");
|
||
|
|
walk(&n.Cond, p, "expr");
|
||
|
|
walk(n.Post, p, "stmt");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
case *ast.RangeStmt:
|
||
|
|
walk(&n.Key, p, "expr");
|
||
|
|
walk(&n.Value, p, "expr");
|
||
|
|
walk(&n.X, p, "expr");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
|
||
|
|
case *ast.ImportSpec:
|
||
|
|
case *ast.ValueSpec:
|
||
|
|
walk(&n.Type, p, "type");
|
||
|
|
walk(n.Values, p, "expr");
|
||
|
|
case *ast.TypeSpec:
|
||
|
|
walk(&n.Type, p, "type");
|
||
|
|
|
||
|
|
case *ast.BadDecl:
|
||
|
|
case *ast.GenDecl:
|
||
|
|
walk(n.Specs, p, "spec");
|
||
|
|
case *ast.FuncDecl:
|
||
|
|
if n.Recv != nil {
|
||
|
|
walk(n.Recv, p, "field");
|
||
|
|
}
|
||
|
|
walk(n.Type, p, "type");
|
||
|
|
walk(n.Body, p, "stmt");
|
||
|
|
|
||
|
|
case *ast.File:
|
||
|
|
walk(n.Decls, p, "decl");
|
||
|
|
|
||
|
|
case *ast.Package:
|
||
|
|
for _, f := range n.Files {
|
||
|
|
walk(f, p, "file");
|
||
|
|
}
|
||
|
|
|
||
|
|
case []ast.Decl:
|
||
|
|
for _, d := range n {
|
||
|
|
walk(d, p, context);
|
||
|
|
}
|
||
|
|
case []ast.Expr:
|
||
|
|
for i := range n {
|
||
|
|
walk(&n[i], p, context);
|
||
|
|
}
|
||
|
|
case []*ast.Field:
|
||
|
|
for _, f := range n {
|
||
|
|
walk(f, p, context);
|
||
|
|
}
|
||
|
|
case []ast.Stmt:
|
||
|
|
for _, s := range n {
|
||
|
|
walk(s, p, context);
|
||
|
|
}
|
||
|
|
case []ast.Spec:
|
||
|
|
for _, s := range n {
|
||
|
|
walk(s, p, context);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|