github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/cmd/fix/cftype.go (about) 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 }