313 строки
		
	
	
	
		
			8,1 КиБ
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			313 строки
		
	
	
	
		
			8,1 КиБ
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
// This file provides functionality to interpret very basic Go SSA, for
 | 
						|
// compile-time initialization of globals.
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"go/constant"
 | 
						|
	"go/token"
 | 
						|
	"go/types"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"golang.org/x/tools/go/ssa"
 | 
						|
)
 | 
						|
 | 
						|
// Interpret instructions as far as possible, and drop those instructions from
 | 
						|
// the basic block.
 | 
						|
func (p *Program) Interpret(block *ssa.BasicBlock) error {
 | 
						|
	for {
 | 
						|
		i, err := p.interpret(block.Instrs)
 | 
						|
		if err == cgoWrapperError {
 | 
						|
			// skip this instruction
 | 
						|
			block.Instrs = block.Instrs[i+1:]
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		block.Instrs = block.Instrs[i:]
 | 
						|
		return err
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Interpret instructions as far as possible, and return the index of the first
 | 
						|
// unknown instruction.
 | 
						|
func (p *Program) interpret(instrs []ssa.Instruction) (int, error) {
 | 
						|
	locals := map[ssa.Value]Value{}
 | 
						|
	for i, instr := range instrs {
 | 
						|
		switch instr := instr.(type) {
 | 
						|
		case *ssa.Alloc:
 | 
						|
			alloc, err := p.getZeroValue(instr.Type().Underlying().(*types.Pointer).Elem())
 | 
						|
			if err != nil {
 | 
						|
				return i, err
 | 
						|
			}
 | 
						|
			locals[instr] = &PointerValue{&alloc}
 | 
						|
		case *ssa.Convert:
 | 
						|
			x, err := p.getValue(instr.X, locals)
 | 
						|
			if err != nil {
 | 
						|
				return i, err
 | 
						|
			}
 | 
						|
			switch typ := instr.Type().Underlying().(type) {
 | 
						|
			case *types.Basic:
 | 
						|
				if _, ok := instr.X.Type().Underlying().(*types.Pointer); ok && typ.Kind() == types.UnsafePointer {
 | 
						|
					locals[instr] = &PointerBitCastValue{typ, x}
 | 
						|
				} else if xtyp, ok := instr.X.Type().Underlying().(*types.Basic); ok && xtyp.Kind() == types.UnsafePointer && typ.Kind() == types.Uintptr {
 | 
						|
					locals[instr] = &PointerToUintptrValue{x}
 | 
						|
				} else {
 | 
						|
					return i, errors.New("todo: init: unknown basic convert: " + instr.String())
 | 
						|
				}
 | 
						|
			case *types.Pointer:
 | 
						|
				if xtyp, ok := instr.X.Type().Underlying().(*types.Basic); ok && xtyp.Kind() == types.UnsafePointer {
 | 
						|
					locals[instr] = &PointerBitCastValue{typ, x}
 | 
						|
				} else {
 | 
						|
					panic("expected unsafe pointer conversion")
 | 
						|
				}
 | 
						|
			default:
 | 
						|
				return i, errors.New("todo: init: unknown convert: " + instr.String())
 | 
						|
			}
 | 
						|
		case *ssa.FieldAddr:
 | 
						|
			x, err := p.getValue(instr.X, locals)
 | 
						|
			if err != nil {
 | 
						|
				return i, err
 | 
						|
			}
 | 
						|
			var structVal *StructValue
 | 
						|
			switch x := x.(type) {
 | 
						|
			case *GlobalValue:
 | 
						|
				structVal = x.Global.initializer.(*StructValue)
 | 
						|
			case *PointerValue:
 | 
						|
				structVal = (*x.Elem).(*StructValue)
 | 
						|
			default:
 | 
						|
				panic("expected a pointer")
 | 
						|
			}
 | 
						|
			locals[instr] = &PointerValue{&structVal.Fields[instr.Field]}
 | 
						|
		case *ssa.IndexAddr:
 | 
						|
			x, err := p.getValue(instr.X, locals)
 | 
						|
			if err != nil {
 | 
						|
				return i, err
 | 
						|
			}
 | 
						|
			if cnst, ok := instr.Index.(*ssa.Const); ok {
 | 
						|
				index, _ := constant.Int64Val(cnst.Value)
 | 
						|
				switch xPtr := x.(type) {
 | 
						|
				case *GlobalValue:
 | 
						|
					x = xPtr.Global.initializer
 | 
						|
				case *PointerValue:
 | 
						|
					x = *xPtr.Elem
 | 
						|
				default:
 | 
						|
					panic("expected a pointer")
 | 
						|
				}
 | 
						|
				switch x := x.(type) {
 | 
						|
				case *ArrayValue:
 | 
						|
					locals[instr] = &PointerValue{&x.Elems[index]}
 | 
						|
				default:
 | 
						|
					return i, errors.New("todo: init IndexAddr not on an array or struct")
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				return i, errors.New("todo: init IndexAddr index: " + instr.Index.String())
 | 
						|
			}
 | 
						|
		case *ssa.MakeInterface:
 | 
						|
			locals[instr] = &InterfaceValue{instr.X.Type(), locals[instr.X]}
 | 
						|
		case *ssa.MakeMap:
 | 
						|
			locals[instr] = &MapValue{instr.Type().Underlying().(*types.Map), nil, nil}
 | 
						|
		case *ssa.MapUpdate:
 | 
						|
			// Assume no duplicate keys exist. This is most likely true for
 | 
						|
			// autogenerated code, but may not be true when trying to interpret
 | 
						|
			// user code.
 | 
						|
			key, err := p.getValue(instr.Key, locals)
 | 
						|
			if err != nil {
 | 
						|
				return i, err
 | 
						|
			}
 | 
						|
			value, err := p.getValue(instr.Value, locals)
 | 
						|
			if err != nil {
 | 
						|
				return i, err
 | 
						|
			}
 | 
						|
			x := locals[instr.Map].(*MapValue)
 | 
						|
			x.Keys = append(x.Keys, key)
 | 
						|
			x.Values = append(x.Values, value)
 | 
						|
		case *ssa.Slice:
 | 
						|
			// Turn a just-allocated array into a slice.
 | 
						|
			if instr.Low != nil || instr.High != nil || instr.Max != nil {
 | 
						|
				return i, errors.New("init: slice expression with bounds")
 | 
						|
			}
 | 
						|
			source, err := p.getValue(instr.X, locals)
 | 
						|
			if err != nil {
 | 
						|
				return i, err
 | 
						|
			}
 | 
						|
			switch source := source.(type) {
 | 
						|
			case *PointerValue: // pointer to array
 | 
						|
				array := (*source.Elem).(*ArrayValue)
 | 
						|
				locals[instr] = &SliceValue{instr.Type().Underlying().(*types.Slice), array}
 | 
						|
			default:
 | 
						|
				return i, errors.New("init: unknown slice type")
 | 
						|
			}
 | 
						|
		case *ssa.Store:
 | 
						|
			if addr, ok := instr.Addr.(*ssa.Global); ok {
 | 
						|
				if strings.HasPrefix(instr.Addr.Name(), "__cgofn__cgo_") || strings.HasPrefix(instr.Addr.Name(), "_cgo_") {
 | 
						|
					// Ignore CGo global variables which we don't use.
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				value, err := p.getValue(instr.Val, locals)
 | 
						|
				if err != nil {
 | 
						|
					return i, err
 | 
						|
				}
 | 
						|
				p.GetGlobal(addr).initializer = value
 | 
						|
			} else if addr, ok := locals[instr.Addr]; ok {
 | 
						|
				value, err := p.getValue(instr.Val, locals)
 | 
						|
				if err != nil {
 | 
						|
					return i, err
 | 
						|
				}
 | 
						|
				if addr, ok := addr.(*PointerValue); ok {
 | 
						|
					*(addr.Elem) = value
 | 
						|
				} else {
 | 
						|
					panic("store to non-pointer")
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				return i, errors.New("todo: init Store: " + instr.String())
 | 
						|
			}
 | 
						|
		case *ssa.UnOp:
 | 
						|
			if instr.Op != token.MUL || instr.CommaOk {
 | 
						|
				return i, errors.New("init: unknown unop: " + instr.String())
 | 
						|
			}
 | 
						|
			valPtr, err := p.getValue(instr.X, locals)
 | 
						|
			if err != nil {
 | 
						|
				return i, err
 | 
						|
			}
 | 
						|
			switch valPtr := valPtr.(type) {
 | 
						|
			case *GlobalValue:
 | 
						|
				locals[instr] = valPtr.Global.initializer
 | 
						|
			case *PointerValue:
 | 
						|
				locals[instr] = *valPtr.Elem
 | 
						|
			default:
 | 
						|
				panic("expected a pointer")
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			return i, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return len(instrs), nil // normally unreachable
 | 
						|
}
 | 
						|
 | 
						|
func (p *Program) getValue(value ssa.Value, locals map[ssa.Value]Value) (Value, error) {
 | 
						|
	switch value := value.(type) {
 | 
						|
	case *ssa.Const:
 | 
						|
		return &ConstValue{value}, nil
 | 
						|
	case *ssa.Function:
 | 
						|
		return &FunctionValue{value.Type(), value}, nil
 | 
						|
	case *ssa.Global:
 | 
						|
		if strings.HasPrefix(value.Name(), "__cgofn__cgo_") || strings.HasPrefix(value.Name(), "_cgo_") {
 | 
						|
			// Ignore CGo global variables which we don't use.
 | 
						|
			return nil, cgoWrapperError
 | 
						|
		}
 | 
						|
		g := p.GetGlobal(value)
 | 
						|
		if g.initializer == nil {
 | 
						|
			value, err := p.getZeroValue(value.Type().Underlying().(*types.Pointer).Elem())
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			g.initializer = value
 | 
						|
		}
 | 
						|
		return &GlobalValue{g}, nil
 | 
						|
	default:
 | 
						|
		if local, ok := locals[value]; ok {
 | 
						|
			return local, nil
 | 
						|
		} else {
 | 
						|
			return nil, errors.New("todo: init: unknown value: " + value.String())
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (p *Program) getZeroValue(t types.Type) (Value, error) {
 | 
						|
	switch typ := t.Underlying().(type) {
 | 
						|
	case *types.Array:
 | 
						|
		elems := make([]Value, typ.Len())
 | 
						|
		for i := range elems {
 | 
						|
			elem, err := p.getZeroValue(typ.Elem())
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			elems[i] = elem
 | 
						|
		}
 | 
						|
		return &ArrayValue{typ.Elem(), elems}, nil
 | 
						|
	case *types.Basic:
 | 
						|
		return &ZeroBasicValue{typ}, nil
 | 
						|
	case *types.Signature:
 | 
						|
		return &FunctionValue{typ, nil}, nil
 | 
						|
	case *types.Interface:
 | 
						|
		return &InterfaceValue{typ, nil}, nil
 | 
						|
	case *types.Map:
 | 
						|
		return &MapValue{typ, nil, nil}, nil
 | 
						|
	case *types.Pointer:
 | 
						|
		return &PointerValue{nil}, nil
 | 
						|
	case *types.Struct:
 | 
						|
		elems := make([]Value, typ.NumFields())
 | 
						|
		for i := range elems {
 | 
						|
			elem, err := p.getZeroValue(typ.Field(i).Type())
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			elems[i] = elem
 | 
						|
		}
 | 
						|
		return &StructValue{t, elems}, nil
 | 
						|
	case *types.Slice:
 | 
						|
		return &SliceValue{typ, nil}, nil
 | 
						|
	default:
 | 
						|
		return nil, errors.New("todo: init: unknown global type: " + typ.String())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Boxed value for interpreter.
 | 
						|
type Value interface {
 | 
						|
}
 | 
						|
 | 
						|
type ConstValue struct {
 | 
						|
	Expr *ssa.Const
 | 
						|
}
 | 
						|
 | 
						|
type ZeroBasicValue struct {
 | 
						|
	Type *types.Basic
 | 
						|
}
 | 
						|
 | 
						|
type PointerValue struct {
 | 
						|
	Elem *Value
 | 
						|
}
 | 
						|
 | 
						|
type FunctionValue struct {
 | 
						|
	Type types.Type
 | 
						|
	Elem *ssa.Function
 | 
						|
}
 | 
						|
 | 
						|
type InterfaceValue struct {
 | 
						|
	Type types.Type
 | 
						|
	Elem Value
 | 
						|
}
 | 
						|
 | 
						|
type PointerBitCastValue struct {
 | 
						|
	Type types.Type
 | 
						|
	Elem Value
 | 
						|
}
 | 
						|
 | 
						|
type PointerToUintptrValue struct {
 | 
						|
	Elem Value
 | 
						|
}
 | 
						|
 | 
						|
type GlobalValue struct {
 | 
						|
	Global *Global
 | 
						|
}
 | 
						|
 | 
						|
type ArrayValue struct {
 | 
						|
	ElemType types.Type
 | 
						|
	Elems    []Value
 | 
						|
}
 | 
						|
 | 
						|
type StructValue struct {
 | 
						|
	Type   types.Type // types.Struct or types.Named
 | 
						|
	Fields []Value
 | 
						|
}
 | 
						|
 | 
						|
type SliceValue struct {
 | 
						|
	Type  *types.Slice
 | 
						|
	Array *ArrayValue
 | 
						|
}
 | 
						|
 | 
						|
type MapValue struct {
 | 
						|
	Type   *types.Map
 | 
						|
	Keys   []Value
 | 
						|
	Values []Value
 | 
						|
}
 |