// 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 }
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) } } } }
// 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) } }
// 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 }
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 }
// 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 }
// 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 }
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 "" }
// 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 }
// 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 }
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 "" }