mirror of
https://github.com/autc04/Retro68.git
synced 2024-09-08 20:55:01 +00:00
728 lines
17 KiB
Go
728 lines
17 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.
|
|
|
|
// DWARF type information structures.
|
|
// The format is heavily biased toward C, but for simplicity
|
|
// the String methods use a pseudo-Go syntax.
|
|
|
|
package dwarf
|
|
|
|
import "strconv"
|
|
|
|
// A Type conventionally represents a pointer to any of the
|
|
// specific Type structures (CharType, StructType, etc.).
|
|
type Type interface {
|
|
Common() *CommonType
|
|
String() string
|
|
Size() int64
|
|
}
|
|
|
|
// A CommonType holds fields common to multiple types.
|
|
// If a field is not known or not applicable for a given type,
|
|
// the zero value is used.
|
|
type CommonType struct {
|
|
ByteSize int64 // size of value of this type, in bytes
|
|
Name string // name that can be used to refer to type
|
|
}
|
|
|
|
func (c *CommonType) Common() *CommonType { return c }
|
|
|
|
func (c *CommonType) Size() int64 { return c.ByteSize }
|
|
|
|
// Basic types
|
|
|
|
// A BasicType holds fields common to all basic types.
|
|
type BasicType struct {
|
|
CommonType
|
|
BitSize int64
|
|
BitOffset int64
|
|
}
|
|
|
|
func (b *BasicType) Basic() *BasicType { return b }
|
|
|
|
func (t *BasicType) String() string {
|
|
if t.Name != "" {
|
|
return t.Name
|
|
}
|
|
return "?"
|
|
}
|
|
|
|
// A CharType represents a signed character type.
|
|
type CharType struct {
|
|
BasicType
|
|
}
|
|
|
|
// A UcharType represents an unsigned character type.
|
|
type UcharType struct {
|
|
BasicType
|
|
}
|
|
|
|
// An IntType represents a signed integer type.
|
|
type IntType struct {
|
|
BasicType
|
|
}
|
|
|
|
// A UintType represents an unsigned integer type.
|
|
type UintType struct {
|
|
BasicType
|
|
}
|
|
|
|
// A FloatType represents a floating point type.
|
|
type FloatType struct {
|
|
BasicType
|
|
}
|
|
|
|
// A ComplexType represents a complex floating point type.
|
|
type ComplexType struct {
|
|
BasicType
|
|
}
|
|
|
|
// A BoolType represents a boolean type.
|
|
type BoolType struct {
|
|
BasicType
|
|
}
|
|
|
|
// An AddrType represents a machine address type.
|
|
type AddrType struct {
|
|
BasicType
|
|
}
|
|
|
|
// An UnspecifiedType represents an implicit, unknown, ambiguous or nonexistent type.
|
|
type UnspecifiedType struct {
|
|
BasicType
|
|
}
|
|
|
|
// qualifiers
|
|
|
|
// A QualType represents a type that has the C/C++ "const", "restrict", or "volatile" qualifier.
|
|
type QualType struct {
|
|
CommonType
|
|
Qual string
|
|
Type Type
|
|
}
|
|
|
|
func (t *QualType) String() string { return t.Qual + " " + t.Type.String() }
|
|
|
|
func (t *QualType) Size() int64 { return t.Type.Size() }
|
|
|
|
// An ArrayType represents a fixed size array type.
|
|
type ArrayType struct {
|
|
CommonType
|
|
Type Type
|
|
StrideBitSize int64 // if > 0, number of bits to hold each element
|
|
Count int64 // if == -1, an incomplete array, like char x[].
|
|
}
|
|
|
|
func (t *ArrayType) String() string {
|
|
return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String()
|
|
}
|
|
|
|
func (t *ArrayType) Size() int64 {
|
|
if t.Count == -1 {
|
|
return 0
|
|
}
|
|
return t.Count * t.Type.Size()
|
|
}
|
|
|
|
// A VoidType represents the C void type.
|
|
type VoidType struct {
|
|
CommonType
|
|
}
|
|
|
|
func (t *VoidType) String() string { return "void" }
|
|
|
|
// A PtrType represents a pointer type.
|
|
type PtrType struct {
|
|
CommonType
|
|
Type Type
|
|
}
|
|
|
|
func (t *PtrType) String() string { return "*" + t.Type.String() }
|
|
|
|
// A StructType represents a struct, union, or C++ class type.
|
|
type StructType struct {
|
|
CommonType
|
|
StructName string
|
|
Kind string // "struct", "union", or "class".
|
|
Field []*StructField
|
|
Incomplete bool // if true, struct, union, class is declared but not defined
|
|
}
|
|
|
|
// A StructField represents a field in a struct, union, or C++ class type.
|
|
type StructField struct {
|
|
Name string
|
|
Type Type
|
|
ByteOffset int64
|
|
ByteSize int64 // usually zero; use Type.Size() for normal fields
|
|
BitOffset int64 // within the ByteSize bytes at ByteOffset
|
|
BitSize int64 // zero if not a bit field
|
|
}
|
|
|
|
func (t *StructType) String() string {
|
|
if t.StructName != "" {
|
|
return t.Kind + " " + t.StructName
|
|
}
|
|
return t.Defn()
|
|
}
|
|
|
|
func (t *StructType) Defn() string {
|
|
s := t.Kind
|
|
if t.StructName != "" {
|
|
s += " " + t.StructName
|
|
}
|
|
if t.Incomplete {
|
|
s += " /*incomplete*/"
|
|
return s
|
|
}
|
|
s += " {"
|
|
for i, f := range t.Field {
|
|
if i > 0 {
|
|
s += "; "
|
|
}
|
|
s += f.Name + " " + f.Type.String()
|
|
s += "@" + strconv.FormatInt(f.ByteOffset, 10)
|
|
if f.BitSize > 0 {
|
|
s += " : " + strconv.FormatInt(f.BitSize, 10)
|
|
s += "@" + strconv.FormatInt(f.BitOffset, 10)
|
|
}
|
|
}
|
|
s += "}"
|
|
return s
|
|
}
|
|
|
|
// An EnumType represents an enumerated type.
|
|
// The only indication of its native integer type is its ByteSize
|
|
// (inside CommonType).
|
|
type EnumType struct {
|
|
CommonType
|
|
EnumName string
|
|
Val []*EnumValue
|
|
}
|
|
|
|
// An EnumValue represents a single enumeration value.
|
|
type EnumValue struct {
|
|
Name string
|
|
Val int64
|
|
}
|
|
|
|
func (t *EnumType) String() string {
|
|
s := "enum"
|
|
if t.EnumName != "" {
|
|
s += " " + t.EnumName
|
|
}
|
|
s += " {"
|
|
for i, v := range t.Val {
|
|
if i > 0 {
|
|
s += "; "
|
|
}
|
|
s += v.Name + "=" + strconv.FormatInt(v.Val, 10)
|
|
}
|
|
s += "}"
|
|
return s
|
|
}
|
|
|
|
// A FuncType represents a function type.
|
|
type FuncType struct {
|
|
CommonType
|
|
ReturnType Type
|
|
ParamType []Type
|
|
}
|
|
|
|
func (t *FuncType) String() string {
|
|
s := "func("
|
|
for i, t := range t.ParamType {
|
|
if i > 0 {
|
|
s += ", "
|
|
}
|
|
s += t.String()
|
|
}
|
|
s += ")"
|
|
if t.ReturnType != nil {
|
|
s += " " + t.ReturnType.String()
|
|
}
|
|
return s
|
|
}
|
|
|
|
// A DotDotDotType represents the variadic ... function parameter.
|
|
type DotDotDotType struct {
|
|
CommonType
|
|
}
|
|
|
|
func (t *DotDotDotType) String() string { return "..." }
|
|
|
|
// A TypedefType represents a named type.
|
|
type TypedefType struct {
|
|
CommonType
|
|
Type Type
|
|
}
|
|
|
|
func (t *TypedefType) String() string { return t.Name }
|
|
|
|
func (t *TypedefType) Size() int64 { return t.Type.Size() }
|
|
|
|
// typeReader is used to read from either the info section or the
|
|
// types section.
|
|
type typeReader interface {
|
|
Seek(Offset)
|
|
Next() (*Entry, error)
|
|
clone() typeReader
|
|
offset() Offset
|
|
// AddressSize returns the size in bytes of addresses in the current
|
|
// compilation unit.
|
|
AddressSize() int
|
|
}
|
|
|
|
// Type reads the type at off in the DWARF ``info'' section.
|
|
func (d *Data) Type(off Offset) (Type, error) {
|
|
return d.readType("info", d.Reader(), off, d.typeCache, nil)
|
|
}
|
|
|
|
// readType reads a type from r at off of name. It adds types to the
|
|
// type cache, appends new typedef types to typedefs, and computes the
|
|
// sizes of types. Callers should pass nil for typedefs; this is used
|
|
// for internal recursion.
|
|
func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Offset]Type, typedefs *[]*TypedefType) (Type, error) {
|
|
if t, ok := typeCache[off]; ok {
|
|
return t, nil
|
|
}
|
|
r.Seek(off)
|
|
e, err := r.Next()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
addressSize := r.AddressSize()
|
|
if e == nil || e.Offset != off {
|
|
return nil, DecodeError{name, off, "no type at offset"}
|
|
}
|
|
|
|
// If this is the root of the recursion, prepare to resolve
|
|
// typedef sizes once the recursion is done. This must be done
|
|
// after the type graph is constructed because it may need to
|
|
// resolve cycles in a different order than readType
|
|
// encounters them.
|
|
if typedefs == nil {
|
|
var typedefList []*TypedefType
|
|
defer func() {
|
|
for _, t := range typedefList {
|
|
t.Common().ByteSize = t.Type.Size()
|
|
}
|
|
}()
|
|
typedefs = &typedefList
|
|
}
|
|
|
|
// Parse type from Entry.
|
|
// Must always set typeCache[off] before calling
|
|
// d.readType recursively, to handle circular types correctly.
|
|
var typ Type
|
|
|
|
nextDepth := 0
|
|
|
|
// Get next child; set err if error happens.
|
|
next := func() *Entry {
|
|
if !e.Children {
|
|
return nil
|
|
}
|
|
// Only return direct children.
|
|
// Skip over composite entries that happen to be nested
|
|
// inside this one. Most DWARF generators wouldn't generate
|
|
// such a thing, but clang does.
|
|
// See golang.org/issue/6472.
|
|
for {
|
|
kid, err1 := r.Next()
|
|
if err1 != nil {
|
|
err = err1
|
|
return nil
|
|
}
|
|
if kid == nil {
|
|
err = DecodeError{name, r.offset(), "unexpected end of DWARF entries"}
|
|
return nil
|
|
}
|
|
if kid.Tag == 0 {
|
|
if nextDepth > 0 {
|
|
nextDepth--
|
|
continue
|
|
}
|
|
return nil
|
|
}
|
|
if kid.Children {
|
|
nextDepth++
|
|
}
|
|
if nextDepth > 0 {
|
|
continue
|
|
}
|
|
return kid
|
|
}
|
|
}
|
|
|
|
// Get Type referred to by Entry's AttrType field.
|
|
// Set err if error happens. Not having a type is an error.
|
|
typeOf := func(e *Entry) Type {
|
|
tval := e.Val(AttrType)
|
|
var t Type
|
|
switch toff := tval.(type) {
|
|
case Offset:
|
|
if t, err = d.readType(name, r.clone(), toff, typeCache, typedefs); err != nil {
|
|
return nil
|
|
}
|
|
case uint64:
|
|
if t, err = d.sigToType(toff); err != nil {
|
|
return nil
|
|
}
|
|
default:
|
|
// It appears that no Type means "void".
|
|
return new(VoidType)
|
|
}
|
|
return t
|
|
}
|
|
|
|
switch e.Tag {
|
|
case TagArrayType:
|
|
// Multi-dimensional array. (DWARF v2 §5.4)
|
|
// Attributes:
|
|
// AttrType:subtype [required]
|
|
// AttrStrideSize: size in bits of each element of the array
|
|
// AttrByteSize: size of entire array
|
|
// Children:
|
|
// TagSubrangeType or TagEnumerationType giving one dimension.
|
|
// dimensions are in left to right order.
|
|
t := new(ArrayType)
|
|
typ = t
|
|
typeCache[off] = t
|
|
if t.Type = typeOf(e); err != nil {
|
|
goto Error
|
|
}
|
|
t.StrideBitSize, _ = e.Val(AttrStrideSize).(int64)
|
|
|
|
// Accumulate dimensions,
|
|
var dims []int64
|
|
for kid := next(); kid != nil; kid = next() {
|
|
// TODO(rsc): Can also be TagEnumerationType
|
|
// but haven't seen that in the wild yet.
|
|
switch kid.Tag {
|
|
case TagSubrangeType:
|
|
count, ok := kid.Val(AttrCount).(int64)
|
|
if !ok {
|
|
// Old binaries may have an upper bound instead.
|
|
count, ok = kid.Val(AttrUpperBound).(int64)
|
|
if ok {
|
|
count++ // Length is one more than upper bound.
|
|
} else if len(dims) == 0 {
|
|
count = -1 // As in x[].
|
|
}
|
|
}
|
|
dims = append(dims, count)
|
|
case TagEnumerationType:
|
|
err = DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"}
|
|
goto Error
|
|
}
|
|
}
|
|
if len(dims) == 0 {
|
|
// LLVM generates this for x[].
|
|
dims = []int64{-1}
|
|
}
|
|
|
|
t.Count = dims[0]
|
|
for i := len(dims) - 1; i >= 1; i-- {
|
|
t.Type = &ArrayType{Type: t.Type, Count: dims[i]}
|
|
}
|
|
|
|
case TagBaseType:
|
|
// Basic type. (DWARF v2 §5.1)
|
|
// Attributes:
|
|
// AttrName: name of base type in programming language of the compilation unit [required]
|
|
// AttrEncoding: encoding value for type (encFloat etc) [required]
|
|
// AttrByteSize: size of type in bytes [required]
|
|
// AttrBitOffset: for sub-byte types, size in bits
|
|
// AttrBitSize: for sub-byte types, bit offset of high order bit in the AttrByteSize bytes
|
|
name, _ := e.Val(AttrName).(string)
|
|
enc, ok := e.Val(AttrEncoding).(int64)
|
|
if !ok {
|
|
err = DecodeError{name, e.Offset, "missing encoding attribute for " + name}
|
|
goto Error
|
|
}
|
|
switch enc {
|
|
default:
|
|
err = DecodeError{name, e.Offset, "unrecognized encoding attribute value"}
|
|
goto Error
|
|
|
|
case encAddress:
|
|
typ = new(AddrType)
|
|
case encBoolean:
|
|
typ = new(BoolType)
|
|
case encComplexFloat:
|
|
typ = new(ComplexType)
|
|
if name == "complex" {
|
|
// clang writes out 'complex' instead of 'complex float' or 'complex double'.
|
|
// clang also writes out a byte size that we can use to distinguish.
|
|
// See issue 8694.
|
|
switch byteSize, _ := e.Val(AttrByteSize).(int64); byteSize {
|
|
case 8:
|
|
name = "complex float"
|
|
case 16:
|
|
name = "complex double"
|
|
}
|
|
}
|
|
case encFloat:
|
|
typ = new(FloatType)
|
|
case encSigned:
|
|
typ = new(IntType)
|
|
case encUnsigned:
|
|
typ = new(UintType)
|
|
case encSignedChar:
|
|
typ = new(CharType)
|
|
case encUnsignedChar:
|
|
typ = new(UcharType)
|
|
}
|
|
typeCache[off] = typ
|
|
t := typ.(interface {
|
|
Basic() *BasicType
|
|
}).Basic()
|
|
t.Name = name
|
|
t.BitSize, _ = e.Val(AttrBitSize).(int64)
|
|
t.BitOffset, _ = e.Val(AttrBitOffset).(int64)
|
|
|
|
case TagClassType, TagStructType, TagUnionType:
|
|
// Structure, union, or class type. (DWARF v2 §5.5)
|
|
// Attributes:
|
|
// AttrName: name of struct, union, or class
|
|
// AttrByteSize: byte size [required]
|
|
// AttrDeclaration: if true, struct/union/class is incomplete
|
|
// Children:
|
|
// TagMember to describe one member.
|
|
// AttrName: name of member [required]
|
|
// AttrType: type of member [required]
|
|
// AttrByteSize: size in bytes
|
|
// AttrBitOffset: bit offset within bytes for bit fields
|
|
// AttrBitSize: bit size for bit fields
|
|
// AttrDataMemberLoc: location within struct [required for struct, class]
|
|
// There is much more to handle C++, all ignored for now.
|
|
t := new(StructType)
|
|
typ = t
|
|
typeCache[off] = t
|
|
switch e.Tag {
|
|
case TagClassType:
|
|
t.Kind = "class"
|
|
case TagStructType:
|
|
t.Kind = "struct"
|
|
case TagUnionType:
|
|
t.Kind = "union"
|
|
}
|
|
t.StructName, _ = e.Val(AttrName).(string)
|
|
t.Incomplete = e.Val(AttrDeclaration) != nil
|
|
t.Field = make([]*StructField, 0, 8)
|
|
var lastFieldType *Type
|
|
var lastFieldBitOffset int64
|
|
for kid := next(); kid != nil; kid = next() {
|
|
if kid.Tag != TagMember {
|
|
continue
|
|
}
|
|
f := new(StructField)
|
|
if f.Type = typeOf(kid); err != nil {
|
|
goto Error
|
|
}
|
|
switch loc := kid.Val(AttrDataMemberLoc).(type) {
|
|
case []byte:
|
|
// TODO: Should have original compilation
|
|
// unit here, not unknownFormat.
|
|
b := makeBuf(d, unknownFormat{}, "location", 0, loc)
|
|
if b.uint8() != opPlusUconst {
|
|
err = DecodeError{name, kid.Offset, "unexpected opcode"}
|
|
goto Error
|
|
}
|
|
f.ByteOffset = int64(b.uint())
|
|
if b.err != nil {
|
|
err = b.err
|
|
goto Error
|
|
}
|
|
case int64:
|
|
f.ByteOffset = loc
|
|
}
|
|
|
|
haveBitOffset := false
|
|
f.Name, _ = kid.Val(AttrName).(string)
|
|
f.ByteSize, _ = kid.Val(AttrByteSize).(int64)
|
|
f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64)
|
|
f.BitSize, _ = kid.Val(AttrBitSize).(int64)
|
|
t.Field = append(t.Field, f)
|
|
|
|
bito := f.BitOffset
|
|
if !haveBitOffset {
|
|
bito = f.ByteOffset * 8
|
|
}
|
|
if bito == lastFieldBitOffset && t.Kind != "union" {
|
|
// Last field was zero width. Fix array length.
|
|
// (DWARF writes out 0-length arrays as if they were 1-length arrays.)
|
|
zeroArray(lastFieldType)
|
|
}
|
|
lastFieldType = &f.Type
|
|
lastFieldBitOffset = bito
|
|
}
|
|
if t.Kind != "union" {
|
|
b, ok := e.Val(AttrByteSize).(int64)
|
|
if ok && b*8 == lastFieldBitOffset {
|
|
// Final field must be zero width. Fix array length.
|
|
zeroArray(lastFieldType)
|
|
}
|
|
}
|
|
|
|
case TagConstType, TagVolatileType, TagRestrictType:
|
|
// Type modifier (DWARF v2 §5.2)
|
|
// Attributes:
|
|
// AttrType: subtype
|
|
t := new(QualType)
|
|
typ = t
|
|
typeCache[off] = t
|
|
if t.Type = typeOf(e); err != nil {
|
|
goto Error
|
|
}
|
|
switch e.Tag {
|
|
case TagConstType:
|
|
t.Qual = "const"
|
|
case TagRestrictType:
|
|
t.Qual = "restrict"
|
|
case TagVolatileType:
|
|
t.Qual = "volatile"
|
|
}
|
|
|
|
case TagEnumerationType:
|
|
// Enumeration type (DWARF v2 §5.6)
|
|
// Attributes:
|
|
// AttrName: enum name if any
|
|
// AttrByteSize: bytes required to represent largest value
|
|
// Children:
|
|
// TagEnumerator:
|
|
// AttrName: name of constant
|
|
// AttrConstValue: value of constant
|
|
t := new(EnumType)
|
|
typ = t
|
|
typeCache[off] = t
|
|
t.EnumName, _ = e.Val(AttrName).(string)
|
|
t.Val = make([]*EnumValue, 0, 8)
|
|
for kid := next(); kid != nil; kid = next() {
|
|
if kid.Tag == TagEnumerator {
|
|
f := new(EnumValue)
|
|
f.Name, _ = kid.Val(AttrName).(string)
|
|
f.Val, _ = kid.Val(AttrConstValue).(int64)
|
|
n := len(t.Val)
|
|
if n >= cap(t.Val) {
|
|
val := make([]*EnumValue, n, n*2)
|
|
copy(val, t.Val)
|
|
t.Val = val
|
|
}
|
|
t.Val = t.Val[0 : n+1]
|
|
t.Val[n] = f
|
|
}
|
|
}
|
|
|
|
case TagPointerType:
|
|
// Type modifier (DWARF v2 §5.2)
|
|
// Attributes:
|
|
// AttrType: subtype [not required! void* has no AttrType]
|
|
// AttrAddrClass: address class [ignored]
|
|
t := new(PtrType)
|
|
typ = t
|
|
typeCache[off] = t
|
|
if e.Val(AttrType) == nil {
|
|
t.Type = &VoidType{}
|
|
break
|
|
}
|
|
t.Type = typeOf(e)
|
|
|
|
case TagSubroutineType:
|
|
// Subroutine type. (DWARF v2 §5.7)
|
|
// Attributes:
|
|
// AttrType: type of return value if any
|
|
// AttrName: possible name of type [ignored]
|
|
// AttrPrototyped: whether used ANSI C prototype [ignored]
|
|
// Children:
|
|
// TagFormalParameter: typed parameter
|
|
// AttrType: type of parameter
|
|
// TagUnspecifiedParameter: final ...
|
|
t := new(FuncType)
|
|
typ = t
|
|
typeCache[off] = t
|
|
if t.ReturnType = typeOf(e); err != nil {
|
|
goto Error
|
|
}
|
|
t.ParamType = make([]Type, 0, 8)
|
|
for kid := next(); kid != nil; kid = next() {
|
|
var tkid Type
|
|
switch kid.Tag {
|
|
default:
|
|
continue
|
|
case TagFormalParameter:
|
|
if tkid = typeOf(kid); err != nil {
|
|
goto Error
|
|
}
|
|
case TagUnspecifiedParameters:
|
|
tkid = &DotDotDotType{}
|
|
}
|
|
t.ParamType = append(t.ParamType, tkid)
|
|
}
|
|
|
|
case TagTypedef:
|
|
// Typedef (DWARF v2 §5.3)
|
|
// Attributes:
|
|
// AttrName: name [required]
|
|
// AttrType: type definition [required]
|
|
t := new(TypedefType)
|
|
typ = t
|
|
typeCache[off] = t
|
|
t.Name, _ = e.Val(AttrName).(string)
|
|
t.Type = typeOf(e)
|
|
|
|
case TagUnspecifiedType:
|
|
// Unspecified type (DWARF v3 §5.2)
|
|
// Attributes:
|
|
// AttrName: name
|
|
t := new(UnspecifiedType)
|
|
typ = t
|
|
typeCache[off] = t
|
|
t.Name, _ = e.Val(AttrName).(string)
|
|
}
|
|
|
|
if err != nil {
|
|
goto Error
|
|
}
|
|
|
|
{
|
|
b, ok := e.Val(AttrByteSize).(int64)
|
|
if !ok {
|
|
b = -1
|
|
switch t := typ.(type) {
|
|
case *TypedefType:
|
|
// Record that we need to resolve this
|
|
// type's size once the type graph is
|
|
// constructed.
|
|
*typedefs = append(*typedefs, t)
|
|
case *PtrType:
|
|
b = int64(addressSize)
|
|
}
|
|
}
|
|
typ.Common().ByteSize = b
|
|
}
|
|
return typ, nil
|
|
|
|
Error:
|
|
// If the parse fails, take the type out of the cache
|
|
// so that the next call with this offset doesn't hit
|
|
// the cache and return success.
|
|
delete(typeCache, off)
|
|
return nil, err
|
|
}
|
|
|
|
func zeroArray(t *Type) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
at, ok := (*t).(*ArrayType)
|
|
if !ok || at.Type.Size() == 0 {
|
|
return
|
|
}
|
|
// Make a copy to avoid invalidating typeCache.
|
|
tt := *at
|
|
tt.Count = 0
|
|
*t = &tt
|
|
}
|