github.com/aykevl/tinygo@v0.5.0/compiler/reflect.go (about)

     1  package compiler
     2  
     3  import (
     4  	"math/big"
     5  	"strings"
     6  )
     7  
     8  var basicTypes = map[string]int64{
     9  	"bool":       1,
    10  	"int":        2,
    11  	"int8":       3,
    12  	"int16":      4,
    13  	"int32":      5,
    14  	"int64":      6,
    15  	"uint":       7,
    16  	"uint8":      8,
    17  	"uint16":     9,
    18  	"uint32":     10,
    19  	"uint64":     11,
    20  	"uintptr":    12,
    21  	"float32":    13,
    22  	"float64":    14,
    23  	"complex64":  15,
    24  	"complex128": 16,
    25  	"string":     17,
    26  	"unsafeptr":  18,
    27  }
    28  
    29  func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
    30  	fn := c.mod.NamedFunction("reflect.ValueOf")
    31  	if fn.IsNil() {
    32  		// reflect.ValueOf is never used, so we can use the most efficient
    33  		// encoding possible.
    34  		for i, t := range typeSlice {
    35  			t.num = uint64(i + 1)
    36  		}
    37  		return
    38  	}
    39  
    40  	// Assign typecodes the way the reflect package expects.
    41  	fallbackIndex := 1
    42  	namedTypes := make(map[string]int)
    43  	for _, t := range typeSlice {
    44  		if t.name[:5] != "type:" {
    45  			panic("expected type name to start with 'type:'")
    46  		}
    47  		num := c.getTypeCodeNum(t.name[5:], &fallbackIndex, namedTypes)
    48  		if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() {
    49  			// TODO: support this in some way, using a side table for example.
    50  			// That's less efficient but better than not working at all.
    51  			// Particularly important on systems with 16-bit pointers (e.g.
    52  			// AVR).
    53  			panic("compiler: could not store type code number inside interface type code")
    54  		}
    55  		t.num = num.Uint64()
    56  	}
    57  }
    58  
    59  // getTypeCodeNum returns the typecode for a given type as expected by the
    60  // reflect package. Also see getTypeCodeName, which serializes types to a string
    61  // based on a types.Type value for this function.
    62  func (c *Compiler) getTypeCodeNum(id string, fallbackIndex *int, namedTypes map[string]int) *big.Int {
    63  	// Note: see src/reflect/type.go for bit allocations.
    64  	// A type can be named or unnamed. Example of both:
    65  	//     basic:~foo:uint64
    66  	//     basic:uint64
    67  	// Extract the class (basic, slice, pointer, etc.), the name, and the
    68  	// contents of this type ID string. Allocate bits based on that, as
    69  	// src/runtime/types.go expects.
    70  	class := id[:strings.IndexByte(id, ':')]
    71  	value := id[len(class)+1:]
    72  	name := ""
    73  	if value[0] == '~' {
    74  		name = value[1:strings.IndexByte(value, ':')]
    75  		value = value[len(name)+2:]
    76  	}
    77  	if class == "basic" {
    78  		// Basic types follow the following bit pattern:
    79  		//    ...xxxxx0
    80  		// where xxxxx is allocated for the 18 possible basic types and all the
    81  		// upper bits are used to indicate the named type.
    82  		num, ok := basicTypes[value]
    83  		if !ok {
    84  			panic("invalid basic type: " + id)
    85  		}
    86  		if name != "" {
    87  			// This type is named, set the upper bits to the name ID.
    88  			num |= int64(getNamedTypeNum(namedTypes, name)) << 5
    89  		}
    90  		return big.NewInt(num << 1)
    91  	} else {
    92  		// Complex types use the following bit pattern:
    93  		//    ...nxxx1
    94  		// where xxx indicates the complex type (any non-basic type). The upper
    95  		// bits contain whatever the type contains. Types that wrap a single
    96  		// other type (channel, interface, pointer, slice) just contain the bits
    97  		// of the wrapped type. Other types (like struct) have a different
    98  		// method of encoding the contents of the type.
    99  		var num *big.Int
   100  		var classNumber int64
   101  		switch class {
   102  		case "chan":
   103  			num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
   104  			classNumber = 0
   105  		case "interface":
   106  			num = big.NewInt(int64(*fallbackIndex))
   107  			*fallbackIndex++
   108  			classNumber = 1
   109  		case "pointer":
   110  			num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
   111  			classNumber = 2
   112  		case "slice":
   113  			num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
   114  			classNumber = 3
   115  		case "array":
   116  			num = big.NewInt(int64(*fallbackIndex))
   117  			*fallbackIndex++
   118  			classNumber = 4
   119  		case "func":
   120  			num = big.NewInt(int64(*fallbackIndex))
   121  			*fallbackIndex++
   122  			classNumber = 5
   123  		case "map":
   124  			num = big.NewInt(int64(*fallbackIndex))
   125  			*fallbackIndex++
   126  			classNumber = 6
   127  		case "struct":
   128  			num = big.NewInt(int64(*fallbackIndex))
   129  			*fallbackIndex++
   130  			classNumber = 7
   131  		default:
   132  			panic("unknown type kind: " + id)
   133  		}
   134  		if name == "" {
   135  			num.Lsh(num, 5).Or(num, big.NewInt((classNumber<<1)+1))
   136  		} else {
   137  			// TODO: store num in a sidetable
   138  			num = big.NewInt(int64(getNamedTypeNum(namedTypes, name))<<1 | 1)
   139  			num.Lsh(num, 4).Or(num, big.NewInt((classNumber<<1)+1))
   140  		}
   141  		return num
   142  	}
   143  }
   144  
   145  // getNamedTypeNum returns an appropriate (unique) number for the given named
   146  // type. If the name already has a number that number is returned, else a new
   147  // number is returned. The number is always non-zero.
   148  func getNamedTypeNum(namedTypes map[string]int, name string) int {
   149  	if num, ok := namedTypes[name]; ok {
   150  		return num
   151  	} else {
   152  		num = len(namedTypes) + 1
   153  		namedTypes[name] = num
   154  		return num
   155  	}
   156  }