Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
165 changes: 122 additions & 43 deletions cl/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,48 +32,132 @@ func initMathBig(_ *gogen.Package, conf *gogen.Config, big gogen.PkgRef) {
conf.UntypedBigFloat = big.Ref("UntypedBigfloat").Type().(*types.Named)
}

func initBuiltinFns(builtin *types.Package, scope *types.Scope, pkg gogen.PkgRef, fns []string) {
for _, fn := range fns {
fnTitle := string(fn[0]-'a'+'A') + fn[1:]
scope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, fn, pkg.Ref(fnTitle)))
}
type Builtin struct {
types.Object
name string
pkg string
sym string
fn bool
}

func initBuiltin(_ *gogen.Package, builtin *types.Package, os, fmt, ng, osx, buil, reflect gogen.PkgRef) {
scope := builtin.Scope()
if ng.Types != nil {
typs := []string{"bigint", "bigrat", "bigfloat"}
for _, typ := range typs {
name := string(typ[0]-('a'-'A')) + typ[1:]
scope.Insert(types.NewTypeName(token.NoPos, builtin, typ, ng.Ref(name).Type()))
}
scope.Insert(types.NewTypeName(token.NoPos, builtin, "uint128", ng.Ref("Uint128").Type()))
scope.Insert(types.NewTypeName(token.NoPos, builtin, "int128", ng.Ref("Int128").Type()))
}
if fmt.Types != nil {
scope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, "echo", fmt.Ref("Println")))
initBuiltinFns(builtin, scope, fmt, []string{
"print", "println", "printf", "errorf",
"fprint", "fprintln", "fprintf",
"sprint", "sprintln", "sprintf",
})
}
if os.Types != nil {
initBuiltinFns(builtin, scope, os, []string{
"open", "create",
})
func (t *Builtin) Parent() *types.Scope {
return Universe
}
func (t *Builtin) Pos() token.Pos {
return token.NoPos
}
func (t *Builtin) Pkg() *types.Package {
return nil
}
func (t *Builtin) Name() string {
return t.name
}
func (t *Builtin) Type() types.Type {
return types.Typ[types.Invalid]
}
func (t *Builtin) Exported() bool {
return false
}
func (t *Builtin) Id() string {
return "_." + t.name
}
func (t *Builtin) String() string {
return "builtin " + t.name
}
func (t *Builtin) Sym() string {
return t.pkg + "." + t.sym
}
func (t *Builtin) IsFunc() bool {
return t.fn
}

var (
Universe *types.Scope
)

var builtinTypes = [...]struct {
name string
pkg string
sym string
}{
{"bigint", "github.com/qiniu/x/xgo/ng", ""},
{"bigrat", "github.com/qiniu/x/xgo/ng", ""},
{"bigfloat", "github.com/qiniu/x/xgo/ng", ""},
{"int128", "github.com/qiniu/x/xgo/ng", ""},
{"uint128", "github.com/qiniu/x/xgo/ng", ""},
}

var builtinFuncs = [...]struct {
name string
pkg string
sym string
}{
{"errorln", "github.com/qiniu/x/osx", ""},
{"fatal", "github.com/qiniu/x/osx", ""},
{"lines", "github.com/qiniu/x/osx", ""},
{"blines", "github.com/qiniu/x/osx", "BLines"},
{"newRange", "github.com/qiniu/x/xgo", "NewRange__0"},
{"echo", "fmt", "Println"},
{"print", "fmt", ""},
{"println", "fmt", ""},
{"printf", "fmt", ""},
{"errorf", "fmt", ""},
{"fprint", "fmt", ""},
{"fprintln", "fmt", ""},
{"sprint", "fmt", ""},
{"sprintln", "fmt", ""},
{"sprintf", "fmt", ""},
{"open", "os", ""},
{"create", "os", ""},
{"type", "reflect", "TypeOf"},
}

type defSym struct {
name string
sym string
fn bool
}

var (
builtinSym map[string][]defSym
)

func insertBuiltin(name, pkg, sym string, fn bool) {
if sym == "" {
sym = string(name[0]-('a'-'A')) + name[1:]
}
if osx.Types != nil {
initBuiltinFns(builtin, scope, osx, []string{
"lines", "errorln", "fatal",
})
scope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, "blines", osx.Ref("BLines")))
builtinSym[pkg] = append(builtinSym[pkg], defSym{name: name, sym: sym, fn: fn})
obj := &Builtin{name: name, pkg: pkg, sym: sym, fn: fn}
Universe.Insert(obj)
}

