github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/internal/typeparams/instance.go (about)

     1  package typeparams
     2  
     3  import (
     4  	"fmt"
     5  	"go/types"
     6  
     7  	"github.com/gopherjs/gopherjs/compiler/internal/symbol"
     8  	"github.com/gopherjs/gopherjs/compiler/typesutil"
     9  )
    10  
    11  // Instance of a generic type or function.
    12  //
    13  // Non-generic objects can be represented as an Instance with zero type params,
    14  // they are instances of themselves.
    15  type Instance struct {
    16  	Object types.Object       // Object to be instantiated.
    17  	TArgs  typesutil.TypeList // Type params to instantiate with.
    18  }
    19  
    20  // String returns a string representation of the Instance.
    21  //
    22  // Two semantically different instances may have the same string representation
    23  // if the instantiated object or its type arguments shadow other types.
    24  func (i *Instance) String() string {
    25  	sym := symbol.New(i.Object).String()
    26  	if len(i.TArgs) == 0 {
    27  		return sym
    28  	}
    29  
    30  	return fmt.Sprintf("%s<%s>", sym, i.TArgs)
    31  }
    32  
    33  // TypeString returns a Go type string representing the instance (suitable for %T verb).
    34  func (i *Instance) TypeString() string {
    35  	tArgs := ""
    36  	if len(i.TArgs) > 0 {
    37  		tArgs = "[" + i.TArgs.String() + "]"
    38  	}
    39  	return fmt.Sprintf("%s.%s%s", i.Object.Pkg().Name(), i.Object.Name(), tArgs)
    40  }
    41  
    42  // IsTrivial returns true if this is an instance of a non-generic object.
    43  func (i *Instance) IsTrivial() bool {
    44  	return len(i.TArgs) == 0
    45  }
    46  
    47  // Recv returns an instance of the receiver type of a method.
    48  //
    49  // Returns zero value if not a method.
    50  func (i *Instance) Recv() Instance {
    51  	sig, ok := i.Object.Type().(*types.Signature)
    52  	if !ok {
    53  		return Instance{}
    54  	}
    55  	recv := typesutil.RecvType(sig)
    56  	if recv == nil {
    57  		return Instance{}
    58  	}
    59  	return Instance{
    60  		Object: recv.Obj(),
    61  		TArgs:  i.TArgs,
    62  	}
    63  }
    64  
    65  // InstanceSet allows collecting and processing unique Instances.
    66  //
    67  // Each Instance may be added to the set any number of times, but it will be
    68  // returned for processing exactly once. Processing order is not specified.
    69  type InstanceSet struct {
    70  	values      []Instance
    71  	unprocessed int              // Index in values for the next unprocessed element.
    72  	seen        InstanceMap[int] // Maps instance to a unique numeric id.
    73  }
    74  
    75  // Add instances to the set. Instances that have been previously added to the
    76  // set won't be requeued for processing regardless of whether they have been
    77  // processed already.
    78  func (iset *InstanceSet) Add(instances ...Instance) *InstanceSet {
    79  	for _, inst := range instances {
    80  		if iset.seen.Has(inst) {
    81  			continue
    82  		}
    83  		iset.seen.Set(inst, iset.seen.Len())
    84  		iset.values = append(iset.values, inst)
    85  	}
    86  	return iset
    87  }
    88  
    89  // ID returns a unique numeric identifier assigned to an instance in the set.
    90  // The ID is guaranteed to be unique among all instances of the same object
    91  // within a given program. The ID will be consistent, as long as instances are
    92  // added to the set in the same order.
    93  //
    94  // In order to have an ID assigned, the instance must have been previously added
    95  // to the set.
    96  //
    97  // Note: these ids are used in the generated code as keys to the specific
    98  // type/function instantiation in the type/function object. Using this has two
    99  // advantages:
   100  //
   101  // - More compact generated code compared to string keys derived from type args.
   102  //
   103  // - Collision avoidance in case of two different types having the same name due
   104  // to shadowing.
   105  //
   106  // Here's an example where it's very difficult to assign non-colliding
   107  // name-based keys to the two different types T:
   108  //
   109  //	func foo() {
   110  //	    type T int
   111  //	    { type T string } // Code block creates a new nested scope allowing for shadowing.
   112  //	}
   113  func (iset *InstanceSet) ID(inst Instance) int {
   114  	id, ok := iset.seen.get(inst)
   115  	if !ok {
   116  		panic(fmt.Errorf("requesting ID of instance %v that hasn't been added to the set", inst))
   117  	}
   118  	return id
   119  }
   120  
   121  // next returns the next Instance to be processed.
   122  //
   123  // If there are no unprocessed instances, the second returned value will be false.
   124  func (iset *InstanceSet) next() (Instance, bool) {
   125  	if iset.exhausted() {
   126  		return Instance{}, false
   127  	}
   128  	next := iset.values[iset.unprocessed]
   129  	iset.unprocessed++
   130  	return next, true
   131  }
   132  
   133  // exhausted returns true if there are no unprocessed instances in the set.
   134  func (iset *InstanceSet) exhausted() bool { return len(iset.values) <= iset.unprocessed }
   135  
   136  // Values returns instances that are currently in the set. Order is not specified.
   137  func (iset *InstanceSet) Values() []Instance {
   138  	return iset.values
   139  }
   140  
   141  // PackageInstanceSets stores an InstanceSet for each package in a program, keyed
   142  // by import path.
   143  type PackageInstanceSets map[string]*InstanceSet
   144  
   145  // Pkg returns InstanceSet for objects defined in the given package.
   146  func (i PackageInstanceSets) Pkg(pkg *types.Package) *InstanceSet {
   147  	path := pkg.Path()
   148  	iset, ok := i[path]
   149  	if !ok {
   150  		iset = &InstanceSet{}
   151  		i[path] = iset
   152  	}
   153  	return iset
   154  }
   155  
   156  // Add instances to the appropriate package's set. Automatically initialized
   157  // new per-package sets upon a first encounter.
   158  func (i PackageInstanceSets) Add(instances ...Instance) {
   159  	for _, inst := range instances {
   160  		i.Pkg(inst.Object.Pkg()).Add(inst)
   161  	}
   162  }
   163  
   164  // ID returns a unique numeric identifier assigned to an instance in the set.
   165  //
   166  // See: InstanceSet.ID().
   167  func (i PackageInstanceSets) ID(inst Instance) int {
   168  	return i.Pkg(inst.Object.Pkg()).ID(inst)
   169  }