You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			263 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
			
		
		
	
	
			263 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
| package ssa2ast
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/token"
 | |
| 	"go/types"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| )
 | |
| 
 | |
| type TypeConverter struct {
 | |
| 	resolver ImportNameResolver
 | |
| }
 | |
| 
 | |
| func NewTypeConverted(resolver ImportNameResolver) *TypeConverter {
 | |
| 	return &TypeConverter{resolver: resolver}
 | |
| }
 | |
| 
 | |
| func (tc *TypeConverter) Convert(typ types.Type) (ast.Expr, error) {
 | |
| 	switch typ := types.Unalias(typ).(type) {
 | |
| 	case *types.Array:
 | |
| 		eltExpr, err := tc.Convert(typ.Elem())
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return &ast.ArrayType{
 | |
| 			Len: &ast.BasicLit{
 | |
| 				Kind:  token.INT,
 | |
| 				Value: strconv.FormatInt(typ.Len(), 10),
 | |
| 			},
 | |
| 			Elt: eltExpr,
 | |
| 		}, nil
 | |
| 	case *types.Basic:
 | |
| 		if typ.Kind() == types.UnsafePointer {
 | |
| 			unsafePkgIdent := tc.resolver(types.Unsafe)
 | |
| 			if unsafePkgIdent == nil {
 | |
| 				return nil, fmt.Errorf("cannot resolve unsafe package")
 | |
| 			}
 | |
| 			return &ast.SelectorExpr{X: unsafePkgIdent, Sel: ast.NewIdent("Pointer")}, nil
 | |
| 		}
 | |
| 		return ast.NewIdent(typ.Name()), nil
 | |
| 	case *types.Chan:
 | |
| 		chanValueExpr, err := tc.Convert(typ.Elem())
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		chanExpr := &ast.ChanType{Value: chanValueExpr}
 | |
| 		switch typ.Dir() {
 | |
| 		case types.SendRecv:
 | |
| 			chanExpr.Dir = ast.SEND | ast.RECV
 | |
| 		case types.RecvOnly:
 | |
| 			chanExpr.Dir = ast.RECV
 | |
| 		case types.SendOnly:
 | |
| 			chanExpr.Dir = ast.SEND
 | |
| 		}
 | |
| 		return chanExpr, nil
 | |
| 	case *types.Interface:
 | |
| 		methods := &ast.FieldList{}
 | |
| 		hasComparable := false
 | |
| 		for i := range typ.NumEmbeddeds() {
 | |
| 			embeddedType := typ.EmbeddedType(i)
 | |
| 			if namedType, ok := embeddedType.(*types.Named); ok && namedType.String() == "comparable" {
 | |
| 				hasComparable = true
 | |
| 			}
 | |
| 			embeddedExpr, err := tc.Convert(embeddedType)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			methods.List = append(methods.List, &ast.Field{Type: embeddedExpr})
 | |
| 		}
 | |
| 
 | |
| 		// Special case, handle "comparable" interface itself
 | |
| 		if !hasComparable && typ.IsComparable() {
 | |
| 			methods.List = append(methods.List, &ast.Field{Type: ast.NewIdent("comparable")})
 | |
| 		}
 | |
| 		for i := range typ.NumExplicitMethods() {
 | |
| 			method := typ.ExplicitMethod(i)
 | |
| 			methodSig, err := tc.Convert(method.Type())
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			methods.List = append(methods.List, &ast.Field{
 | |
| 				Names: []*ast.Ident{ast.NewIdent(method.Name())},
 | |
| 				Type:  methodSig,
 | |
| 			})
 | |
| 		}
 | |
| 		return &ast.InterfaceType{Methods: methods}, nil
 | |
| 	case *types.Map:
 | |
| 		keyExpr, err := tc.Convert(typ.Key())
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		valueExpr, err := tc.Convert(typ.Elem())
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return &ast.MapType{Key: keyExpr, Value: valueExpr}, nil
 | |
| 	case *types.Named:
 | |
| 		obj := typ.Obj()
 | |
| 
 | |
| 		// TODO: rewrite struct inlining without reflection hack
 | |
| 		if parent := obj.Parent(); parent != nil {
 | |
| 			isFuncScope := reflect.ValueOf(parent).Elem().FieldByName("isFunc")
 | |
| 			if isFuncScope.Bool() {
 | |
| 				return tc.Convert(obj.Type().Underlying())
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		var namedExpr ast.Expr
 | |
| 		if pkgIdent := tc.resolver(obj.Pkg()); pkgIdent != nil {
 | |
| 			// reference to unexported named emulated through new interface with explicit declarated methods
 | |
| 			if !token.IsExported(obj.Name()) {
 | |
| 				var methods []*types.Func
 | |
| 				for i := range typ.NumMethods() {
 | |
| 					method := typ.Method(i)
 | |
| 					if token.IsExported(method.Name()) {
 | |
| 						methods = append(methods, method)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				fakeInterface := types.NewInterfaceType(methods, nil)
 | |
| 				return tc.Convert(fakeInterface)
 | |
| 			}
 | |
| 			namedExpr = &ast.SelectorExpr{X: pkgIdent, Sel: ast.NewIdent(obj.Name())}
 | |
| 		} else {
 | |
| 			namedExpr = ast.NewIdent(obj.Name())
 | |
| 		}
 | |
| 
 | |
| 		typeParams := typ.TypeArgs()
 | |
| 		if typeParams == nil || typeParams.Len() == 0 {
 | |
| 			return namedExpr, nil
 | |
| 		}
 | |
| 		if typeParams.Len() == 1 {
 | |
| 			typeParamExpr, err := tc.Convert(typeParams.At(0))
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			return &ast.IndexExpr{X: namedExpr, Index: typeParamExpr}, nil
 | |
| 		}
 | |
| 		genericExpr := &ast.IndexListExpr{X: namedExpr}
 | |
| 		for i := range typeParams.Len() {
 | |
| 			typeArgs := typeParams.At(i)
 | |
| 			typeParamExpr, err := tc.Convert(typeArgs)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			genericExpr.Indices = append(genericExpr.Indices, typeParamExpr)
 | |
| 		}
 | |
| 		return genericExpr, nil
 | |
| 	case *types.Pointer:
 | |
| 		expr, err := tc.Convert(typ.Elem())
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return &ast.StarExpr{X: expr}, nil
 | |
| 	case *types.Signature:
 | |
| 		funcSigExpr := &ast.FuncType{Params: &ast.FieldList{}}
 | |
| 		if sigParams := typ.Params(); sigParams != nil {
 | |
| 			for i := range sigParams.Len() {
 | |
| 				param := sigParams.At(i)
 | |
| 
 | |
| 				var paramType ast.Expr
 | |
| 				if typ.Variadic() && i == sigParams.Len()-1 {
 | |
| 					slice := param.Type().(*types.Slice)
 | |
| 
 | |
| 					eltExpr, err := tc.Convert(slice.Elem())
 | |
| 					if err != nil {
 | |
| 						return nil, err
 | |
| 					}
 | |
| 					paramType = &ast.Ellipsis{Elt: eltExpr}
 | |
| 				} else {
 | |
| 					paramExpr, err := tc.Convert(param.Type())
 | |
| 					if err != nil {
 | |
| 						return nil, err
 | |
| 					}
 | |
| 					paramType = paramExpr
 | |
| 				}
 | |
| 				f := &ast.Field{Type: paramType}
 | |
| 				if name := param.Name(); name != "" {
 | |
| 					f.Names = []*ast.Ident{ast.NewIdent(name)}
 | |
| 				}
 | |
| 				funcSigExpr.Params.List = append(funcSigExpr.Params.List, f)
 | |
| 			}
 | |
| 		}
 | |
| 		if sigResults := typ.Results(); sigResults != nil {
 | |
| 			funcSigExpr.Results = &ast.FieldList{}
 | |
| 			for i := range sigResults.Len() {
 | |
| 				result := sigResults.At(i)
 | |
| 				resultExpr, err := tc.Convert(result.Type())
 | |
| 				if err != nil {
 | |
| 					return nil, err
 | |
| 				}
 | |
| 
 | |
| 				f := &ast.Field{Type: resultExpr}
 | |
| 				if name := result.Name(); name != "" {
 | |
| 					f.Names = []*ast.Ident{ast.NewIdent(name)}
 | |
| 				}
 | |
| 				funcSigExpr.Results.List = append(funcSigExpr.Results.List, f)
 | |
| 			}
 | |
| 		}
 | |
| 		if typeParams := typ.TypeParams(); typeParams != nil {
 | |
| 			funcSigExpr.TypeParams = &ast.FieldList{}
 | |
| 			for i := range typeParams.Len() {
 | |
| 				typeParam := typeParams.At(i)
 | |
| 				resultExpr, err := tc.Convert(typeParam.Constraint().Underlying())
 | |
| 				if err != nil {
 | |
| 					return nil, err
 | |
| 				}
 | |
| 				f := &ast.Field{Type: resultExpr, Names: []*ast.Ident{ast.NewIdent(typeParam.Obj().Name())}}
 | |
| 				funcSigExpr.TypeParams.List = append(funcSigExpr.TypeParams.List, f)
 | |
| 			}
 | |
| 		}
 | |
| 		return funcSigExpr, nil
 | |
| 	case *types.Slice:
 | |
| 		eltExpr, err := tc.Convert(typ.Elem())
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return &ast.ArrayType{Elt: eltExpr}, nil
 | |
| 	case *types.Struct:
 | |
| 		fieldList := &ast.FieldList{}
 | |
| 		for i := range typ.NumFields() {
 | |
| 			f := typ.Field(i)
 | |
| 			fieldExpr, err := tc.Convert(f.Type())
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			field := &ast.Field{Type: fieldExpr}
 | |
| 			if !f.Anonymous() {
 | |
| 				field.Names = []*ast.Ident{ast.NewIdent(f.Name())}
 | |
| 			}
 | |
| 			if tag := typ.Tag(i); len(tag) > 0 {
 | |
| 				field.Tag = &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", tag)}
 | |
| 			}
 | |
| 			fieldList.List = append(fieldList.List, field)
 | |
| 		}
 | |
| 		return &ast.StructType{Fields: fieldList}, nil
 | |
| 	case *types.TypeParam:
 | |
| 		return ast.NewIdent(typ.Obj().Name()), nil
 | |
| 	case *types.Union:
 | |
| 		var unionExpr ast.Expr
 | |
| 		for i := range typ.Len() {
 | |
| 			term := typ.Term(i)
 | |
| 			expr, err := tc.Convert(term.Type())
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			if term.Tilde() {
 | |
| 				expr = &ast.UnaryExpr{Op: token.TILDE, X: expr}
 | |
| 			}
 | |
| 			if unionExpr == nil {
 | |
| 				unionExpr = expr
 | |
| 			} else {
 | |
| 				unionExpr = &ast.BinaryExpr{X: unionExpr, Op: token.OR, Y: expr}
 | |
| 			}
 | |
| 		}
 | |
| 		return unionExpr, nil
 | |
| 	default:
 | |
| 		return nil, fmt.Errorf("type %v: %w", typ, ErrUnsupported)
 | |
| 	}
 | |
| }
 |