github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/ssa/instantiate_test.go (about)

     1  // Copyright 2022 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 ssa
     6  
     7  // Note: Tests use unexported functions.
     8  
     9  import (
    10  	"bytes"
    11  	"go/types"
    12  	"reflect"
    13  	"sort"
    14  	"testing"
    15  
    16  	"github.com/powerman/golang-tools/go/loader"
    17  	"github.com/powerman/golang-tools/internal/typeparams"
    18  )
    19  
    20  // TestNeedsInstance ensures that new method instances can be created via needsInstance,
    21  // that TypeArgs are as expected, and can be accessed via _Instances.
    22  func TestNeedsInstance(t *testing.T) {
    23  	if !typeparams.Enabled {
    24  		return
    25  	}
    26  	const input = `
    27  package p
    28  
    29  import "unsafe"
    30  
    31  type Pointer[T any] struct {
    32  	v unsafe.Pointer
    33  }
    34  
    35  func (x *Pointer[T]) Load() *T {
    36  	return (*T)(LoadPointer(&x.v))
    37  }
    38  
    39  func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
    40  `
    41  	// The SSA members for this package should look something like this:
    42  	//      func  LoadPointer func(addr *unsafe.Pointer) (val unsafe.Pointer)
    43  	//      type  Pointer     struct{v unsafe.Pointer}
    44  	//        method (*Pointer[T any]) Load() *T
    45  	//      func  init        func()
    46  	//      var   init$guard  bool
    47  
    48  	// Parse
    49  	var conf loader.Config
    50  	f, err := conf.ParseFile("<input>", input)
    51  	if err != nil {
    52  		t.Fatalf("parse: %v", err)
    53  	}
    54  	conf.CreateFromFiles("p", f)
    55  
    56  	// Load
    57  	lprog, err := conf.Load()
    58  	if err != nil {
    59  		t.Fatalf("Load: %v", err)
    60  	}
    61  
    62  	// Create and build SSA
    63  	prog := NewProgram(lprog.Fset, 0)
    64  
    65  	for _, info := range lprog.AllPackages {
    66  		prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
    67  	}
    68  
    69  	p := prog.Package(lprog.Package("p").Pkg)
    70  	p.Build()
    71  
    72  	ptr := p.Type("Pointer").Type().(*types.Named)
    73  	if ptr.NumMethods() != 1 {
    74  		t.Fatalf("Expected Pointer to have 1 method. got %d", ptr.NumMethods())
    75  	}
    76  
    77  	obj := ptr.Method(0)
    78  	if obj.Name() != "Load" {
    79  		t.Errorf("Expected Pointer to have method named 'Load'. got %q", obj.Name())
    80  	}
    81  
    82  	meth := prog.FuncValue(obj)
    83  
    84  	var cr creator
    85  	intSliceTyp := types.NewSlice(types.Typ[types.Int])
    86  	instance := prog.needsInstance(meth, []types.Type{intSliceTyp}, &cr)
    87  	if len(cr) != 1 {
    88  		t.Errorf("Expected first instance to create a function. got %d created functions", len(cr))
    89  	}
    90  	if instance._Origin != meth {
    91  		t.Errorf("Expected Origin of %s to be %s. got %s", instance, meth, instance._Origin)
    92  	}
    93  	if len(instance._TypeArgs) != 1 || !types.Identical(instance._TypeArgs[0], intSliceTyp) {
    94  		t.Errorf("Expected TypeArgs of %s to be %v. got %v", instance, []types.Type{intSliceTyp}, instance._TypeArgs)
    95  	}
    96  	instances := prog._Instances(meth)
    97  	if want := []*Function{instance}; !reflect.DeepEqual(instances, want) {
    98  		t.Errorf("Expected instances of %s to be %v. got %v", meth, want, instances)
    99  	}
   100  
   101  	// A second request with an identical type returns the same Function.
   102  	second := prog.needsInstance(meth, []types.Type{types.NewSlice(types.Typ[types.Int])}, &cr)
   103  	if second != instance || len(cr) != 1 {
   104  		t.Error("Expected second identical instantiation to not create a function")
   105  	}
   106  
   107  	// Add a second instance.
   108  	inst2 := prog.needsInstance(meth, []types.Type{types.NewSlice(types.Typ[types.Uint])}, &cr)
   109  	instances = prog._Instances(meth)
   110  
   111  	// Note: instance.Name() < inst2.Name()
   112  	sort.Slice(instances, func(i, j int) bool {
   113  		return instances[i].Name() < instances[j].Name()
   114  	})
   115  	if want := []*Function{instance, inst2}; !reflect.DeepEqual(instances, want) {
   116  		t.Errorf("Expected instances of %s to be %v. got %v", meth, want, instances)
   117  	}
   118  
   119  	// build and sanity check manually created instance.
   120  	var b builder
   121  	b.buildFunction(instance)
   122  	var buf bytes.Buffer
   123  	if !sanityCheck(instance, &buf) {
   124  		t.Errorf("sanityCheck of %s failed with: %s", instance, buf.String())
   125  	}
   126  }