...

Source file src/pkg/cmd/fix/cftype.go

     1	// Copyright 2017 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package main
     6	
     7	import (
     8		"go/ast"
     9		"go/token"
    10		"reflect"
    11		"strings"
    12	)
    13	
    14	func init() {
    15		register(cftypeFix)
    16	}
    17	
    18	var cftypeFix = fix{
    19		name:     "cftype",
    20		date:     "2017-09-27",
    21		f:        cftypefix,
    22		desc:     `Fixes initializers and casts of C.*Ref and JNI types`,
    23		disabled: false,
    24	}
    25	
    26	// Old state:
    27	//   type CFTypeRef unsafe.Pointer
    28	// New state:
    29	//   type CFTypeRef uintptr
    30	// and similar for other *Ref types.
    31	// This fix finds nils initializing these types and replaces the nils with 0s.
    32	func cftypefix(f *ast.File) bool {
    33		return typefix(f, func(s string) bool {
    34			return strings.HasPrefix(s, "C.") && strings.HasSuffix(s, "Ref") && s != "C.CFAllocatorRef"
    35		})
    36	}
    37	
    38	// typefix replaces nil with 0 for all nils whose type, when passed to badType, returns true.
    39	func typefix(f *ast.File, badType func(string) bool) bool {
    40		if !imports(f, "C") {
    41			return false
    42		}
    43		typeof, _ := typecheck(&TypeConfig{}, f)
    44		changed := false
    45	
    46		// step 1: Find all the nils with the offending types.
    47		// Compute their replacement.
    48		badNils := map[interface{}]ast.Expr{}
    49		walk(f, func(n interface{}) {
    50			if i, ok := n.(*ast.Ident); ok && i.Name == "nil" && badType(typeof[n]) {
    51				badNils[n] = &ast.BasicLit{ValuePos: i.NamePos, Kind: token.INT, Value: "0"}
    52			}
    53		})
    54	
    55		// step 2: find all uses of the bad nils, replace them with 0.
    56		// There's no easy way to map from an ast.Expr to all the places that use them, so
    57		// we use reflect to find all such references.
    58		if len(badNils) > 0 {
    59			exprType := reflect.TypeOf((*ast.Expr)(nil)).Elem()
    60			exprSliceType := reflect.TypeOf(([]ast.Expr)(nil))
    61			walk(f, func(n interface{}) {
    62				if n == nil {
    63					return
    64				}
    65				v := reflect.ValueOf(n)
    66				if v.Type().Kind() != reflect.Ptr {
    67					return
    68				}
    69				if v.IsNil() {
    70					return
    71				}
    72				v = v.Elem()
    73				if v.Type().Kind() != reflect.Struct {
    74					return
    75				}
    76				for i := 0; i < v.NumField(); i++ {
    77					f := v.Field(i)
    78					if f.Type() == exprType {
    79						if r := badNils[f.Interface()]; r != nil {
    80							f.Set(reflect.ValueOf(r))
    81							changed = true
    82						}
    83					}
    84					if f.Type() == exprSliceType {
    85						for j := 0; j < f.Len(); j++ {
    86							e := f.Index(j)
    87							if r := badNils[e.Interface()]; r != nil {
    88								e.Set(reflect.ValueOf(r))
    89								changed = true
    90							}
    91						}
    92					}
    93				}
    94			})
    95		}
    96	
    97		// step 3: fix up invalid casts.
    98		// It used to be ok to cast between *unsafe.Pointer and *C.CFTypeRef in a single step.
    99		// Now we need unsafe.Pointer as an intermediate cast.
   100		// (*unsafe.Pointer)(x) where x is type *bad -> (*unsafe.Pointer)(unsafe.Pointer(x))
   101		// (*bad.type)(x) where x is type *unsafe.Pointer -> (*bad.type)(unsafe.Pointer(x))
   102		walk(f, func(n interface{}) {
   103			if n == nil {
   104				return
   105			}
   106			// Find pattern like (*a.b)(x)
   107			c, ok := n.(*ast.CallExpr)
   108			if !ok {
   109				return
   110			}
   111			if len(c.Args) != 1 {
   112				return
   113			}
   114			p, ok := c.Fun.(*ast.ParenExpr)
   115			if !ok {
   116				return
   117			}
   118			s, ok := p.X.(*ast.StarExpr)
   119			if !ok {
   120				return
   121			}
   122			t, ok := s.X.(*ast.SelectorExpr)
   123			if !ok {
   124				return
   125			}
   126			pkg, ok := t.X.(*ast.Ident)
   127			if !ok {
   128				return
   129			}
   130			dst := pkg.Name + "." + t.Sel.Name
   131			src := typeof[c.Args[0]]
   132			if badType(dst) && src == "*unsafe.Pointer" ||
   133				dst == "unsafe.Pointer" && strings.HasPrefix(src, "*") && badType(src[1:]) {
   134				c.Args[0] = &ast.CallExpr{
   135					Fun:  &ast.SelectorExpr{X: &ast.Ident{Name: "unsafe"}, Sel: &ast.Ident{Name: "Pointer"}},
   136					Args: []ast.Expr{c.Args[0]},
   137				}
   138				changed = true
   139			}
   140		})
   141	
   142		return changed
   143	}
   144	

View as plain text