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 }
// 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()) } }
// 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}) }
// 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 (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 (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool { // Reject cross-package references if r.to is unexported. // (Such references may be qualified identifiers or field/method // selections.) if !ast.IsExported(r.to) && pkg != from.Pkg() { r.errorf(from.Pos(), "renaming this %s %q to %q would make it unexported", objectKind(from), from.Name(), r.to) r.errorf(id.Pos(), "\tbreaking references from packages such as %q", pkg.Path()) return false } return true }
// 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 (r *renamer) checkInLocalScope(from types.Object) { info := r.packages[from.Pkg()] // Is this object an implicit local var for a type switch? // Each case has its own var, whose position is the decl of y, // but Ident in that decl does not appear in the Uses map. // // switch y := x.(type) { // Defs[Ident(y)] is undefined // case int: print(y) // Implicits[CaseClause(int)] = Var(y_int) // case string: print(y) // Implicits[CaseClause(string)] = Var(y_string) // } // var isCaseVar bool for syntax, obj := range info.Implicits { if _, ok := syntax.(*ast.CaseClause); ok && obj.Pos() == from.Pos() { isCaseVar = true r.check(obj) } } r.checkInLexicalScope(from, info) // Finally, if this was a type switch, change the variable y. if isCaseVar { _, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos()) path[0].(*ast.Ident).Name = r.to // path is [Ident AssignStmt TypeSwitchStmt...] } }
// check performs safety checks of the renaming of the 'from' object to r.to. func (r *renamer) check(from types.Object) { if r.objsToUpdate[from] { return } r.objsToUpdate[from] = true // NB: order of conditions is important. if from_, ok := from.(*types.PkgName); ok { r.checkInFileBlock(from_) } else if from_, ok := from.(*types.Label); ok { r.checkLabel(from_) } else if isPackageLevel(from) { r.checkInPackageBlock(from) } else if v, ok := from.(*types.Var); ok && v.IsField() { r.checkStructField(v) } else if f, ok := from.(*types.Func); ok && f.Type().(*types.Signature).Recv() != nil { r.checkMethod(f) } else if isLocal(from) { r.checkInLocalScope(from) } else { r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n", objectKind(from), from) } }
// checkInPackageBlock performs safety checks for renames of // func/var/const/type objects in the package block. func (r *renamer) checkInPackageBlock(from types.Object) { // Check that there are no references to the name from another // package if the renaming would make it unexported. if ast.IsExported(from.Name()) && !ast.IsExported(r.to) { for pkg, info := range r.packages { if pkg == from.Pkg() { continue } if id := someUse(info, from); id != nil && !r.checkExport(id, pkg, from) { break } } } info := r.packages[from.Pkg()] lexinfo := lexical.Structure(r.iprog.Fset, from.Pkg(), &info.Info, info.Files) // Check that in the package block, "init" is a function, and never referenced. if r.to == "init" { kind := objectKind(from) if kind == "func" { // Reject if intra-package references to it exist. if refs := lexinfo.Refs[from]; len(refs) > 0 { r.errorf(from.Pos(), "renaming this func %q to %q would make it a package initializer", from.Name(), r.to) r.errorf(refs[0].Id.Pos(), "\tbut references to it exist") } } else { r.errorf(from.Pos(), "you cannot have a %s at package level named %q", kind, r.to) } } // Check for conflicts between package block and all file blocks. for _, f := range info.Files { if prev, b := lexinfo.Blocks[f].Lookup(r.to); b == lexinfo.Blocks[f] { r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", objectKind(from), from.Name(), r.to) r.errorf(prev.Pos(), "\twith this %s", objectKind(prev)) return // since checkInPackageBlock would report redundant errors } } // Check for conflicts in lexical scope. if from.Exported() { for _, info := range r.packages { r.checkInLexicalScope(from, info) } } else { r.checkInLexicalScope(from, info) } }
func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) { r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) switch { case delta < 0: // analogous to sub-block conflict r.errorf(syntax.Sel.Pos(), "\twould change the referent of this selection") r.errorf(obj.Pos(), "\tto this %s", objectKind(obj)) case delta == 0: // analogous to same-block conflict r.errorf(syntax.Sel.Pos(), "\twould make this reference ambiguous") r.errorf(obj.Pos(), "\twith this %s", objectKind(obj)) case delta > 0: // analogous to super-block conflict r.errorf(syntax.Sel.Pos(), "\twould shadow this selection") r.errorf(obj.Pos(), "\tto the %s declared here", objectKind(obj)) } }
// checkInLexicalScope performs safety checks that a renaming does not // change the lexical reference structure of the specified package. // // For objects in lexical scope, there are three kinds of conflicts: // same-, sub-, and super-block conflicts. We will illustrate all three // using this example: // // var x int // var z int // // func f(y int) { // print(x) // print(y) // } // // Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object // with the new name already exists, defined in the same lexical block // as the old object. // // Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists // a reference to x from within (what would become) a hole in its scope. // The definition of y in an (inner) sub-block would cast a shadow in // the scope of the renamed variable. // // Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the // converse situation: there is an existing definition of the new name // (x) in an (enclosing) super-block, and the renaming would create a // hole in its scope, within which there exist references to it. The // new name casts a shadow in scope of the existing definition of x in // the super-block. // // Removing the old name (and all references to it) is always safe, and // requires no checks. // func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) { lexinfo := lexical.Structure(r.iprog.Fset, info.Pkg, &info.Info, info.Files) b := lexinfo.Defs[from] // the block defining the 'from' object if b != nil { to, toBlock := b.Lookup(r.to) if toBlock == b { // same-block conflict r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(to.Pos(), "\tconflicts with %s in same block", objectKind(to)) return } else if toBlock != nil { // Check for super-block conflict. // The name r.to is defined in a superblock. // Is that name referenced from within this block? for _, ref := range lexinfo.Refs[to] { if obj, _ := ref.Env.Lookup(from.Name()); obj == from { // super-block conflict r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(ref.Id.Pos(), "\twould shadow this reference") r.errorf(to.Pos(), "\tto the %s declared here", objectKind(to)) return } } } } // Check for sub-block conflict. // Is there an intervening definition of r.to between // the block defining 'from' and some reference to it? for _, ref := range lexinfo.Refs[from] { // TODO(adonovan): think about dot imports. // (Is b == fromBlock an invariant?) _, fromBlock := ref.Env.Lookup(from.Name()) fromDepth := fromBlock.Depth() to, toBlock := ref.Env.Lookup(r.to) if to != nil { // sub-block conflict if toBlock.Depth() > fromDepth { r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(ref.Id.Pos(), "\twould cause this reference to become shadowed") r.errorf(to.Pos(), "\tby this intervening %s definition", objectKind(to)) return } } } // Renaming a type that is used as an embedded field // requires renaming the field too. e.g. // type T int // if we rename this to U.. // var s struct {T} // print(s.T) // ...this must change too if _, ok := from.(*types.TypeName); ok { for id, obj := range info.Uses { if obj == from { if field := info.Defs[id]; field != nil { r.check(field) } } } } }
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 }