Example #1
0
// compare must be called for each comparison x==y.
func (f *Finder) compare(x, y types.Type) {
	if types.AssignableTo(x, y) {
		f.assign(y, x)
	} else if types.AssignableTo(y, x) {
		f.assign(x, y)
	}
}
func (tr *Transformer) matchWildcard(xobj *types.Var, y ast.Expr) bool {
	name := xobj.Name()

	if tr.verbose {
		fmt.Fprintf(os.Stderr, "%s: wildcard %s -> %s?: ",
			tr.fset.Position(y.Pos()), name, astString(tr.fset, y))
	}

	// Check that y is assignable to the declared type of the param.
	yt := tr.info.TypeOf(y)
	if yt == nil {
		// y has no type.
		// Perhaps it is an *ast.Ellipsis in [...]T{}, or
		// an *ast.KeyValueExpr in T{k: v}.
		// Clearly these pseudo-expressions cannot match a
		// wildcard, but it would nice if we had a way to ignore
		// the difference between T{v} and T{k:v} for structs.
		return false
	}
	if !types.AssignableTo(yt, xobj.Type()) {
		if tr.verbose {
			fmt.Fprintf(os.Stderr, "%s not assignable to %s\n", yt, xobj.Type())
		}
		return false
	}

	// A wildcard matches any expression.
	// If it appears multiple times in the pattern, it must match
	// the same expression each time.
	if old, ok := tr.env[name]; ok {
		// found existing binding
		tr.allowWildcards = false
		r := tr.matchExpr(old, y)
		if tr.verbose {
			fmt.Fprintf(os.Stderr, "%t secondary match, primary was %s\n",
				r, astString(tr.fset, old))
		}
		tr.allowWildcards = true
		return r
	}

	if tr.verbose {
		fmt.Fprintf(os.Stderr, "primary match\n")
	}

	tr.env[name] = y // record binding
	return true
}
Example #3
0
func (c *typeFilterConstraint) solve(a *analysis, delta *nodeset) {
	for _, x := range delta.AppendTo(a.deltaSpace) {
		ifaceObj := nodeid(x)
		tDyn, _, indirect := a.taggedValue(ifaceObj)
		if indirect {
			// TODO(adonovan): we'll need to implement this
			// when we start creating indirect tagged objects.
			panic("indirect tagged object")
		}

		if types.AssignableTo(tDyn, c.typ) {
			if a.addLabel(c.dst, ifaceObj) {
				a.addWork(c.dst)
			}
		}
	}
}
Example #4
0
// typeAssert must be called for each type assertion x.(T) where x has
// interface type I.
func (f *Finder) typeAssert(I, T types.Type) {
	// Type assertions are slightly subtle, because they are allowed
	// to be "impossible", e.g.
	//
	// 	var x interface{f()}
	//	_ = x.(interface{f()int}) // legal
	//
	// (In hindsight, the language spec should probably not have
	// allowed this, but it's too late to fix now.)
	//
	// This means that a type assert from I to T isn't exactly a
	// constraint that T is assignable to I, but for a refactoring
	// tool it is a conditional constraint that, if T is assignable
	// to I before a refactoring, it should remain so after.

	if types.AssignableTo(T, I) {
		f.assign(I, T)
	}
}
Example #5
0
// findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil.
func findVisibleConsts(prog *ssa.Program, qpos *QueryPos) map[ssa.Const]*ssa.NamedConst {
	constants := make(map[ssa.Const]*ssa.NamedConst)
	for _, pkg := range prog.AllPackages() {
		for _, mem := range pkg.Members {
			obj, ok := mem.(*ssa.NamedConst)
			if !ok {
				continue
			}
			consttype := obj.Type()
			if !types.AssignableTo(consttype, builtinErrorType) {
				continue
			}
			if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) {
				continue
			}
			constants[*obj.Value] = obj
		}
	}

	return constants
}
Example #6
0
func (tr *Transformer) matchWildcard(xobj *types.Var, y ast.Expr) bool {
	name := xobj.Name()

	if tr.verbose {
		fmt.Fprintf(os.Stderr, "%s: wildcard %s -> %s?: ",
			tr.fset.Position(y.Pos()), name, astString(tr.fset, y))
	}

	// Check that y is assignable to the declared type of the param.
	if yt := tr.info.TypeOf(y); !types.AssignableTo(yt, xobj.Type()) {
		if tr.verbose {
			fmt.Fprintf(os.Stderr, "%s not assignable to %s\n", yt, xobj.Type())
		}
		return false
	}

	// A wildcard matches any expression.
	// If it appears multiple times in the pattern, it must match
	// the same expression each time.
	if old, ok := tr.env[name]; ok {
		// found existing binding
		tr.allowWildcards = false
		r := tr.matchExpr(old, y)
		if tr.verbose {
			fmt.Fprintf(os.Stderr, "%t secondary match, primary was %s\n",
				r, astString(tr.fset, old))
		}
		tr.allowWildcards = true
		return r
	}

	if tr.verbose {
		fmt.Fprintf(os.Stderr, "primary match\n")
	}

	tr.env[name] = y // record binding
	return true
}
Example #7
0
// Implements displays the "implements" relation as it pertains to the
// selected type.
//
func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
	// Find the selected type.
	// TODO(adonovan): fix: make it work on qualified Idents too.
	path, action := findInterestingNode(qpos.info, qpos.path)
	if action != actionType {
		return nil, fmt.Errorf("no type here")
	}
	T := qpos.info.TypeOf(path[0].(ast.Expr))
	if T == nil {
		return nil, fmt.Errorf("no type here")
	}

	// Find all named types, even local types (which can have
	// methods via promotion) and the built-in "error".
	//
	// TODO(adonovan): include all packages in PTA scope too?
	// i.e. don't reduceScope?
	//
	var allNamed []types.Type
	for _, info := range o.typeInfo {
		for _, obj := range info.Defs {
			if obj, ok := obj.(*types.TypeName); ok {
				allNamed = append(allNamed, obj.Type())
			}
		}
	}
	allNamed = append(allNamed, types.Universe.Lookup("error").Type())

	var msets types.MethodSetCache

	// Test each named type.
	var to, from, fromPtr []types.Type
	for _, U := range allNamed {
		if isInterface(T) {
			if msets.MethodSet(T).Len() == 0 {
				continue // empty interface
			}
			if isInterface(U) {
				if msets.MethodSet(U).Len() == 0 {
					continue // empty interface
				}

				// T interface, U interface
				if !types.Identical(T, U) {
					if types.AssignableTo(U, T) {
						to = append(to, U)
					}
					if types.AssignableTo(T, U) {
						from = append(from, U)
					}
				}
			} else {
				// T interface, U concrete
				if types.AssignableTo(U, T) {
					to = append(to, U)
				} else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
					to = append(to, pU)
				}
			}
		} else if isInterface(U) {
			if msets.MethodSet(U).Len() == 0 {
				continue // empty interface
			}

			// T concrete, U interface
			if types.AssignableTo(T, U) {
				from = append(from, U)
			} else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
				fromPtr = append(fromPtr, U)
			}
		}
	}

	var pos interface{} = qpos
	if nt, ok := deref(T).(*types.Named); ok {
		pos = nt.Obj()
	}

	// Sort types (arbitrarily) to ensure test determinism.
	sort.Sort(typesByString(to))
	sort.Sort(typesByString(from))
	sort.Sort(typesByString(fromPtr))

	return &implementsResult{T, pos, to, from, fromPtr}, nil
}
Example #8
0
// NewTransformer returns a transformer based on the specified template,
// a single-file package containing "before" and "after" functions as
// described in the package documentation.
// tmplInfo is the type information for tmplFile.
//
func NewTransformer(fset *token.FileSet, tmplPkg *types.Package, tmplFile *ast.File, tmplInfo *types.Info, verbose bool) (*Transformer, error) {
	// Check the template.
	beforeSig := funcSig(tmplPkg, "before")
	if beforeSig == nil {
		return nil, fmt.Errorf("no 'before' func found in template")
	}
	afterSig := funcSig(tmplPkg, "after")
	if afterSig == nil {
		return nil, fmt.Errorf("no 'after' func found in template")
	}

	// TODO(adonovan): should we also check the names of the params match?
	if !types.Identical(afterSig, beforeSig) {
		return nil, fmt.Errorf("before %s and after %s functions have different signatures",
			beforeSig, afterSig)
	}

	for _, imp := range tmplFile.Imports {
		if imp.Name != nil && imp.Name.Name == "." {
			// Dot imports are currently forbidden.  We
			// make the simplifying assumption that all
			// imports are regular, without local renames.
			// TODO(adonovan): document
			return nil, fmt.Errorf("dot-import (of %s) in template", imp.Path.Value)
		}
	}
	var beforeDecl, afterDecl *ast.FuncDecl
	for _, decl := range tmplFile.Decls {
		if decl, ok := decl.(*ast.FuncDecl); ok {
			switch decl.Name.Name {
			case "before":
				beforeDecl = decl
			case "after":
				afterDecl = decl
			}
		}
	}

	before, err := soleExpr(beforeDecl)
	if err != nil {
		return nil, fmt.Errorf("before: %s", err)
	}
	after, err := soleExpr(afterDecl)
	if err != nil {
		return nil, fmt.Errorf("after: %s", err)
	}

	wildcards := make(map[*types.Var]bool)
	for i := 0; i < beforeSig.Params().Len(); i++ {
		wildcards[beforeSig.Params().At(i)] = true
	}

	// checkExprTypes returns an error if Tb (type of before()) is not
	// safe to replace with Ta (type of after()).
	//
	// Only superficial checks are performed, and they may result in both
	// false positives and negatives.
	//
	// Ideally, we would only require that the replacement be assignable
	// to the context of a specific pattern occurrence, but the type
	// checker doesn't record that information and it's complex to deduce.
	// A Go type cannot capture all the constraints of a given expression
	// context, which may include the size, constness, signedness,
	// namedness or constructor of its type, and even the specific value
	// of the replacement.  (Consider the rule that array literal keys
	// must be unique.)  So we cannot hope to prove the safety of a
	// transformation in general.
	Tb := tmplInfo.TypeOf(before)
	Ta := tmplInfo.TypeOf(after)
	if types.AssignableTo(Tb, Ta) {
		// safe: replacement is assignable to pattern.
	} else if tuple, ok := Tb.(*types.Tuple); ok && tuple.Len() == 0 {
		// safe: pattern has void type (must appear in an ExprStmt).
	} else {
		return nil, fmt.Errorf("%s is not a safe replacement for %s", Ta, Tb)
	}

	tr := &Transformer{
		fset:           fset,
		verbose:        verbose,
		wildcards:      wildcards,
		allowWildcards: true,
		seenInfos:      make(map[*types.Info]bool),
		importedObjs:   make(map[types.Object]*ast.SelectorExpr),
		before:         before,
		after:          after,
	}

	// Combine type info from the template and input packages, and
	// type info for the synthesized ASTs too.  This saves us
	// having to book-keep where each ast.Node originated as we
	// construct the resulting hybrid AST.
	tr.info = &types.Info{
		Types:      make(map[ast.Expr]types.TypeAndValue),
		Defs:       make(map[*ast.Ident]types.Object),
		Uses:       make(map[*ast.Ident]types.Object),
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
	}
	mergeTypeInfo(tr.info, tmplInfo)

	// Compute set of imported objects required by after().
	// TODO(adonovan): reject dot-imports in pattern
	ast.Inspect(after, func(n ast.Node) bool {
		if n, ok := n.(*ast.SelectorExpr); ok {
			if _, ok := tr.info.Selections[n]; !ok {
				// qualified ident
				obj := tr.info.Uses[n.Sel]
				tr.importedObjs[obj] = n
				return false // prune
			}
		}
		return true // recur
	})

	return tr, nil
}
Example #9
0
func (l langType) EmitTypeInfo() string {

	l.BuildTypeHaxe() // generate the code to emulate compiler reflect data output

	var ret string
	ret += "\nclass TypeInfo{\n\n"

	ret += fmt.Sprintf("public static var nextTypeID=%d;\n", l.PogoComp().NextTypeID) // must be last as will change during processing

	// TODO review if this is required
	ret += "public static function isHaxeClass(id:Int):Bool {\nswitch(id){" + "\n"
	for k := range l.hc.pteKeys {
		v := l.hc.pte.At(l.hc.pteKeys[k])
		goType := l.hc.pteKeys[k].String()
		//fmt.Println("DEBUG full goType", goType)
		haxeClass := getHaxeClass(goType)
		if haxeClass != "" {
			ret += "case " + fmt.Sprintf("%d", v) + `: return true; // ` + goType + "\n"
		}
	}
	ret += `default: return false;}}` + "\n"

	ret += "public static function getName(id:Int):String {\n"
	ret += "\tif(id<0||id>=nextTypeID)return \"reflect.CREATED\"+Std.string(id);\n"
	ret += "\tif(id==0)return \"(haxeTypeID=0)\";" + "\n"
	ret += "\t#if (js || php || node) if(id==null)return \"(haxeTypeID=null)\"; #end\n"
	ret += "\t" + `return Go_haxegoruntime_getTTypeSString.callFromRT(0,id);` + "\n}\n"
	ret += "public static function typeString(i:Interface):String {\nreturn getName(i.typ);\n}\n"
	/*
		ret += "static var typIDs:Map<String,Int> = ["
		deDup := make(map[string]bool)
		for k := range pteKeys {
			v := pte.At(pteKeys[k])
			nam := haxeStringConst("`"+preprocessTypeName(pteKeys[k].String())+"`", "CompilerInternal:haxe.EmitTypeInfo()")
			if len(nam) != 0 {
				if deDup[nam] { // have one already!!
					nam = fmt.Sprintf("%s (duplicate type name! this id=%d)\"", nam[:len(nam)-1], v)
				} else {
					deDup[nam] = true
				}
				ret += ` ` + nam + ` => ` + fmt.Sprintf("%d", v) + `,` + "\n"
			}
		}
		ret += "];\n"
	*/
	ret += "public static function getId(name:String):Int {\n"
	ret += "\tvar t:Int;\n"
	//ret += "\ttry { t=typIDs[name];\n"
	//ret += "\t} catch(x:Dynamic) { Scheduler.panicFromHaxe(\"TraceInfo.getId() not found:\"+name+x); t=-1; } ;\n"
	ret += "\t" + `t = Go_haxegoruntime_getTTypeIIDD.callFromRT(0,name);` + "\n"
	ret += "\treturn t;\n}\n"

	//function to answer the question is the type a concrete value?
	ret += "public static function isConcrete(t:Int):Bool {\nswitch(t){" + "\n"
	for T := range l.hc.pteKeys {
		t := l.hc.pte.At(l.hc.pteKeys[T])
		switch l.hc.pteKeys[T].Underlying().(type) {
		case *types.Interface:
			ret += `case ` + fmt.Sprintf("%d", t) + `: return false;` + "\n"
		}
	}
	ret += "default: return true;}}\n"

	//emulation of: func IsIdentical(x, y Type) bool
	ret += "public static function isIdentical(v:Int,t:Int):Bool {\nif(v==t) return true;\nswitch(v){" + "\n"
	for V := range l.hc.pteKeys {
		v := l.hc.pte.At(l.hc.pteKeys[V])
		ret0 := ""
		for T := range l.hc.pteKeys {
			t := l.hc.pte.At(l.hc.pteKeys[T])
			if v != t && types.Identical(l.hc.pteKeys[V], l.hc.pteKeys[T]) {
				ret0 += `case ` + fmt.Sprintf("%d", t) + `: return true;` + "\n"
			}
		}
		if ret0 != "" {
			ret += `case ` + fmt.Sprintf("%d", v) + `: switch(t){` + "\n"
			ret += ret0
			ret += "default: return false;}\n"
		}
	}
	ret += "default: return false;}}\n"

	ret += "}\n"

	l.PogoComp().WriteAsClass("TypeInfo", ret)

	ret = "class TypeAssign {"

	//emulation of: func IsAssignableTo(V, T Type) bool
	ret += "public static function isAssignableTo(v:Int,t:Int):Bool {\n\tif(v==t) return true;\n"
	ret += "\tfor(ae in isAsssignableToArray) if(ae==(v<<16|t)) return true;\n"
	ret += "\treturn false;\n}\n"

	ret += "static var isAsssignableToArray:Array<Int> = ["
	for V := range l.hc.pteKeys {
		v := l.hc.pte.At(l.hc.pteKeys[V])
		for T := range l.hc.pteKeys {
			t := l.hc.pte.At(l.hc.pteKeys[T])
			if v != t && types.AssignableTo(l.hc.pteKeys[V], l.hc.pteKeys[T]) {
				ret += fmt.Sprintf("%d,", v.(int)<<16|t.(int))
			}
		}
		ret += "\n"
	}
	ret += "];\n"

	ret += "}\n"

	l.PogoComp().WriteAsClass("TypeAssign", ret)
	ret = "class TypeZero {"

	// function to give the zero value for each type
	ret += "public static function zeroValue(t:Int):Dynamic {\nswitch(t){" + "\n"
	for T := range l.hc.pteKeys {
		t := l.hc.pte.At(l.hc.pteKeys[T])
		z := l.LangType(l.hc.pteKeys[T], true, "EmitTypeInfo()")
		if z == "" {
			z = "null"
		}
		if z != "null" {
			ret += `case ` + fmt.Sprintf("%d", t) + `: return `
			ret += z + ";\n"
		}
	}
	ret += "default: return null;}}\n"

	ret += "}\n"

	l.PogoComp().WriteAsClass("TypeZero", ret)

	return ""
}
Example #10
0
// Implements displays the "implements" relation as it pertains to the
// selected type.  If the selection is a method, 'implements' displays
// the corresponding methods of the types that would have been reported
// by an implements query on the receiver type.
//
func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
	// Find the selected type.
	path, action := findInterestingNode(qpos.info, qpos.path)

	var method *types.Func
	var T types.Type // selected type (receiver if method != nil)

	switch action {
	case actionExpr:
		// method?
		if id, ok := path[0].(*ast.Ident); ok {
			if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
				recv := obj.Type().(*types.Signature).Recv()
				if recv == nil {
					return nil, fmt.Errorf("this function is not a method")
				}
				method = obj
				T = recv.Type()
			}
		}
	case actionType:
		T = qpos.info.TypeOf(path[0].(ast.Expr))
	}
	if T == nil {
		return nil, fmt.Errorf("no type or method here")
	}

	// Find all named types, even local types (which can have
	// methods via promotion) and the built-in "error".
	//
	// TODO(adonovan): include all packages in PTA scope too?
	// i.e. don't reduceScope?
	//
	var allNamed []types.Type
	for _, info := range o.typeInfo {
		for _, obj := range info.Defs {
			if obj, ok := obj.(*types.TypeName); ok {
				allNamed = append(allNamed, obj.Type())
			}
		}
	}
	allNamed = append(allNamed, types.Universe.Lookup("error").Type())

	var msets types.MethodSetCache

	// Test each named type.
	var to, from, fromPtr []types.Type
	for _, U := range allNamed {
		if isInterface(T) {
			if msets.MethodSet(T).Len() == 0 {
				continue // empty interface
			}
			if isInterface(U) {
				if msets.MethodSet(U).Len() == 0 {
					continue // empty interface
				}

				// T interface, U interface
				if !types.Identical(T, U) {
					if types.AssignableTo(U, T) {
						to = append(to, U)
					}
					if types.AssignableTo(T, U) {
						from = append(from, U)
					}
				}
			} else {
				// T interface, U concrete
				if types.AssignableTo(U, T) {
					to = append(to, U)
				} else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
					to = append(to, pU)
				}
			}
		} else if isInterface(U) {
			if msets.MethodSet(U).Len() == 0 {
				continue // empty interface
			}

			// T concrete, U interface
			if types.AssignableTo(T, U) {
				from = append(from, U)
			} else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
				fromPtr = append(fromPtr, U)
			}
		}
	}

	var pos interface{} = qpos
	if nt, ok := deref(T).(*types.Named); ok {
		pos = nt.Obj()
	}

	// Sort types (arbitrarily) to ensure test determinism.
	sort.Sort(typesByString(to))
	sort.Sort(typesByString(from))
	sort.Sort(typesByString(fromPtr))

	var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
	if method != nil {
		for _, t := range to {
			toMethod = append(toMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
		for _, t := range from {
			fromMethod = append(fromMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
		for _, t := range fromPtr {
			fromPtrMethod = append(fromPtrMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
	}

	return &implementsResult{qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod}, nil
}
Example #11
0
// Implements displays the "implements" relation as it pertains to the
// selected type.
// If the selection is a method, 'implements' displays
// the corresponding methods of the types that would have been reported
// by an implements query on the receiver type.
//
func implements(q *Query) error {
	lconf := loader.Config{Build: q.Build}
	allowErrors(&lconf)

	qpkg, err := importQueryPackage(q.Pos, &lconf)
	if err != nil {
		return err
	}

	// Set the packages to search.
	if len(q.Scope) > 0 {
		// Inspect all packages in the analysis scope, if specified.
		if err := setPTAScope(&lconf, q.Scope); err != nil {
			return err
		}
	} else {
		// Otherwise inspect the forward and reverse
		// transitive closure of the selected package.
		// (In theory even this is incomplete.)
		_, rev, _ := importgraph.Build(q.Build)
		for path := range rev.Search(qpkg) {
			lconf.ImportWithTests(path)
		}

		// TODO(adonovan): for completeness, we should also
		// type-check and inspect function bodies in all
		// imported packages.  This would be expensive, but we
		// could optimize by skipping functions that do not
		// contain type declarations.  This would require
		// changing the loader's TypeCheckFuncBodies hook to
		// provide the []*ast.File.
	}

	// Load/parse/type-check the program.
	lprog, err := lconf.Load()
	if err != nil {
		return err
	}
	q.Fset = lprog.Fset

	qpos, err := parseQueryPos(lprog, q.Pos, false)
	if err != nil {
		return err
	}

	// Find the selected type.
	path, action := findInterestingNode(qpos.info, qpos.path)

	var method *types.Func
	var T types.Type // selected type (receiver if method != nil)

	switch action {
	case actionExpr:
		// method?
		if id, ok := path[0].(*ast.Ident); ok {
			if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
				recv := obj.Type().(*types.Signature).Recv()
				if recv == nil {
					return fmt.Errorf("this function is not a method")
				}
				method = obj
				T = recv.Type()
			}
		}
	case actionType:
		T = qpos.info.TypeOf(path[0].(ast.Expr))
	}
	if T == nil {
		return fmt.Errorf("no type or method here")
	}

	// Find all named types, even local types (which can have
	// methods via promotion) and the built-in "error".
	var allNamed []types.Type
	for _, info := range lprog.AllPackages {
		for _, obj := range info.Defs {
			if obj, ok := obj.(*types.TypeName); ok {
				allNamed = append(allNamed, obj.Type())
			}
		}
	}
	allNamed = append(allNamed, types.Universe.Lookup("error").Type())

	var msets typeutil.MethodSetCache

	// Test each named type.
	var to, from, fromPtr []types.Type
	for _, U := range allNamed {
		if isInterface(T) {
			if msets.MethodSet(T).Len() == 0 {
				continue // empty interface
			}
			if isInterface(U) {
				if msets.MethodSet(U).Len() == 0 {
					continue // empty interface
				}

				// T interface, U interface
				if !types.Identical(T, U) {
					if types.AssignableTo(U, T) {
						to = append(to, U)
					}
					if types.AssignableTo(T, U) {
						from = append(from, U)
					}
				}
			} else {
				// T interface, U concrete
				if types.AssignableTo(U, T) {
					to = append(to, U)
				} else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
					to = append(to, pU)
				}
			}
		} else if isInterface(U) {
			if msets.MethodSet(U).Len() == 0 {
				continue // empty interface
			}

			// T concrete, U interface
			if types.AssignableTo(T, U) {
				from = append(from, U)
			} else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
				fromPtr = append(fromPtr, U)
			}
		}
	}

	var pos interface{} = qpos
	if nt, ok := deref(T).(*types.Named); ok {
		pos = nt.Obj()
	}

	// Sort types (arbitrarily) to ensure test determinism.
	sort.Sort(typesByString(to))
	sort.Sort(typesByString(from))
	sort.Sort(typesByString(fromPtr))

	var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
	if method != nil {
		for _, t := range to {
			toMethod = append(toMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
		for _, t := range from {
			fromMethod = append(fromMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
		for _, t := range fromPtr {
			fromPtrMethod = append(fromPtrMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
	}

	q.result = &implementsResult{
		qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod,
	}
	return nil
}
Example #12
0
func (l langType) EmitTypeInfo() string {

	BuildTypeHaxe() // generate the code to emulate compiler reflect data output

	var ret string
	ret += "\nclass TypeInfo{\n\n"

	ret += fmt.Sprintf("public static var nextTypeID=%d;\n", pogo.NextTypeID) // must be last as will change during processing

	// TODO review if this is required
	ret += "public static function isHaxeClass(id:Int):Bool {\nswitch(id){" + "\n"
	for k := range pteKeys {
		v := pte.At(pteKeys[k])
		goType := pteKeys[k].String()
		//fmt.Println("DEBUG full goType", goType)
		haxeClass := getHaxeClass(goType)
		if haxeClass != "" {
			ret += "case " + fmt.Sprintf("%d", v) + `: return true; // ` + goType + "\n"
		}
	}
	ret += `default: return false;}}` + "\n"

	ret += "public static function getName(id:Int):String {\n"
	ret += "\tif(id<0||id>=nextTypeID)return \"reflect.CREATED\"+Std.string(id);\n"
	ret += "\tif(id==0)return \"(haxeTypeID=0)\";" + "\n"
	ret += "\t#if (js || php || node) if(id==null)return \"(haxeTypeID=null)\"; #end\n"
	ret += "\t" + `return Go_haxegoruntime_getTTypeSString.callFromRT(0,id);` + "\n}\n"
	ret += "public static function typeString(i:Interface):String {\nreturn getName(i.typ);\n}\n"
	/*
		ret += "static var typIDs:Map<String,Int> = ["
		deDup := make(map[string]bool)
		for k := range pteKeys {
			v := pte.At(pteKeys[k])
			nam := haxeStringConst("`"+preprocessTypeName(pteKeys[k].String())+"`", "CompilerInternal:haxe.EmitTypeInfo()")
			if len(nam) != 0 {
				if deDup[nam] { // have one already!!
					nam = fmt.Sprintf("%s (duplicate type name! this id=%d)\"", nam[:len(nam)-1], v)
				} else {
					deDup[nam] = true
				}
				ret += ` ` + nam + ` => ` + fmt.Sprintf("%d", v) + `,` + "\n"
			}
		}
		ret += "];\n"
	*/
	ret += "public static function getId(name:String):Int {\n"
	ret += "\tvar t:Int;\n"
	//ret += "\ttry { t=typIDs[name];\n"
	//ret += "\t} catch(x:Dynamic) { Scheduler.panicFromHaxe(\"TraceInfo.getId() not found:\"+name+x); t=-1; } ;\n"
	ret += "\t" + `t = Go_haxegoruntime_getTTypeIIDD.callFromRT(0,name);` + "\n"
	ret += "\treturn t;\n}\n"

	//function to answer the question is the type a concrete value?
	ret += "public static function isConcrete(t:Int):Bool {\nswitch(t){" + "\n"
	for T := range pteKeys {
		t := pte.At(pteKeys[T])
		switch pteKeys[T].Underlying().(type) {
		case *types.Interface:
			ret += `case ` + fmt.Sprintf("%d", t) + `: return false;` + "\n"
		}
	}
	ret += "default: return true;}}\n"

	//emulation of: func IsIdentical(x, y Type) bool
	ret += "public static function isIdentical(v:Int,t:Int):Bool {\nif(v==t) return true;\nswitch(v){" + "\n"
	for V := range pteKeys {
		v := pte.At(pteKeys[V])
		ret0 := ""
		for T := range pteKeys {
			t := pte.At(pteKeys[T])
			if v != t && types.Identical(pteKeys[V], pteKeys[T]) {
				ret0 += `case ` + fmt.Sprintf("%d", t) + `: return true;` + "\n"
			}
		}
		if ret0 != "" {
			ret += `case ` + fmt.Sprintf("%d", v) + `: switch(t){` + "\n"
			ret += ret0
			ret += "default: return false;}\n"
		}
	}
	ret += "default: return false;}}\n"

	ret += "}\n"

	pogo.WriteAsClass("TypeInfo", ret)

	ret = "class TypeAssign {"

	//emulation of: func IsAssignableTo(V, T Type) bool
	ret += "public static function isAssignableTo(v:Int,t:Int):Bool {\n\tif(v==t) return true;\n"
	ret += "\tfor(ae in isAsssignableToArray) if(ae==(v<<16|t)) return true;\n"
	ret += "\treturn false;\n}\n"

	ret += "static var isAsssignableToArray:Array<Int> = ["
	for V := range pteKeys {
		v := pte.At(pteKeys[V])
		for T := range pteKeys {
			t := pte.At(pteKeys[T])
			if v != t && types.AssignableTo(pteKeys[V], pteKeys[T]) {
				ret += fmt.Sprintf("%d,", v.(int)<<16|t.(int))
			}
		}
		ret += "\n"
	}
	ret += "];\n"

	ret += "}\n"

	pogo.WriteAsClass("TypeAssign", ret)

	/*
		ret = "class TypeAssert {"

		//emulation of: func type.AsertableTo(V *Interface, T Type) bool
		ret += "public static function assertableTo(v:Int,t:Int):Bool {\n"
		//ret += "trace(\"DEBUG assertableTo()\",v,t);\n"
		ret += "\tif(v==t) return true;\n"
		ret += "\tfor(ae in isAssertableToArray) if(ae==(v<<16|t)) return true;\n"
		ret += "return false;\n}\n"
		ret += "static var isAssertableToArray:Array<Int> = [ "
		for tid, typ := range typesByID {
			ret0 := ""
			if typ != nil {
				for iid, ityp := range typesByID {
					if ityp != nil {
						iface, isIface := ityp.Underlying().(*types.Interface)
						if isIface {
							if tid != iid && types.AssertableTo(iface, typ) {
								ret0 += fmt.Sprintf("0x%08X,", (tid<<16)|iid)
							}
						}
					}
				}
			}
			if ret0 != "" {
				ret += ret0
				ret += "\n"
			}
		}
		ret += "];\n"

		ret += "}\n"

		pogo.WriteAsClass("TypeAssert", ret)
	*/

	ret = "class TypeZero {"

	// function to give the zero value for each type
	ret += "public static function zeroValue(t:Int):Dynamic {\nswitch(t){" + "\n"
	for T := range pteKeys {
		t := pte.At(pteKeys[T])
		z := l.LangType(pteKeys[T], true, "EmitTypeInfo()")
		if z == "" {
			z = "null"
		}
		if z != "null" {
			ret += `case ` + fmt.Sprintf("%d", t) + `: return `
			ret += z + ";\n"
		}
	}
	ret += "default: return null;}}\n"

	ret += "}\n"

	pogo.WriteAsClass("TypeZero", ret)
	/*
		ret = "class MethodTypeInfo {"

		ret += "public static function method(t:Int,m:String):Dynamic {\nswitch(t){" + "\n"

		tta := pogo.TypesWithMethodSets() //[]types.Type
		sort.Sort(pogo.TypeSorter(tta))
		for T := range tta {
			t := pte.At(tta[T])
			if t != nil { // it is used?
				ret += `case ` + fmt.Sprintf("%d", t) + `: switch(m){` + "\n"
				ms := types.NewMethodSet(tta[T])
				msNames := []string{}
				for m := 0; m < ms.Len(); m++ {
					msNames = append(msNames, ms.At(m).String())
				}
				sort.Strings(msNames)
				deDup := make(map[string][]int) // TODO check this logic, required for non-public methods
				for pass := 1; pass <= 2; pass++ {
					for _, msString := range msNames {
						for m := 0; m < ms.Len(); m++ {
							if ms.At(m).String() == msString { // ensure we do this in a repeatable order
								funcObj, ok := ms.At(m).Obj().(*types.Func)
								pkgName := "unknown"
								if ok && funcObj.Pkg() != nil && ms.At(m).Recv() == tta[T] {
									line := ""
									ss := strings.Split(funcObj.Pkg().Name(), "/")
									pkgName = ss[len(ss)-1]
									if strings.HasPrefix(pkgName, "_") { // exclude functions in haxe for now
										// TODO NoOp for now... so haxe types cant be "Involked" when held in interface types
										// *** need to deal with getters and setters
										// *** also with calling parameters which are different for a Haxe API
									} else {
										switch pass {
										case 1:
											idx, exists := deDup[funcObj.Name()]
											if exists {
												if len(idx) > len(ms.At(m).Index()) {
													deDup[funcObj.Name()] = ms.At(m).Index()
												}
											} else {
												deDup[funcObj.Name()] = ms.At(m).Index()
											}
										case 2:
											idx, _ := deDup[funcObj.Name()]
											if len(idx) != len(ms.At(m).Index()) {
												line += "// Duplicate unused: "
											}
											line += `case "` + funcObj.Name() + `": return `
											fnToCall := l.LangName(
												ms.At(m).Obj().Pkg().Name()+":"+ms.At(m).Recv().String(),
												funcObj.Name())
											line += `Go_` + fnToCall + `.call` + "; "
										}
									}
									ret += line
								}
								if pass == 2 {
									ret += fmt.Sprintf("// %v %v %v %v\n",
										ms.At(m).Obj().Name(),
										ms.At(m).Kind(),
										ms.At(m).Index(),
										ms.At(m).Indirect())
								}
							}
						}
					}
				}
				ret += "default:}\n"
			}
		}

		// TODO look for overloaded types at this point

		ret += "default:}\n Scheduler.panicFromHaxe( " + `"no method found!"` + "); return null;}\n" // TODO improve error

		pogo.WriteAsClass("MethodTypeInfo", ret+"}\n")
	*/
	return ""
}