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)
	}
}
Example #2
0
func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) {
	for ifaceObj := range delta {
		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 #3
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 #4
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 #5
0
func WriteProgramCode(pkgs []*Archive, importContext *ImportContext, w *SourceMapFilter) {
	mainPkg := pkgs[len(pkgs)-1]
	minify := mainPkg.Minified

	declsByObject := make(map[string][]*Decl)
	var pendingDecls []*Decl
	for _, pkg := range pkgs {
		for i := range pkg.Declarations {
			d := &pkg.Declarations[i]
			if len(d.DceFilters) == 0 {
				pendingDecls = append(pendingDecls, d)
				continue
			}
			for _, f := range d.DceFilters {
				o := string(pkg.ImportPath) + ":" + string(f)
				declsByObject[o] = append(declsByObject[o], d)
			}
		}
	}

	for len(pendingDecls) != 0 {
		d := pendingDecls[len(pendingDecls)-1]
		pendingDecls = pendingDecls[:len(pendingDecls)-1]
		for _, dep := range d.DceDeps {
			o := string(dep)
			if decls, ok := declsByObject[o]; ok {
				delete(declsByObject, o)
				name := strings.Split(o, ":")[1]
				for _, d := range decls {
					for i, f := range d.DceFilters {
						if string(f) == name {
							d.DceFilters[i] = d.DceFilters[len(d.DceFilters)-1]
							d.DceFilters = d.DceFilters[:len(d.DceFilters)-1]
							break
						}
					}
					if len(d.DceFilters) == 0 {
						pendingDecls = append(pendingDecls, d)
					}
				}
			}
		}
	}

	w.Write([]byte("\"use strict\";\n(function() {\n\n"))
	w.Write(removeWhitespace([]byte(strings.TrimSpace(prelude)), minify))
	w.Write([]byte("\n"))

	// write packages
	for _, pkg := range pkgs {
		WritePkgCode(pkg, minify, w)
	}

	// write interfaces
	allTypeNames := []*types.TypeName{types.New("error").(*types.Named).Obj()}
	for _, pkg := range pkgs {
		scope := importContext.Packages[string(pkg.ImportPath)].Scope()
		for _, name := range scope.Names() {
			if typeName, isTypeName := scope.Lookup(name).(*types.TypeName); isTypeName {
				if _, notUsed := declsByObject[string(pkg.ImportPath)+":"+name]; !notUsed {
					allTypeNames = append(allTypeNames, typeName)
				}
			}
		}
	}
	for _, t := range allTypeNames {
		if in, isInterface := t.Type().Underlying().(*types.Interface); isInterface {
			if in.Empty() {
				continue
			}
			implementedBy := make(map[string]bool, 0)
			for _, other := range allTypeNames {
				otherType := other.Type()
				switch otherType.Underlying().(type) {
				case *types.Interface:
					// skip
				case *types.Struct:
					if types.AssignableTo(otherType, in) {
						implementedBy[fmt.Sprintf("$packages[\"%s\"].%s", other.Pkg().Path(), other.Name())] = true
					}
					if types.AssignableTo(types.NewPointer(otherType), in) {
						implementedBy[fmt.Sprintf("$packages[\"%s\"].%s.Ptr", other.Pkg().Path(), other.Name())] = true
					}
				default:
					if types.AssignableTo(otherType, in) {
						implementedBy[fmt.Sprintf("$packages[\"%s\"].%s", other.Pkg().Path(), other.Name())] = true
					}
					if types.AssignableTo(types.NewPointer(otherType), in) {
						implementedBy[fmt.Sprintf("$ptrType($packages[\"%s\"].%s)", other.Pkg().Path(), other.Name())] = true
					}
				}
			}
			list := make([]string, 0, len(implementedBy))
			for ref := range implementedBy {
				list = append(list, ref)
			}
			sort.Strings(list)
			var target string
			switch t.Name() {
			case "error":
				target = "$error"
			default:
				target = fmt.Sprintf("$packages[\"%s\"].%s", t.Pkg().Path(), t.Name())
			}
			w.Write(removeWhitespace([]byte(fmt.Sprintf("%s.implementedBy = [%s];\n", target, strings.Join(list, ", "))), minify))
		}
	}

	for _, pkg := range pkgs {
		w.Write([]byte("$packages[\"" + string(pkg.ImportPath) + "\"].init();\n"))
	}

	w.Write([]byte("$packages[\"" + string(mainPkg.ImportPath) + "\"].main();\n\n})();\n"))
}
Example #6
0
// NewTransformer returns a transformer based on the specified template,
// a package containing "before" and "after" functions as described
// in the package documentation.
//
func NewTransformer(fset *token.FileSet, template *loader.PackageInfo, verbose bool) (*Transformer, error) {
	// Check the template.
	beforeSig := funcSig(template.Pkg, "before")
	if beforeSig == nil {
		return nil, fmt.Errorf("no 'before' func found in template")
	}
	afterSig := funcSig(template.Pkg, "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)
	}

	templateFile := template.Files[0]
	for _, imp := range templateFile.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 document
			return nil, fmt.Errorf("dot-import (of %s) in template", imp.Path.Value)
		}
	}
	var beforeDecl, afterDecl *ast.FuncDecl
	for _, decl := range templateFile.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 := template.TypeOf(before)
	Ta := template.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.
	//
	// TODO(adonovan): move type utility methods of PackageInfo to
	// types.Info, or at least into go/types.typeutil.
	tr.info.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.Info, &template.Info)

	// Compute set of imported objects required by after().
	// TODO 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 #7
