Example #1
0
// IntuitiveMethodSet returns the intuitive method set of a type, T.
//
// The result contains MethodSet(T) and additionally, if T is a
// concrete type, methods belonging to *T if there is no identically
// named method on T itself.  This corresponds to user intuition about
// method sets; this function is intended only for user interfaces.
//
// The order of the result is as for types.MethodSet(T).
//
func IntuitiveMethodSet(T types.Type, msets *types.MethodSetCache) []*types.Selection {
	var result []*types.Selection
	mset := msets.MethodSet(T)
	if _, ok := T.Underlying().(*types.Interface); ok {
		for i, n := 0, mset.Len(); i < n; i++ {
			result = append(result, mset.At(i))
		}
	} else {
		pmset := msets.MethodSet(types.NewPointer(T))
		for i, n := 0, pmset.Len(); i < n; i++ {
			meth := pmset.At(i)
			if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
				meth = m
			}
			result = append(result, meth)
		}
	}
	return result
}
Example #2
0
// computeImplements computes the "implements" relation over all pairs
// of named types in allNamed.
func computeImplements(cache *types.MethodSetCache, allNamed []*types.Named) map[*types.Named]implementsFacts {
	// Information about a single type's method set.
	type msetInfo struct {
		typ          types.Type
		mset         *types.MethodSet
		mask1, mask2 uint64
	}

	initMsetInfo := func(info *msetInfo, typ types.Type) {
		info.typ = typ
		info.mset = cache.MethodSet(typ)
		for i := 0; i < info.mset.Len(); i++ {
			name := info.mset.At(i).Obj().Name()
			info.mask1 |= 1 << methodBit(name[0])
			info.mask2 |= 1 << methodBit(name[len(name)-1])
		}
	}

	// satisfies(T, U) reports whether type T satisfies type U.
	// U must be an interface.
	//
	// Since there are thousands of types (and thus millions of
	// pairs of types) and types.Assignable(T, U) is relatively
	// expensive, we compute assignability directly from the
	// method sets.  (At least one of T and U must be an
	// interface.)
	//
	// We use a trick (thanks gri!) related to a Bloom filter to
	// quickly reject most tests, which are false.  For each
	// method set, we precompute a mask, a set of bits, one per
	// distinct initial byte of each method name.  Thus the mask
	// for io.ReadWriter would be {'R','W'}.  AssignableTo(T, U)
	// cannot be true unless mask(T)&mask(U)==mask(U).
	//
	// As with a Bloom filter, we can improve precision by testing
	// additional hashes, e.g. using the last letter of each
	// method name, so long as the subset mask property holds.
	//
	// When analyzing the standard library, there are about 1e6
	// calls to satisfies(), of which 0.6% return true.  With a
	// 1-hash filter, 95% of calls avoid the expensive check; with
	// a 2-hash filter, this grows to 98.2%.
	satisfies := func(T, U *msetInfo) bool {
		return T.mask1&U.mask1 == U.mask1 &&
			T.mask2&U.mask2 == U.mask2 &&
			containsAllIdsOf(T.mset, U.mset)
	}

	// Information about a named type N, and perhaps also *N.
	type namedInfo struct {
		isInterface bool
		base        msetInfo // N
		ptr         msetInfo // *N, iff N !isInterface
	}

	var infos []namedInfo

	// Precompute the method sets and their masks.
	for _, N := range allNamed {
		var info namedInfo
		initMsetInfo(&info.base, N)
		_, info.isInterface = N.Underlying().(*types.Interface)
		if !info.isInterface {
			initMsetInfo(&info.ptr, types.NewPointer(N))
		}

		if info.base.mask1|info.ptr.mask1 == 0 {
			continue // neither N nor *N has methods
		}

		infos = append(infos, info)
	}

	facts := make(map[*types.Named]implementsFacts)

	// Test all pairs of distinct named types (T, U).
	// TODO(adonovan): opt: compute (U, T) at the same time.
	for t := range infos {
		T := &infos[t]
		var to, from, fromPtr []types.Type
		for u := range infos {
			if t == u {
				continue
			}
			U := &infos[u]
			switch {
			case T.isInterface && U.isInterface:
				if satisfies(&U.base, &T.base) {
					to = append(to, U.base.typ)
				}
				if satisfies(&T.base, &U.base) {
					from = append(from, U.base.typ)
				}
			case T.isInterface: // U concrete
				if satisfies(&U.base, &T.base) {
					to = append(to, U.base.typ)
				} else if satisfies(&U.ptr, &T.base) {
					to = append(to, U.ptr.typ)
				}
			case U.isInterface: // T concrete
				if satisfies(&T.base, &U.base) {
					from = append(from, U.base.typ)
				} else if satisfies(&T.ptr, &U.base) {
					fromPtr = append(fromPtr, U.base.typ)
				}
			}
		}

		// Sort types (arbitrarily) to avoid nondeterminism.
		sort.Sort(typesByString(to))
		sort.Sort(typesByString(from))
		sort.Sort(typesByString(fromPtr))

		facts[T.base.typ.(*types.Named)] = implementsFacts{to, from, fromPtr}
	}

	return facts
}