func init() {
Universe = types.NewScope(nil, 0, 0, "universe")
builtinSym = make(map[string][]defSym)
for _, def := range builtinTypes {
insertBuiltin(def.name, def.pkg, def.sym, false)
}
if reflect.Types != nil {
scope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, "type", reflect.Ref("TypeOf")))
for _, def := range builtinFuncs {
insertBuiltin(def.name, def.pkg, def.sym, true)
}
if buil.Types != nil {
scope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, "newRange", buil.Ref("NewRange__0")))
}

func initBuiltin(pkg *gogen.Package, builtin *types.Package, conf *gogen.Config) {
scope := builtin.Scope()
for im, defs := range builtinSym {
if p := pkg.TryImport(im); p.Types != nil {
for _, def := range defs {
obj := p.Ref(def.sym)
if def.fn {
scope.Insert(gogen.NewOverloadFunc(token.NoPos, builtin, def.name, obj))
} else {
scope.Insert(types.NewTypeName(token.NoPos, builtin, def.name, obj.Type()))
}
if rec, ok := conf.Recorder.(*goxRecorder); ok {
rec.Builtin(def.name, obj)
}
}
}
}
scope.Insert(types.NewTypeName(token.NoPos, builtin, "any", gogen.TyEmptyInterface))
}
Expand All @@ -84,11 +168,6 @@ const (

func (ctx *pkgCtx) newBuiltinDefault(pkg *gogen.Package, conf *gogen.Config) *types.Package {
builtin := types.NewPackage("", "")
fmt := pkg.Import("fmt")
os := pkg.TryImport("os")
reflect := pkg.TryImport("reflect")
osx := pkg.TryImport(osxPkgPath)
buil := pkg.TryImport("github.com/qiniu/x/xgo")
ng := pkg.TryImport("github.com/qiniu/x/xgo/ng")
strx := pkg.TryImport("github.com/qiniu/x/stringutil")
stringslice := pkg.TryImport("github.com/qiniu/x/stringslice")
Expand All @@ -105,7 +184,7 @@ func (ctx *pkgCtx) newBuiltinDefault(pkg *gogen.Package, conf *gogen.Config) *ty
}
}
}
initBuiltin(pkg, builtin, os, fmt, ng, osx, buil, reflect)
initBuiltin(pkg, builtin, conf)
gogen.InitBuiltin(pkg, builtin, conf)
if strx.Types != nil {
ti := pkg.BuiltinTI(types.Typ[types.String])
Expand Down
34 changes: 34 additions & 0 deletions cl/builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,4 +639,38 @@ func TestClassFileEnd(t *testing.T) {
}
}

func TestUniverse(t *testing.T) {
echo := Universe.Lookup("echo")
if echo == nil {
t.Fatal("not found")
}
if echo.Parent() != Universe {
t.Fatal("bad parent")
}
if echo.Pos() != token.NoPos {
t.Fatal("must nopos")
}
if echo.Pkg() != nil {
t.Fatal("must nil")
}
if echo.Type() != types.Typ[types.Invalid] {
t.Fatal("must invalid")
}
if echo.Name() != "echo" {
t.Fatal("bad name")
}
if echo.Id() != "_.echo" {
t.Fatal("bad id")
}
if echo.String() != "builtin echo" {
t.Fatal("bad string")
}
if b, ok := echo.(*Builtin); !ok || b.Sym() != "fmt.Println" {
t.Fatal("bad sym")
}
if echo.(*Builtin).Exported() != false {
t.Fatal("bad exported")
}
}

// -----------------------------------------------------------------------------
3 changes: 3 additions & 0 deletions cl/cltest/recorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,6 @@ func (info gopRecorder) Select(e *ast.SelectorExpr, sel *types.Selection) {
// *ast.RangeStmt
func (info gopRecorder) Scope(n ast.Node, scope *types.Scope) {
}

func (info gopRecorder) Builtin(name string, obj types.Object) {
}
3 changes: 3 additions & 0 deletions cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ type Recorder interface {
// *ast.LambdaExpr2
//
Scope(ast.Node, *types.Scope)

// Go+ builtin name object
Builtin(name string, obj types.Object)
}

// -----------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions cl/internal/typesutil/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,9 @@ func NewTypeAndValueForObject(obj types.Object) (ret types.TypeAndValue) {
(*TypeAndValue)(unsafe.Pointer(&ret)).mode = mode
return
}

