// 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 main() { // Parse one file. fset := token.NewFileSet() f, err := parser.ParseFile(fset, "input.go", input, 0) if err != nil { log.Fatal(err) // parse error } conf := types.Config{Importer: importer.Default()} pkg, err := conf.Check("hello", fset, []*ast.File{f}, nil) if err != nil { log.Fatal(err) // type error } //!+implements // Find all named types at package level. var allNamed []*types.Named for _, name := range pkg.Scope().Names() { if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok { allNamed = append(allNamed, obj.Type().(*types.Named)) } } // Test assignability of all distinct pairs of // named types (T, U) where U is an interface. for _, T := range allNamed { for _, U := range allNamed { if T == U || !types.IsInterface(U) { continue } if types.AssignableTo(T, U) { fmt.Printf("%s satisfies %s\n", T, U) } else if !types.IsInterface(T) && types.AssignableTo(types.NewPointer(T), U) { fmt.Printf("%s satisfies %s\n", types.NewPointer(T), U) } } } //!-implements }
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 (g *javaGen) genInterface(iface interfaceInfo) { var exts []string numM := iface.t.NumMethods() for _, other := range g.allIntf { // Only extend interfaces with fewer methods to avoid circular references if other.t.NumMethods() < numM && types.AssignableTo(iface.t, other.t) { n := other.obj.Name() if p := other.obj.Pkg(); p != g.pkg { n = fmt.Sprintf("%s.%s.%s", g.javaPkgName(p), className(p), n) } exts = append(exts, n) } } g.Printf("public interface %s", iface.obj.Name()) if len(exts) > 0 { g.Printf(" extends %s", strings.Join(exts, ", ")) } g.Printf(" {\n") g.Indent() for _, m := range iface.summary.callable { if !g.isSigSupported(m.Type()) { g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", iface.obj.Name(), m.Name()) continue } g.genFuncSignature(m, false, true) } g.Outdent() g.Printf("}\n") g.Printf("\n") g.Printf(javaProxyPreamble, iface.obj.Name()) g.Indent() for _, m := range iface.summary.callable { if !g.isSigSupported(m.Type()) { g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", iface.obj.Name(), m.Name()) continue } g.genFuncSignature(m, false, false) } g.Outdent() g.Printf("}\n\n") }
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 }
// 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 }
// 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 main() { RPCDir := findRPCDir() fset, pkg, info, clientAst := parseFiles(RPCDir) serverMethods := getMethods(pkg, "RPCServer") _ = serverMethods errcount := 0 for _, decl := range clientAst.Decls { fndecl := publicMethodOf(decl, "RPCClient") if fndecl == nil { continue } if fndecl.Name.Name == "Continue" { // complex function, skip check continue } callx := findCallCall(fndecl) if callx == nil { log.Printf("%s: could not find RPC call", fset.Position(fndecl.Pos())) errcount++ continue } if len(callx.Args) != 3 { log.Printf("%s: wrong number of arguments for RPC call", fset.Position(callx.Pos())) errcount++ continue } arg0, arg0islit := callx.Args[0].(*ast.BasicLit) arg1 := callx.Args[1] arg2 := callx.Args[2] if !arg0islit || arg0.Kind != token.STRING { continue } name, _ := strconv.Unquote(arg0.Value) serverMethod := serverMethods[name] if serverMethod == nil { log.Printf("%s: could not find RPC method %q", fset.Position(callx.Pos()), name) errcount++ continue } params := serverMethod.Type().(*types.Signature).Params() if a, e := info.TypeOf(arg1), params.At(0).Type(); !types.AssignableTo(a, e) { log.Printf("%s: wrong type of first argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf)) errcount++ continue } if a, e := info.TypeOf(arg2), params.At(1).Type(); !types.AssignableTo(a, e) { log.Printf("%s: wrong type of second argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf)) errcount++ continue } if c**t, ok := arg1.(*ast.CompositeLit); ok { typ := params.At(0).Type() st := typ.Underlying().(*types.Struct) if len(c**t.Elts) != st.NumFields() && types.TypeString(typ, qf) != "DebuggerCommand" { log.Printf("%s: wrong number of fields in first argument's literal %d, expected %d", fset.Position(callx.Pos()), len(c**t.Elts), st.NumFields()) errcount++ continue } } } if errcount > 0 { log.Printf("%d errors", errcount) os.Exit(1) } }
// Is type src assignment compatible to type dst? // If so, return op code to use in conversion. // If not, return 0. func assignop(src *Type, dst *Type, why *string) NodeOp { if !types.AssignableTo(src.Type, dst.Type) { return 0 } if src == dst { return OCONVNOP } if src == nil || dst == nil { return 0 } // 1. src type is identical to dst. if Eqtype(src, dst) { return OCONVNOP } // 3. dst is an interface type and src implements dst. if dst.IsInterface() { dstInterface := dst.Type.(*types.Interface) if types.Implements(src.Type, dstInterface) { return OCONVIFACE } return 0 } if isptrto(dst, TINTER) { if why != nil { *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst) } return 0 } if src.IsChan() || dst.IsChan() { panic("channels not supported") } // 5. src is the predeclared identifier nil and dst is a nillable type. if src.Etype() == TNIL { switch dst.Etype() { case TARRAY: if dst.Bound() != -100 { // not slice break } fallthrough case TPTR32, TPTR64, TFUNC, TMAP, TCHAN, TINTER: return OCONVNOP } } // 6. rule about untyped constants - already converted by defaultlit. // 7. Any typed value can be assigned to the blank identifier. if dst.Etype() == TBLANK { return OCONVNOP } return 0 }
func (g *javaGen) genStruct(obj *types.TypeName, T *types.Struct) { fields := exportedFields(T) methods := exportedMethodSet(types.NewPointer(obj.Type())) var impls []string pT := types.NewPointer(obj.Type()) for _, iface := range g.allIntf { if types.AssignableTo(pT, iface.obj.Type()) { n := iface.obj.Name() if p := iface.obj.Pkg(); p != g.pkg { n = fmt.Sprintf("%s.%s.%s", g.javaPkgName(p), className(p), n) } impls = append(impls, n) } } g.Printf("public static final class %s extends Seq.Proxy", obj.Name()) if len(impls) > 0 { g.Printf(" implements %s", strings.Join(impls, ", ")) } g.Printf(" {\n") g.Indent() n := obj.Name() g.Printf("private %s(go.Seq.Ref ref) { super(ref); }\n\n", n) for _, f := range fields { if t := f.Type(); !g.isSupported(t) { g.Printf("// skipped field %s.%s with unsupported type: %T\n\n", n, f.Name(), t) continue } g.Printf("public final native %s get%s();\n", g.javaType(f.Type()), f.Name()) g.Printf("public final native void set%s(%s v);\n\n", f.Name(), g.javaType(f.Type())) } var isStringer bool for _, m := range methods { if !g.isSigSupported(m.Type()) { g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name()) continue } g.genFuncSignature(m, false, false) t := m.Type().(*types.Signature) isStringer = isStringer || (m.Name() == "String" && t.Params().Len() == 0 && t.Results().Len() == 1 && types.Identical(t.Results().At(0).Type(), types.Typ[types.String])) } g.Printf("@Override public boolean equals(Object o) {\n") g.Indent() g.Printf("if (o == null || !(o instanceof %s)) {\n return false;\n}\n", n) g.Printf("%s that = (%s)o;\n", n, n) for _, f := range fields { if t := f.Type(); !g.isSupported(t) { g.Printf("// skipped field %s.%s with unsupported type: %T\n\n", n, f.Name(), t) continue } nf := f.Name() g.Printf("%s this%s = get%s();\n", g.javaType(f.Type()), nf, nf) g.Printf("%s that%s = that.get%s();\n", g.javaType(f.Type()), nf, nf) if isJavaPrimitive(f.Type()) { g.Printf("if (this%s != that%s) {\n return false;\n}\n", nf, nf) } else { g.Printf("if (this%s == null) {\n", nf) g.Indent() g.Printf("if (that%s != null) {\n return false;\n}\n", nf) g.Outdent() g.Printf("} else if (!this%s.equals(that%s)) {\n return false;\n}\n", nf, nf) } } g.Printf("return true;\n") g.Outdent() g.Printf("}\n\n") g.Printf("@Override public int hashCode() {\n") g.Printf(" return java.util.Arrays.hashCode(new Object[] {") idx := 0 for _, f := range fields { if t := f.Type(); !g.isSupported(t) { continue } if idx > 0 { g.Printf(", ") } idx++ g.Printf("get%s()", f.Name()) } g.Printf("});\n") g.Printf("}\n\n") g.Printf("@Override public String toString() {\n") g.Indent() if isStringer { g.Printf("return String();\n") } else { g.Printf("StringBuilder b = new StringBuilder();\n") g.Printf(`b.append("%s").append("{");`, obj.Name()) g.Printf("\n") for _, f := range fields { if t := f.Type(); !g.isSupported(t) { continue } n := f.Name() g.Printf(`b.append("%s:").append(get%s()).append(",");`, n, n) g.Printf("\n") } g.Printf(`return b.append("}").toString();`) g.Printf("\n") } g.Outdent() g.Printf("}\n") g.Outdent() g.Printf("}\n\n") }
func (g *JavaGen) genStruct(s structInfo) { pkgPath := "" if g.Pkg != nil { pkgPath = g.Pkg.Path() } n := s.obj.Name() g.Printf(javaPreamble, g.javaPkgName(g.Pkg), n, g.gobindOpts(), pkgPath) fields := exportedFields(s.t) methods := exportedMethodSet(types.NewPointer(s.obj.Type())) var impls []string jinf := g.jstructs[s.obj] if jinf != nil { impls = append(impls, "Seq.GoObject") for _, cls := range jinf.supers { if cls.Interface { impls = append(impls, cls.Name) } } } else { impls = append(impls, "Seq.Proxy") } pT := types.NewPointer(s.obj.Type()) for _, iface := range g.allIntf { if types.AssignableTo(pT, iface.obj.Type()) { n := iface.obj.Name() if p := iface.obj.Pkg(); p != g.Pkg { n = fmt.Sprintf("%s.%s", g.javaPkgName(p), n) } impls = append(impls, n) } } g.Printf("public final class %s", n) if jinf != nil { if jinf.extends != nil { g.Printf(" extends %s", jinf.extends.Name) } } if len(impls) > 0 { g.Printf(" implements %s", strings.Join(impls, ", ")) } g.Printf(" {\n") g.Indent() g.Printf("static { %s.touch(); }\n\n", g.className()) g.genProxyImpl(n) cons := g.constructors[s.obj] for _, f := range cons { if !g.isSigSupported(f.Type()) { g.Printf("// skipped constructor %s.%s with unsupported parameter or return types\n\n", n, f.Name()) continue } g.genConstructor(f, n, jinf != nil) } if jinf == nil || jinf.genNoargCon { // constructor for Go instantiated instances. g.Printf("%s(Seq.Ref ref) { this.ref = ref; }\n\n", n) if len(cons) == 0 { // Generate default no-arg constructor g.Printf("public %s() { this.ref = __New(); }\n\n", n) g.Printf("private static native Seq.Ref __New();\n\n") } } for _, f := range fields { if t := f.Type(); !g.isSupported(t) { g.Printf("// skipped field %s.%s with unsupported type: %T\n\n", n, f.Name(), t) continue } g.Printf("public final native %s get%s();\n", g.javaType(f.Type()), f.Name()) g.Printf("public final native void set%s(%s v);\n\n", f.Name(), g.javaType(f.Type())) } var isStringer bool for _, m := range methods { if !g.isSigSupported(m.Type()) { g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", n, m.Name()) continue } var jm *java.Func hasThis := false if jinf != nil { jm = jinf.methods[m.Name()] if jm != nil { g.Printf("@Override ") } // Check the implicit this argument, if any sig := m.Type().(*types.Signature) params := sig.Params() if params.Len() > 0 { v := params.At(0) if v.Name() == "this" { t := v.Type() if isJavaType(t) { clsName := classNameFor(t) cls := g.clsMap[clsName] found := false for _, sup := range jinf.supers { if cls == sup { found = true break } } if !found { g.errorf("the type %s of the `this` argument to method %s.%s is not a super class to %s", cls.Name, n, m.Name(), n) continue } hasThis = true } } } } g.Printf("public native ") g.genFuncSignature(m, jm, hasThis) t := m.Type().(*types.Signature) isStringer = isStringer || (m.Name() == "String" && t.Params().Len() == 0 && t.Results().Len() == 1 && types.Identical(t.Results().At(0).Type(), types.Typ[types.String])) } if jinf == nil { g.genObjectMethods(n, fields, isStringer) } g.Outdent() g.Printf("}\n\n") }
func (g *javaGen) genStruct(obj *types.TypeName, T *types.Struct, intfs []*types.TypeName) { fields := exportedFields(T) methods := exportedMethodSet(types.NewPointer(obj.Type())) impls := []string{"go.Seq.Object"} pT := types.NewPointer(obj.Type()) for _, intf := range intfs { if types.AssignableTo(pT, intf.Type()) { impls = append(impls, intf.Name()) } } g.Printf("public static final class %s implements %s {\n", obj.Name(), strings.Join(impls, ", ")) g.Indent() g.Printf("private static final String DESCRIPTOR = \"go.%s.%s\";\n", g.pkg.Name(), obj.Name()) for i, f := range fields { g.Printf("private static final int FIELD_%s_GET = 0x%x0f;\n", f.Name(), i) g.Printf("private static final int FIELD_%s_SET = 0x%x1f;\n", f.Name(), i) } for i, m := range methods { g.Printf("private static final int CALL_%s = 0x%x0c;\n", m.Name(), i) } g.Printf("\n") g.Printf("private go.Seq.Ref ref;\n\n") n := obj.Name() g.Printf("private %s(go.Seq.Ref ref) { this.ref = ref; }\n\n", n) g.Printf(`public go.Seq.Ref ref() { return ref; } public void call(int code, go.Seq in, go.Seq out) { throw new RuntimeException("internal error: cycle: cannot call concrete proxy"); } `) for _, f := range fields { g.Printf("public %s get%s() {\n", g.javaType(f.Type()), f.Name()) g.Indent() g.Printf("Seq in = new Seq();\n") g.Printf("Seq out = new Seq();\n") g.Printf("in.writeRef(ref);\n") g.Printf("Seq.send(DESCRIPTOR, FIELD_%s_GET, in, out);\n", f.Name()) if seqType(f.Type()) == "Ref" { g.Printf("return new %s(out.read%s);\n", g.javaType(f.Type()), seqRead(f.Type())) } else { g.Printf("return out.read%s;\n", seqRead(f.Type())) } g.Outdent() g.Printf("}\n\n") g.Printf("public void set%s(%s v) {\n", f.Name(), g.javaType(f.Type())) g.Indent() g.Printf("Seq in = new Seq();\n") g.Printf("in.writeRef(ref);\n") g.Printf("in.write%s;\n", seqWrite(f.Type(), "v")) g.Printf("Seq.send(DESCRIPTOR, FIELD_%s_SET, in, null);\n", f.Name()) g.Outdent() g.Printf("}\n\n") } for _, m := range methods { g.genFunc(m, true) } g.Printf("@Override public boolean equals(Object o) {\n") g.Indent() g.Printf("if (o == null || !(o instanceof %s)) {\n return false;\n}\n", n) g.Printf("%s that = (%s)o;\n", n, n) for _, f := range fields { nf := f.Name() g.Printf("%s this%s = get%s();\n", g.javaType(f.Type()), nf, nf) g.Printf("%s that%s = that.get%s();\n", g.javaType(f.Type()), nf, nf) if isJavaPrimitive(f.Type()) { g.Printf("if (this%s != that%s) {\n return false;\n}\n", nf, nf) } else { g.Printf("if (this%s == null) {\n", nf) g.Indent() g.Printf("if (that%s != null) {\n return false;\n}\n", nf) g.Outdent() g.Printf("} else if (!this%s.equals(that%s)) {\n return false;\n}\n", nf, nf) } } g.Printf("return true;\n") g.Outdent() g.Printf("}\n\n") g.Printf("@Override public int hashCode() {\n") g.Printf(" return java.util.Arrays.hashCode(new Object[] {") for i, f := range fields { if i > 0 { g.Printf(", ") } g.Printf("get%s()", f.Name()) } g.Printf("});\n") g.Printf("}\n\n") // TODO(crawshaw): use String() string if it is defined. g.Printf("@Override public String toString() {\n") g.Indent() g.Printf("StringBuilder b = new StringBuilder();\n") g.Printf(`b.append("%s").append("{");`, obj.Name()) g.Printf("\n") for _, f := range fields { n := f.Name() g.Printf(`b.append("%s:").append(get%s()).append(",");`, n, n) g.Printf("\n") } g.Printf(`return b.append("}").toString();`) g.Printf("\n") g.Outdent() g.Printf("}\n\n") g.Outdent() g.Printf("}\n\n") }