func (f *Function) addParamObj(obj types.Object) *Parameter { name := obj.Name() if name == "" { name = fmt.Sprintf("arg%d", len(f.Params)) } param := f.addParam(name, obj.Type(), obj.Pos()) param.object = obj return param }
// addSpilledParam declares a parameter that is pre-spilled to the // stack; the function body will load/store the spilled location. // Subsequent lifting will eliminate spills where possible. // func (f *Function) addSpilledParam(obj types.Object) { param := f.addParamObj(obj) spill := &Alloc{Comment: obj.Name()} spill.setType(types.NewPointer(obj.Type())) spill.setPos(obj.Pos()) f.objects[obj] = spill f.Locals = append(f.Locals, spill) f.emit(spill) f.emit(&Store{Addr: spill, Val: param}) }
func formatMember(obj types.Object, maxname int) string { var buf bytes.Buffer fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name()) switch obj := obj.(type) { case *types.Const: fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Pkg(), obj.Type()), obj.Val().String()) case *types.Func: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type())) case *types.TypeName: // Abbreviate long aggregate type names. var abbrev string switch t := obj.Type().Underlying().(type) { case *types.Interface: if t.NumMethods() > 1 { abbrev = "interface{...}" } case *types.Struct: if t.NumFields() > 1 { abbrev = "struct{...}" } } if abbrev == "" { fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type().Underlying())) } else { fmt.Fprintf(&buf, " %s", abbrev) } case *types.Var: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type())) } return buf.String() }
func (p *printer) printObj(obj types.Object) { p.printf("%s", obj.Name()) // don't write untyped types (for constants) if typ := obj.Type(); typed(typ) { p.print(" ") p.writeType(p.pkg, typ) } // write constant value if obj, ok := obj.(*types.Const); ok { p.printf(" = %s", obj.Val()) } }
func (c *compiler) newStackVarEx(argument int, stackf *LLVMValue, v types.Object, value llvm.Value, name string) (stackvalue llvm.Value, stackvar *LLVMValue) { typ := v.Type() // We need to put alloca instructions in the top block or the values // displayed when inspecting these variables in a debugger will be // completely messed up. curBlock := c.builder.GetInsertBlock() if p := curBlock.Parent(); !p.IsNil() { fb := p.FirstBasicBlock() fi := fb.FirstInstruction() if !fb.IsNil() && !fi.IsNil() { c.builder.SetInsertPointBefore(fi) } } old := c.builder.CurrentDebugLocation() c.builder.SetCurrentDebugLocation(llvm.Value{}) stackvalue = c.builder.CreateAlloca(c.types.ToLLVM(typ), name) // For arguments we want to insert the store instruction // without debug information to ensure that they are executed // (and hence have proper values) before the debugger hits the // first line in a function. if argument == 0 { c.builder.SetCurrentDebugLocation(old) c.builder.SetInsertPointAtEnd(curBlock) } if !value.IsNil() { c.builder.CreateStore(value, stackvalue) } c.builder.SetCurrentDebugLocation(old) c.builder.SetInsertPointAtEnd(curBlock) ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ)) stackvar = ptrvalue.makePointee() stackvar.stack = stackf c.objectdata[v].Value = stackvar file := c.fileset.File(v.Pos()) tag := llvm.DW_TAG_auto_variable if argument > 0 { tag = llvm.DW_TAG_arg_variable } ld := llvm.NewLocalVariableDescriptor(tag) ld.Argument = uint32(argument) ld.Line = uint32(file.Line(v.Pos())) ld.Name = name ld.File = &llvm.ContextDescriptor{llvm.FileDescriptor(file.Name())} ld.Type = c.tollvmDebugDescriptor(typ) ld.Context = c.currentDebugContext() c.builder.InsertDeclare(c.module.Module, llvm.MDNode([]llvm.Value{stackvalue}), c.debug_info.MDNode(ld)) return stackvalue, stackvar }
// addSpilledParam declares a parameter that is pre-spilled to the // stack; the function body will load/store the spilled location. // Subsequent lifting will eliminate spills where possible. // func (f *Function) addSpilledParam(obj types.Object) { name := obj.Name() param := f.addParam(name, obj.Type()) spill := &Alloc{ Name_: name + "~", // "~" means "spilled" Type_: pointer(obj.Type()), pos: obj.Pos(), } f.objects[obj] = spill f.Locals = append(f.Locals, spill) f.emit(spill) f.emit(&Store{Addr: spill, Val: param}) }
func (p *exporter) obj(obj types.Object) { if trace { p.tracef("object %s {\n", obj.Name()) defer p.tracef("}\n") } switch obj := obj.(type) { case *types.Const: p.int(constTag) p.string(obj.Name()) p.typ(obj.Type()) p.value(obj.Val()) case *types.TypeName: p.int(typeTag) // name is written by corresponding named type p.typ(obj.Type().(*types.Named)) case *types.Var: p.int(varTag) p.string(obj.Name()) p.typ(obj.Type()) case *types.Func: p.int(funcTag) p.string(obj.Name()) p.typ(obj.Type()) default: panic(fmt.Sprintf("unexpected object type %T", obj)) } }
func (c *compiler) newStackVarEx(argument int, stackf *LLVMValue, v types.Object, value llvm.Value, name string) (stackvalue llvm.Value, stackvar *LLVMValue) { typ := v.Type() // We need to put alloca instructions in the top block or the values // displayed when inspecting these variables in a debugger will be // completely messed up. curBlock := c.builder.GetInsertBlock() if p := curBlock.Parent(); !p.IsNil() { fb := p.FirstBasicBlock() fi := fb.FirstInstruction() if !fb.IsNil() && !fi.IsNil() { c.builder.SetInsertPointBefore(fi) } } old := c.builder.CurrentDebugLocation() c.builder.SetCurrentDebugLocation(llvm.Value{}) stackvalue = c.builder.CreateAlloca(c.types.ToLLVM(typ), name) // For arguments we want to insert the store instruction // without debug information to ensure that they are executed // (and hence have proper values) before the debugger hits the // first line in a function. if argument == 0 { c.builder.SetCurrentDebugLocation(old) c.builder.SetInsertPointAtEnd(curBlock) } if !value.IsNil() { c.builder.CreateStore(value, stackvalue) } c.builder.SetCurrentDebugLocation(old) c.builder.SetInsertPointAtEnd(curBlock) ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ)) stackvar = ptrvalue.makePointee() stackvar.stack = stackf c.objectdata[v].Value = stackvar // Generate debug metadata (will return nil // if debug-data generation is disabled). if descriptor := c.createLocalVariableMetadata(v, argument); descriptor != nil { c.builder.InsertDeclare( c.module.Module, llvm.MDNode([]llvm.Value{stackvalue}), c.debug_info.MDNode(descriptor), ) } return stackvalue, stackvar }
func (w *Walker) emitObj(obj types.Object) { switch obj := obj.(type) { case *types.Const: w.emitf("const %s %s", obj.Name(), w.typeString(obj.Type())) w.emitf("const %s = %s", obj.Name(), obj.Val()) case *types.Var: w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type())) case *types.TypeName: w.emitType(obj) case *types.Func: w.emitFunc(obj) default: panic("unknown object: " + obj.String()) } }
func (p *printer) printObj(obj types.Object) { p.print(obj.Name()) typ, basic := obj.Type().Underlying().(*types.Basic) if basic && typ.Info()&types.IsUntyped != 0 { // don't write untyped types } else { p.print(" ") p.writeType(p.pkg, obj.Type()) } if obj, ok := obj.(*types.Const); ok { floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0 p.print(" = ") p.print(valString(obj.Val(), floatFmt)) } }
func (c *funcContext) initType(o types.Object) { if _, isInterface := o.Type().Underlying().(*types.Interface); !isInterface { writeMethodSet := func(t types.Type) { methodSet := types.NewMethodSet(t) if methodSet.Len() == 0 { return } methods := make([]string, methodSet.Len()) for i := range methods { method := methodSet.At(i) pkgPath := "" if !method.Obj().Exported() { pkgPath = method.Obj().Pkg().Path() } t := method.Type().(*types.Signature) embeddedIndex := -1 if len(method.Index()) > 1 { embeddedIndex = method.Index()[0] } methods[i] = fmt.Sprintf(`["%s", "%s", %s, %s, %t, %d]`, method.Obj().Name(), pkgPath, c.typeArray(t.Params()), c.typeArray(t.Results()), t.Variadic(), embeddedIndex) } c.Printf("%s.methods = [%s];", c.typeName(t), strings.Join(methods, ", ")) } writeMethodSet(o.Type()) writeMethodSet(types.NewPointer(o.Type())) } switch t := o.Type().Underlying().(type) { case *types.Array, *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Slice, *types.Signature, *types.Struct: c.Printf("%s.init(%s);", c.objectName(o), c.initArgs(t)) } }
func objectKind(obj types.Object) string { switch obj := obj.(type) { case *types.PkgName: return "imported package name" case *types.TypeName: return "type" case *types.Var: if obj.IsField() { return "field" } case *types.Func: if obj.Type().(*types.Signature).Recv() != nil { return "method" } } // label, func, var, const return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types.")) }
// createLocalVariableMetadata creates and returns a debug descriptor for // a local variable in a function. // // obj is the go/types object for the var. // paramIndex is 0 for "auto" variables (non-parameter stack vars), and a // 1-based index for parameter vars. func (c *compiler) createLocalVariableMetadata(obj types.Object, paramIndex int) llvm.DebugDescriptor { ctx := c.currentDebugContext() if ctx == nil { return nil } tag := llvm.DW_TAG_auto_variable if paramIndex > 0 { tag = llvm.DW_TAG_arg_variable } position := c.fileset.Position(obj.Pos()) ld := llvm.NewLocalVariableDescriptor(tag) ld.Argument = uint32(paramIndex) ld.Line = uint32(position.Line) ld.Name = obj.Name() ld.File = &llvm.ContextDescriptor{llvm.FileDescriptor(position.Filename)} ld.Type = c.tollvmDebugDescriptor(obj.Type()) ld.Context = ctx return ld }
func typeObjectToJson(o *types.Object) interface{} { switch o := (*o).(type) { case *types.Package: return typePackageToJson(o) case *types.Const: return struct { Isa string Pkg interface{} Name string Type interface{} Val interface{} }{ "Const", typePackageToJson(o.Pkg()), o.Name(), typeTypeToJson(o.Type()), o.Val(), } case *types.TypeName: return struct { Isa string Pkg interface{} Name string Type interface{} }{ "TypeName", typePackageToJson(o.Pkg()), o.Name(), typeTypeToJson(o.Type()), } case *types.Var: return struct { Isa string Pkg interface{} Name string Type interface{} }{ "Var", typePackageToJson(o.Pkg()), o.Name(), typeTypeToJson(o.Type()), } case *types.Func: return struct { Isa string Pkg interface{} Name string Type interface{} }{ "Func", typePackageToJson(o.Pkg()), o.Name(), typeTypeToJson(o.Type()), } default: if o != nil { return nil } else { return "UNKNOWN" } } return nil }
// memberFromObject populates package pkg with a member for the // typechecker object obj. // // For objects from Go source code, syntax is the associated syntax // tree (for funcs and vars only); it will be used during the build // phase. // func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { name := obj.Name() switch obj := obj.(type) { case *types.TypeName: pkg.Members[name] = &Type{object: obj} case *types.Const: c := &NamedConst{ object: obj, Value: NewConst(obj.Val(), obj.Type()), } pkg.values[obj] = c.Value pkg.Members[name] = c case *types.Var: spec, _ := syntax.(*ast.ValueSpec) g := &Global{ Pkg: pkg, name: name, object: obj, typ: types.NewPointer(obj.Type()), // address pos: obj.Pos(), spec: spec, } pkg.values[obj] = g pkg.Members[name] = g case *types.Func: var fs *funcSyntax synthetic := "loaded from gc object file" if decl, ok := syntax.(*ast.FuncDecl); ok { synthetic = "" fs = &funcSyntax{ functype: decl.Type, recvField: decl.Recv, body: decl.Body, } } fn := &Function{ name: name, object: obj, Signature: obj.Type().(*types.Signature), Synthetic: synthetic, pos: obj.Pos(), // (iff syntax) Pkg: pkg, Prog: pkg.Prog, syntax: fs, } pkg.values[obj] = fn if fn.Signature.Recv() == nil { pkg.Members[name] = fn // package-level function } default: // (incl. *types.Package) panic("unexpected Object type: " + obj.String()) } }
// memberFromObject populates package pkg with a member for the // typechecker object obj. // // For objects from Go source code, syntax is the associated syntax // tree (for funcs and vars only); it will be used during the build // phase. // func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { name := obj.Name() switch obj := obj.(type) { case *types.TypeName: pkg.values[obj] = nil // for needMethods pkg.Members[name] = &Type{ object: obj, pkg: pkg, } case *types.Const: c := &NamedConst{ object: obj, Value: NewConst(obj.Val(), obj.Type()), pkg: pkg, } pkg.values[obj] = c.Value pkg.Members[name] = c case *types.Var: g := &Global{ Pkg: pkg, name: name, object: obj, typ: types.NewPointer(obj.Type()), // address pos: obj.Pos(), } pkg.values[obj] = g pkg.Members[name] = g case *types.Func: fn := &Function{ name: name, object: obj, Signature: obj.Type().(*types.Signature), syntax: syntax, pos: obj.Pos(), // (iff syntax) Pkg: pkg, Prog: pkg.Prog, } if syntax == nil { fn.Synthetic = "loaded from gc object file" } pkg.values[obj] = fn if fn.Signature.Recv() == nil { pkg.Members[name] = fn // package-level function } default: // (incl. *types.Package) panic("unexpected Object type: " + obj.String()) } }
func (w *PkgWalker) LookupObjects(pkg *types.Package, pkgInfo *types.Info, cursor *FileCursor) { var cursorObj types.Object var cursorSelection *types.Selection var cursorObjIsDef bool //lookup defs _ = cursorObjIsDef if cursorObj == nil { for sel, obj := range pkgInfo.Selections { if cursor.pos >= sel.Sel.Pos() && cursor.pos <= sel.Sel.End() { cursorObj = obj.Obj() cursorSelection = obj break } } } if cursorObj == nil { for id, obj := range pkgInfo.Defs { if cursor.pos >= id.Pos() && cursor.pos <= id.End() { cursorObj = obj cursorObjIsDef = true break } } } _ = cursorSelection if cursorObj == nil { for id, obj := range pkgInfo.Uses { if cursor.pos >= id.Pos() && cursor.pos <= id.End() { cursorObj = obj break } } } if cursorObj == nil { return } kind, err := parserObjKind(cursorObj) if err != nil { log.Fatalln(err) } if kind == ObjField { if cursorObj.(*types.Var).Anonymous() { if named, ok := cursorObj.Type().(*types.Named); ok { cursorObj = named.Obj() } } } cursorPkg := cursorObj.Pkg() cursorPos := cursorObj.Pos() var fieldTypeInfo *types.Info var fieldTypeObj types.Object if cursorPkg == pkg { fieldTypeInfo = pkgInfo } cursorIsInterfaceMethod := false var cursorInterfaceTypeName string if kind == ObjMethod && cursorSelection != nil && cursorSelection.Recv() != nil { sig := cursorObj.(*types.Func).Type().Underlying().(*types.Signature) if _, ok := sig.Recv().Type().Underlying().(*types.Interface); ok { named := cursorSelection.Recv().(*types.Named) obj, typ := w.lookupNamedMethod(named, cursorObj.Name()) if obj != nil { cursorObj = obj } if typ != nil { cursorPkg = typ.Obj().Pkg() cursorInterfaceTypeName = typ.Obj().Name() } cursorIsInterfaceMethod = true } } if cursorPkg != nil && cursorPkg != pkg && kind != ObjPkgName && w.isBinaryPkg(cursorPkg.Path()) { conf := &PkgConfig{ IgnoreFuncBodies: true, AllowBinary: true, Info: &types.Info{ Defs: make(map[*ast.Ident]types.Object), }, } pkg, _ := w.Import("", cursorPkg.Path(), conf) if pkg != nil { if cursorIsInterfaceMethod { for _, obj := range conf.Info.Defs { if obj == nil { continue } if fn, ok := obj.(*types.Func); ok { if fn.Name() == cursorObj.Name() { if sig, ok := fn.Type().Underlying().(*types.Signature); ok { if named, ok := sig.Recv().Type().(*types.Named); ok { if named.Obj() != nil && named.Obj().Name() == cursorInterfaceTypeName { cursorPos = obj.Pos() break } } } } } } } else { for _, obj := range conf.Info.Defs { if obj != nil && obj.String() == cursorObj.String() { cursorPos = obj.Pos() break } } } } if kind == ObjField || cursorIsInterfaceMethod { fieldTypeInfo = conf.Info } } if kind == ObjField { fieldTypeObj = w.LookupStructFromField(fieldTypeInfo, cursorPkg, cursorObj, cursorPos) } if typeFindDef { fmt.Println(w.fset.Position(cursorPos)) } if typeFindInfo { if kind == ObjField && fieldTypeObj != nil { typeName := fieldTypeObj.Name() if fieldTypeObj.Pkg() != nil && fieldTypeObj.Pkg() != pkg { typeName = fieldTypeObj.Pkg().Name() + "." + fieldTypeObj.Name() } fmt.Println(typeName, simpleType(cursorObj.String())) } else if kind == ObjBuiltin { fmt.Println(builtinInfo(cursorObj.Name())) } else if cursorIsInterfaceMethod { fmt.Println(strings.Replace(simpleType(cursorObj.String()), "(interface)", cursorPkg.Name()+"."+cursorInterfaceTypeName, 1)) } else { fmt.Println(simpleType(cursorObj.String())) } } //if f, ok := w.parsedFileCache[w.fset.Position(cursorPos).Filename]; ok { // for _, d := range f.Decls { // if inRange(d, cursorPos) { // if fd, ok := d.(*ast.FuncDecl); ok { // fd.Body = nil // } // commentMap := ast.NewCommentMap(w.fset, f, f.Comments) // commentedNode := printer.CommentedNode{Node: d} // if comments := commentMap.Filter(d).Comments(); comments != nil { // commentedNode.Comments = comments // } // var b bytes.Buffer // printer.Fprint(&b, w.fset, &commentedNode) // b.Write([]byte("\n\n")) // Add a blank line between entries if we print documentation. // log.Println(w.nodeString(d)) // } // } //} if !typeFindUse { return } var usages []int if kind == ObjPkgName { for id, obj := range pkgInfo.Uses { if obj != nil && obj.Id() == cursorObj.Id() { //!= nil && cursorObj.Pos() == obj.Pos() { usages = append(usages, int(id.Pos())) } } } else { for id, obj := range pkgInfo.Defs { if obj == cursorObj { //!= nil && cursorObj.Pos() == obj.Pos() { usages = append(usages, int(id.Pos())) } } for id, obj := range pkgInfo.Uses { if obj == cursorObj { //!= nil && cursorObj.Pos() == obj.Pos() { usages = append(usages, int(id.Pos())) } } } (sort.IntSlice(usages)).Sort() for _, pos := range usages { fmt.Println(w.fset.Position(token.Pos(pos))) } }
// addNamedLocal creates a local variable, adds it to function f and // returns it. Its name and type are taken from obj. Subsequent // calls to f.lookup(obj) will return the same local. // func (f *Function) addNamedLocal(obj types.Object) *Alloc { l := f.addLocal(obj.Type(), obj.Pos()) l.Comment = obj.Name() f.objects[obj] = l return l }
func (x *exporter) exportObject(obj types.Object) { switch t := obj.(type) { case *types.Var: if !obj.IsExported() { return } x.write("\tvar @\"\".%s ", obj.Name()) x.exportName(obj.Type()) x.write("\n") case *types.Func: sig := t.Type().(*types.Signature) recv := sig.Recv() if recv == nil && !t.IsExported() { return // The package "go/ast" has an interface "Decl" (http://golang.org/pkg/go/ast/#Decl) // containing "filtered or unexported methods", specifically a method named "declNode". // // No implementation of that method actually does anything, but it forces type // correctness and also disallows other packages from defining new types that // satisfies that interface. // // As the interface is exported and the types implementing the interface are too, // "declNode" must be exported to be able to properly type check any code that type // casts from and to that interface. // // To be clear; exporting *all* receiver methods is a superset of the methods // that must be exported. } x.write("\tfunc ") if recv != nil { x.write("(") x.exportName(recv) x.write(") ") } x.write(`@"".%s`, t.Name()) x.writeFunc = false x.exportName(sig) x.write("\n") case *types.Const: if !t.IsExported() { return } x.write("\tconst @\"\".%s ", t.Name()) x.exportName(t.Type()) x.write(" = ") x.exportName(t.Val()) x.write("\n") case *types.TypeName: // Some types that are not exported outside of the package actually // need to be exported for the compiler. // // An example would be a package having an exported struct type // that has a member variable of some unexported type: // // As declaring variables of the exported struct type is allowed // outside of the package, the compiler needs to know the size // of the struct. // // To be clear; exporting *all* types is a superset of the types // that must be exported. x.write("\ttype @\"\".%s ", obj.Name()) x.exportName(obj.Type().Underlying()) x.write("\n") if u, ok := obj.Type().(*types.Named); ok { for i := 0; i < u.NumMethods(); i++ { m := u.Method(i) x.exportObject(m) } } default: panic(fmt.Sprintf("UNHANDLED %T", t)) } }