func NewTypeAndValueForBuiltin(obj types.Object) (ret types.TypeAndValue) {
ret.Type = obj.Type()
(*TypeAndValue)(unsafe.Pointer(&ret)).mode = Builtin
return
}
4 changes: 4 additions & 0 deletions cl/internal/typesutil/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,8 @@ func TestTypeAndValue(t *testing.T) {
if !ret.IsBuiltin() {
t.Fatal("NewTypeAndValueForObject: not builtin?")
}
ret = NewTypeAndValueForBuiltin(types.Universe.Lookup("len"))
if !ret.IsBuiltin() {
t.Fatal("NewTypeAndValueForObject: not builtin?")
}
}
40 changes: 34 additions & 6 deletions cl/recorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ type goxRecorder struct {
types map[ast.Expr]types.TypeAndValue
referDefs map[*ast.Ident]ast.Node
referUses map[string][]*ast.Ident
builtin map[*ast.Ident]struct{}
}

func newRecorder(rec Recorder) *goxRecorder {
types := make(map[ast.Expr]types.TypeAndValue)
referDefs := make(map[*ast.Ident]ast.Node)
referUses := make(map[string][]*ast.Ident)
return &goxRecorder{rec, types, referDefs, referUses}
builtin := make(map[*ast.Ident]struct{})
return &goxRecorder{Recorder: rec, types: types, referDefs: referDefs,
referUses: referUses, builtin: builtin}
}

// Refer uses maps identifiers to name for ast.OverloadFuncDecl.
Expand Down Expand Up @@ -127,23 +130,22 @@ func (p *goxRecorder) Member(id ast.Node, obj types.Object) {
p.Type(v, tv)
}
case *ast.Ident: // it's in a classfile and impossible converted from Go
p.Use(v, obj)
p.Type(v, typesutil.NewTypeAndValueForObject(obj))
p.recordIdent(v, obj)
}
}

func (p *goxRecorder) Call(id ast.Node, obj types.Object) {
switch v := id.(type) {
case *ast.Ident:
p.Use(v, obj)
p.Type(v, typesutil.NewTypeAndValueForObject(obj))
p.recordIdent(v, obj)
case *ast.SelectorExpr:
p.Use(v.Sel, obj)
p.Type(v, typesutil.NewTypeAndValueForObject(obj))
case *ast.CallExpr:
switch id := v.Fun.(type) {
case *ast.Ident:
p.Use(id, obj)
p.recordIdent(id, obj)
return
case *ast.SelectorExpr:
p.Use(id.Sel, obj)
}
Expand Down Expand Up @@ -253,10 +255,36 @@ func (rec *goxRecorder) recordType(typ ast.Expr, t types.Type) {
}

func (rec *goxRecorder) recordIdent(ident *ast.Ident, obj types.Object) {
if _, ok := rec.builtin[ident]; ok {
return
}
if o, ok := isBuiltinFunc(ident, obj); ok {
rec.builtin[ident] = struct{}{}
rec.Use(ident, o)
rec.Type(ident, typesutil.NewTypeAndValueForBuiltin(o))
return
}
rec.Use(ident, obj)
rec.Type(ident, typesutil.NewTypeAndValueForObject(obj))
}

func isBuiltinFunc(ident *ast.Ident, obj types.Object) (types.Object, bool) {
if pkg := obj.Pkg(); pkg == nil || (pkg.Path() == "" && pkg.Name() == "") {
if o := Universe.Lookup(ident.Name); o != nil && o.(*Builtin).IsFunc() {
return o, true
}
switch ident.Name {
case "append", "cap", "clear", "close", "complex", "copy",
"delete", "imag", "len", "make", "max", "min", "new",
"panic", "real", "recover":
if o := types.Universe.Lookup(ident.Name); o != nil {
return o, true
}
}
}
return nil, false
}

func (rec *goxRecorder) recordExpr(ctx *blockCtx, expr ast.Expr, _ bool) {
switch v := expr.(type) {
case *ast.Ident:
Expand Down
13 changes: 13 additions & 0 deletions x/typesutil/gopinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ type Info struct {

// Overloads maps identifiers to the overload decl object.
Overloads map[*ast.Ident]types.Object

// Builtins maps identifiers the Go+ builtin name object.
Builtins map[string]types.Object
}

// ObjectOf returns the object denoted by the specified id,
Expand Down Expand Up @@ -188,6 +191,9 @@ type xgoRecorder struct {

// NewRecorder creates a new recorder for cl.NewPackage.
func NewRecorder(info *Info) cl.Recorder {
if info == nil {
return nil
}
return xgoRecorder{info}
}

Expand Down Expand Up @@ -334,4 +340,11 @@ func (info xgoRecorder) Scope(n ast.Node, scope *types.Scope) {
}
}

// Go+ builtin name object
func (info xgoRecorder) Builtin(name string, obj types.Object) {
if info.Builtins != nil {
info.Builtins[name] = obj
}
}

// -----------------------------------------------------------------------------
Loading