Home | History | Annotate | Download | only in fix
      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