// makeFrameType constructs a struct type for the frame of a function. // The offsets in this struct type are such that the struct can be // instantiated at this function's frame pointer. func (p *Process) makeFrameType(s *gosym.Func) (*remoteType, os.Error) { n := len(s.Params) + len(s.Locals) fields := make([]eval.StructField, n) layout := make([]remoteStructField, n) i := 0 // TODO(austin): There can be multiple locals/parameters with // the same name. We probably need liveness information to do // anything about this. Once we have that, perhaps we give // such fields interface{} type? Or perhaps we disambiguate // the names with numbers. Disambiguation is annoying for // things like "i", where there's an obvious right answer. for _, param := range s.Params { rt, err := p.typeOfSym(param) if err != nil { return nil, err } if rt == nil { //fmt.Printf(" (no type)\n"); continue } // TODO(austin): Why do local variables carry their // package name? fields[i].Name = param.BaseName() fields[i].Type = rt.Type // Parameters have positive offsets from FP layout[i].offset = int(param.Value) layout[i].fieldType = rt i++ } for _, local := range s.Locals { rt, err := p.typeOfSym(local) if err != nil { return nil, err } if rt == nil { continue } fields[i].Name = local.BaseName() fields[i].Type = rt.Type // Locals have negative offsets from FP - PtrSize layout[i].offset = -int(local.Value) - p.PtrSize() layout[i].fieldType = rt i++ } fields = fields[0:i] layout = layout[0:i] t := eval.NewStructType(fields) mk := func(r remote) eval.Value { return remoteStruct{r, layout} } return &remoteType{t, 0, 0, mk}, nil }
// parseRemoteType parses a Type structure in a remote process to // construct the corresponding interpreter type and remote type. func parseRemoteType(a aborter, rs remoteStruct) *remoteType { addr := rs.addr().base p := rs.addr().p // We deal with circular types by discovering cycles at // NamedTypes. If a type cycles back to something other than // a named type, we're guaranteed that there will be a named // type somewhere in that cycle. Thus, we continue down, // re-parsing types until we reach the named type in the // cycle. In order to still create one remoteType per remote // type, we insert an empty remoteType in the type map the // first time we encounter the type and re-use that structure // the second time we encounter it. rt, ok := p.types[addr] if ok && rt.Type != nil { return rt } else if !ok { rt = &remoteType{} p.types[addr] = rt } if debugParseRemoteType { sym := p.syms.SymByAddr(uint64(addr)) name := "<unknown>" if sym != nil { name = sym.Name } log.Stderrf("%sParsing type at %#x (%s)", prtIndent, addr, name) prtIndent += " " defer func() { prtIndent = prtIndent[0 : len(prtIndent)-1] }() } // Get Type header itype := proc.Word(rs.field(p.f.Type.Typ).(remoteUint).aGet(a)) typ := rs.field(p.f.Type.Ptr).(remotePtr).aGet(a).(remoteStruct) // Is this a named type? var nt *eval.NamedType uncommon := typ.field(p.f.CommonType.UncommonType).(remotePtr).aGet(a) if uncommon != nil { name := uncommon.(remoteStruct).field(p.f.UncommonType.Name).(remotePtr).aGet(a) if name != nil { // TODO(austin) Declare type in appropriate remote package nt = eval.NewNamedType(name.(remoteString).aGet(a)) rt.Type = nt } } // Create type var t eval.Type var mk maker switch itype { case p.runtime.PBoolType: t = eval.BoolType mk = mkBool case p.runtime.PUint8Type: t = eval.Uint8Type mk = mkUint8 case p.runtime.PUint16Type: t = eval.Uint16Type mk = mkUint16 case p.runtime.PUint32Type: t = eval.Uint32Type mk = mkUint32 case p.runtime.PUint64Type: t = eval.Uint64Type mk = mkUint64 case p.runtime.PUintType: t = eval.UintType mk = mkUint case p.runtime.PUintptrType: t = eval.UintptrType mk = mkUintptr case p.runtime.PInt8Type: t = eval.Int8Type mk = mkInt8 case p.runtime.PInt16Type: t = eval.Int16Type mk = mkInt16 case p.runtime.PInt32Type: t = eval.Int32Type mk = mkInt32 case p.runtime.PInt64Type: t = eval.Int64Type mk = mkInt64 case p.runtime.PIntType: t = eval.IntType mk = mkInt case p.runtime.PFloat32Type: t = eval.Float32Type mk = mkFloat32 case p.runtime.PFloat64Type: t = eval.Float64Type mk = mkFloat64 case p.runtime.PFloatType: t = eval.FloatType mk = mkFloat case p.runtime.PStringType: t = eval.StringType mk = mkString case p.runtime.PArrayType: // Cast to an ArrayType typ := p.runtime.ArrayType.mk(typ.addr()).(remoteStruct) len := int64(typ.field(p.f.ArrayType.Len).(remoteUint).aGet(a)) elem := parseRemoteType(a, typ.field(p.f.ArrayType.Elem).(remotePtr).aGet(a).(remoteStruct)) t = eval.NewArrayType(len, elem.Type) mk = func(r remote) eval.Value { return remoteArray{r, len, elem} } case p.runtime.PStructType: // Cast to a StructType typ := p.runtime.StructType.mk(typ.addr()).(remoteStruct) fs := typ.field(p.f.StructType.Fields).(remoteSlice).aGet(a) fields := make([]eval.StructField, fs.Len) layout := make([]remoteStructField, fs.Len) for i := range fields { f := fs.Base.(remoteArray).elem(int64(i)).(remoteStruct) elemrs := f.field(p.f.StructField.Typ).(remotePtr).aGet(a).(remoteStruct) elem := parseRemoteType(a, elemrs) fields[i].Type = elem.Type name := f.field(p.f.StructField.Name).(remotePtr).aGet(a) if name == nil { fields[i].Anonymous = true } else { fields[i].Name = name.(remoteString).aGet(a) } layout[i].offset = int(f.field(p.f.StructField.Offset).(remoteUint).aGet(a)) layout[i].fieldType = elem } t = eval.NewStructType(fields) mk = func(r remote) eval.Value { return remoteStruct{r, layout} } case p.runtime.PPtrType: // Cast to a PtrType typ := p.runtime.PtrType.mk(typ.addr()).(remoteStruct) elem := parseRemoteType(a, typ.field(p.f.PtrType.Elem).(remotePtr).aGet(a).(remoteStruct)) t = eval.NewPtrType(elem.Type) mk = func(r remote) eval.Value { return remotePtr{r, elem} } case p.runtime.PSliceType: // Cast to a SliceType typ := p.runtime.SliceType.mk(typ.addr()).(remoteStruct) elem := parseRemoteType(a, typ.field(p.f.SliceType.Elem).(remotePtr).aGet(a).(remoteStruct)) t = eval.NewSliceType(elem.Type) mk = func(r remote) eval.Value { return remoteSlice{r, elem} } case p.runtime.PMapType, p.runtime.PChanType, p.runtime.PFuncType, p.runtime.PInterfaceType, p.runtime.PUnsafePointerType, p.runtime.PDotDotDotType: // TODO(austin) t = eval.UintptrType mk = mkUintptr default: sym := p.syms.SymByAddr(uint64(itype)) name := "<unknown symbol>" if sym != nil { name = sym.Name } err := fmt.Sprintf("runtime type at %#x has unexpected type %#x (%s)", addr, itype, name) a.Abort(FormatError(err)) } // Fill in the remote type if nt != nil { nt.Complete(t) } else { rt.Type = t } rt.size = int(typ.field(p.f.CommonType.Size).(remoteUint).aGet(a)) rt.mk = mk return rt }
// populateWorld defines constants in the given world for each package // in this process. These packages are structs that, in turn, contain // fields for each global and function in that package. func (p *Process) populateWorld(w *eval.World) os.Error { type def struct { t eval.Type v eval.Value } packages := make(map[string]map[string]def) for _, s := range p.syms.Syms { if s.ReceiverName() != "" { // TODO(austin) continue } // Package pkgName := s.PackageName() switch pkgName { case "", "type", "extratype", "string", "go": // "go" is really "go.string" continue } pkg, ok := packages[pkgName] if !ok { pkg = make(map[string]def) packages[pkgName] = pkg } // Symbol name name := s.BaseName() if _, ok := pkg[name]; ok { log.Stderrf("Multiple definitions of symbol %s", s.Name) continue } // Symbol type rt, err := p.typeOfSym(&s) if err != nil { return err } // Definition switch s.Type { case 'D', 'd', 'B', 'b': // Global variable if rt == nil { continue } pkg[name] = def{rt.Type, rt.mk(remote{proc.Word(s.Value), p})} case 'T', 't', 'L', 'l': // Function s := s.Func // TODO(austin): Ideally, this would *also* be // callable. How does that interact with type // conversion syntax? rt, err := p.makeFrameType(s) if err != nil { return err } pkg[name] = def{eval.NewPtrType(rt.Type), remoteFramePtr{p, s, rt}} } } // TODO(austin): Define remote types // Define packages for pkgName, defs := range packages { fields := make([]eval.StructField, len(defs)) vals := make([]eval.Value, len(defs)) i := 0 for name, def := range defs { fields[i].Name = name fields[i].Type = def.t vals[i] = def.v i++ } pkgType := eval.NewStructType(fields) pkgVal := remotePackage{vals} err := w.DefineConst(pkgName, pkgType, pkgVal) if err != nil { log.Stderrf("while defining package %s: %v", pkgName, err) } } return nil }