0
func (l langType) EmitTypeInfo() string {
	ret := "class TypeInfo{\n"
	pte := pogo.TypesEncountered
	pteKeys := pogo.TypesEncountered.Keys()

	ret += "public static function getName(id:Int):String {\nswitch(id){" + "\n"
	for k := range pteKeys {
		v := pte.At(pteKeys[k])
		ret += "case " + fmt.Sprintf("%d", v) + `: return "` + pteKeys[k].String() + `";` + "\n"
	}
	ret += `default: return "UNKNOWN";}}` + "\n"

	ret += "public static function typeString(i:Interface):String {\nreturn getName(i.typ);\n}\n"

	ret += "public static function getId(name:String):Int {\nswitch(name){" + "\n"
	for k := range pteKeys {
		v := pte.At(pteKeys[k])
		ret += `case "` + pteKeys[k].String() + `": return ` + fmt.Sprintf("%d", v) + `;` + "\n"
	}
	ret += "default: return -1;}}\n"

	//emulation of: func IsAssignableTo(V, T Type) bool
	ret += "public static function isAssignableTo(v:Int,t:Int):Bool {\nif(v==t) return true;\nswitch(v){" + "\n"
	for V := range pteKeys {
		v := pte.At(pteKeys[V])
		ret += `case ` + fmt.Sprintf("%d", v) + `: switch(t){` + "\n"
		for T := range pteKeys {
			t := pte.At(pteKeys[T])
			if v != t && types.AssignableTo(pteKeys[V], pteKeys[T]) {
				ret += `case ` + fmt.Sprintf("%d", t) + `: return true;` + "\n"
			}
		}
		ret += "default: return false;}\n"
	}
	ret += "default: return false;}}\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])
		ret += `case ` + fmt.Sprintf("%d", v) + `: switch(t){` + "\n"
		for T := range pteKeys {
			t := pte.At(pteKeys[T])
			if v != t && types.Identical(pteKeys[V], pteKeys[T]) {
				ret += `case ` + fmt.Sprintf("%d", t) + `: return true;` + "\n"
			}
		}
		ret += "default: return false;}\n"
	}
	ret += "default: return false;}}\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"
		default:
			ret += `case ` + fmt.Sprintf("%d", t) + `: return true;` + "\n"
		}
	}
	ret += "default: return false;}}\n"

	// 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])
		ret += `case ` + fmt.Sprintf("%d", t) + `: return `
		ret += l.LangType(pteKeys[T], true, "EmitTypeInfo()") + ";\n"
	}
	ret += "default: return null;}}\n"

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

	tta := pogo.TypesWithMethodSets() //[]types.Type

	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])
			for m := 0; m < ms.Len(); m++ {
				funcObj, ok := ms.At(m).Obj().(*types.Func)
				pkgName := "unknown"
				if ok && funcObj.Pkg() != nil {
					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 {
						line = `case "` + funcObj.Name() + `": return `
						fnToCall := l.LangName(ms.At(m).Recv().String(),
							funcObj.Name())
						ovPkg, _, isOv := l.PackageOverloaded(funcObj.Pkg().Name())
						if isOv {
							fnToCall = strings.Replace(fnToCall, "_"+funcObj.Pkg().Name()+"_", "_"+ovPkg+"_", -1) // NOTE this is not a fool-proof method
						}
						line += `Go_` + fnToCall + `.call` + "; "
					}
					ret += line
				}
				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"
		}
	}
	ret += "default:}\n Scheduler.panicFromHaxe( " + `"no method found!"` + "); return null;}\n" // TODO improve error

	return ret + "}"
}
Example #8
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
}