Source file src/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[any]ast.Expr{}
    49  	walk(f, func(n any) {
    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 any) {
    62  			if n == nil {
    63  				return
    64  			}
    65  			v := reflect.ValueOf(n)
    66  			if v.Type().Kind() != reflect.Pointer {
    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 any) {
   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