github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/bind/genjava.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package bind 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/constant" 11 "go/token" 12 "go/types" 13 "io" 14 "math" 15 "regexp" 16 "strings" 17 ) 18 19 // TODO(crawshaw): disallow basic android java type names in exported symbols. 20 // TODO(crawshaw): generate all relevant "implements" relationships for interfaces. 21 // TODO(crawshaw): consider introducing Java functions for casting to and from interfaces at runtime. 22 23 type ErrorList []error 24 25 func (list ErrorList) Error() string { 26 buf := new(bytes.Buffer) 27 for i, err := range list { 28 if i > 0 { 29 buf.WriteRune('\n') 30 } 31 io.WriteString(buf, err.Error()) 32 } 33 return buf.String() 34 } 35 36 type javaGen struct { 37 *printer 38 fset *token.FileSet 39 pkg *types.Package 40 javaPkg string 41 err ErrorList 42 } 43 44 func (g *javaGen) genStruct(obj *types.TypeName, T *types.Struct) { 45 fields := exportedFields(T) 46 methods := exportedMethodSet(types.NewPointer(obj.Type())) 47 48 g.Printf("public static final class %s implements go.Seq.Object {\n", obj.Name()) 49 g.Indent() 50 g.Printf("private static final String DESCRIPTOR = \"go.%s.%s\";\n", g.pkg.Name(), obj.Name()) 51 for i, f := range fields { 52 g.Printf("private static final int FIELD_%s_GET = 0x%x0f;\n", f.Name(), i) 53 g.Printf("private static final int FIELD_%s_SET = 0x%x1f;\n", f.Name(), i) 54 } 55 for i, m := range methods { 56 g.Printf("private static final int CALL_%s = 0x%x0c;\n", m.Name(), i) 57 } 58 g.Printf("\n") 59 60 g.Printf("private go.Seq.Ref ref;\n\n") 61 62 n := obj.Name() 63 g.Printf("private %s(go.Seq.Ref ref) { this.ref = ref; }\n\n", n) 64 g.Printf(`public go.Seq.Ref ref() { return ref; } 65 66 public void call(int code, go.Seq in, go.Seq out) { 67 throw new RuntimeException("internal error: cycle: cannot call concrete proxy"); 68 } 69 70 `) 71 72 for _, f := range fields { 73 g.Printf("public %s get%s() {\n", g.javaType(f.Type()), f.Name()) 74 g.Indent() 75 g.Printf("Seq in = new Seq();\n") 76 g.Printf("Seq out = new Seq();\n") 77 g.Printf("in.writeRef(ref);\n") 78 g.Printf("Seq.send(DESCRIPTOR, FIELD_%s_GET, in, out);\n", f.Name()) 79 if seqType(f.Type()) == "Ref" { 80 g.Printf("return new %s(out.read%s);\n", g.javaType(f.Type()), seqRead(f.Type())) 81 } else { 82 g.Printf("return out.read%s;\n", seqRead(f.Type())) 83 } 84 g.Outdent() 85 g.Printf("}\n\n") 86 87 g.Printf("public void set%s(%s v) {\n", f.Name(), g.javaType(f.Type())) 88 g.Indent() 89 g.Printf("Seq in = new Seq();\n") 90 g.Printf("Seq out = new Seq();\n") 91 g.Printf("in.writeRef(ref);\n") 92 g.Printf("in.write%s;\n", seqWrite(f.Type(), "v")) 93 g.Printf("Seq.send(DESCRIPTOR, FIELD_%s_SET, in, out);\n", f.Name()) 94 g.Outdent() 95 g.Printf("}\n\n") 96 } 97 98 for _, m := range methods { 99 g.genFunc(m, true) 100 } 101 102 g.Printf("@Override public boolean equals(Object o) {\n") 103 g.Indent() 104 g.Printf("if (o == null || !(o instanceof %s)) {\n return false;\n}\n", n) 105 g.Printf("%s that = (%s)o;\n", n, n) 106 for _, f := range fields { 107 nf := f.Name() 108 g.Printf("%s this%s = get%s();\n", g.javaType(f.Type()), nf, nf) 109 g.Printf("%s that%s = that.get%s();\n", g.javaType(f.Type()), nf, nf) 110 if isJavaPrimitive(f.Type()) { 111 g.Printf("if (this%s != that%s) {\n return false;\n}\n", nf, nf) 112 } else { 113 g.Printf("if (this%s == null) {\n", nf) 114 g.Indent() 115 g.Printf("if (that%s != null) {\n return false;\n}\n", nf) 116 g.Outdent() 117 g.Printf("} else if (!this%s.equals(that%s)) {\n return false;\n}\n", nf, nf) 118 } 119 } 120 g.Printf("return true;\n") 121 g.Outdent() 122 g.Printf("}\n\n") 123 124 g.Printf("@Override public int hashCode() {\n") 125 g.Printf(" return java.util.Arrays.hashCode(new Object[] {") 126 for i, f := range fields { 127 if i > 0 { 128 g.Printf(", ") 129 } 130 g.Printf("get%s()", f.Name()) 131 } 132 g.Printf("});\n") 133 g.Printf("}\n\n") 134 135 // TODO(crawshaw): use String() string if it is defined. 136 g.Printf("@Override public String toString() {\n") 137 g.Indent() 138 g.Printf("StringBuilder b = new StringBuilder();\n") 139 g.Printf(`b.append("%s").append("{");`, obj.Name()) 140 g.Printf("\n") 141 for _, f := range fields { 142 n := f.Name() 143 g.Printf(`b.append("%s:").append(get%s()).append(",");`, n, n) 144 g.Printf("\n") 145 } 146 g.Printf(`return b.append("}").toString();`) 147 g.Printf("\n") 148 g.Outdent() 149 g.Printf("}\n\n") 150 151 g.Outdent() 152 g.Printf("}\n\n") 153 } 154 155 func (g *javaGen) genInterfaceStub(o *types.TypeName, m *types.Interface) { 156 g.Printf("public static abstract class Stub implements %s {\n", o.Name()) 157 g.Indent() 158 159 g.Printf("static final String DESCRIPTOR = \"go.%s.%s\";\n\n", g.pkg.Name(), o.Name()) 160 g.Printf("private final go.Seq.Ref ref;\n") 161 g.Printf("public Stub() {\n ref = go.Seq.createRef(this);\n}\n\n") 162 g.Printf("public go.Seq.Ref ref() { return ref; }\n\n") 163 164 g.Printf("public void call(int code, go.Seq in, go.Seq out) {\n") 165 g.Indent() 166 g.Printf("switch (code) {\n") 167 168 for i := 0; i < m.NumMethods(); i++ { 169 f := m.Method(i) 170 g.Printf("case Proxy.CALL_%s: {\n", f.Name()) 171 g.Indent() 172 173 sig := f.Type().(*types.Signature) 174 params := sig.Params() 175 for i := 0; i < params.Len(); i++ { 176 p := sig.Params().At(i) 177 jt := g.javaType(p.Type()) 178 g.Printf("%s param_%s;\n", jt, paramName(params, i)) 179 g.genRead("param_"+paramName(params, i), "in", p.Type()) 180 } 181 182 res := sig.Results() 183 var returnsError bool 184 var numRes = res.Len() 185 if (res.Len() == 1 && isErrorType(res.At(0).Type())) || 186 (res.Len() == 2 && isErrorType(res.At(1).Type())) { 187 numRes -= 1 188 returnsError = true 189 } 190 191 if returnsError { 192 g.Printf("try {\n") 193 g.Indent() 194 } 195 196 if numRes > 0 { 197 g.Printf("%s result = ", g.javaType(res.At(0).Type())) 198 } 199 200 g.Printf("this.%s(", f.Name()) 201 for i := 0; i < params.Len(); i++ { 202 if i > 0 { 203 g.Printf(", ") 204 } 205 g.Printf("param_%s", paramName(params, i)) 206 } 207 g.Printf(");\n") 208 209 if numRes > 0 { 210 g.Printf("out.write%s;\n", seqWrite(res.At(0).Type(), "result")) 211 } 212 if returnsError { 213 g.Printf("out.writeString(null);\n") 214 g.Outdent() 215 g.Printf("} catch (Exception e) {\n") 216 g.Indent() 217 if numRes > 0 { 218 resTyp := res.At(0).Type() 219 g.Printf("%s result = %s;\n", g.javaType(resTyp), g.javaTypeDefault(resTyp)) 220 g.Printf("out.write%s;\n", seqWrite(resTyp, "result")) 221 } 222 g.Printf("out.writeString(e.getMessage());\n") 223 g.Outdent() 224 g.Printf("}\n") 225 } 226 g.Printf("return;\n") 227 g.Outdent() 228 g.Printf("}\n") 229 } 230 231 g.Printf("default:\n throw new RuntimeException(\"unknown code: \"+ code);\n") 232 g.Printf("}\n") 233 g.Outdent() 234 g.Printf("}\n") 235 236 g.Outdent() 237 g.Printf("}\n\n") 238 } 239 240 const javaProxyPreamble = `static final class Proxy implements %s { 241 static final String DESCRIPTOR = Stub.DESCRIPTOR; 242 243 private go.Seq.Ref ref; 244 245 Proxy(go.Seq.Ref ref) { this.ref = ref; } 246 247 public go.Seq.Ref ref() { return ref; } 248 249 public void call(int code, go.Seq in, go.Seq out) { 250 throw new RuntimeException("cycle: cannot call proxy"); 251 } 252 253 ` 254 255 func (g *javaGen) genInterface(o *types.TypeName) { 256 iface := o.Type().(*types.Named).Underlying().(*types.Interface) 257 258 summary := makeIfaceSummary(iface) 259 260 g.Printf("public interface %s extends go.Seq.Object {\n", o.Name()) 261 g.Indent() 262 263 methodSigErr := false 264 for _, m := range summary.callable { 265 if err := g.funcSignature(m, false); err != nil { 266 methodSigErr = true 267 g.errorf("%v", err) 268 } 269 g.Printf(";\n\n") 270 } 271 if methodSigErr { 272 return // skip stub generation, more of the same errors 273 } 274 275 if summary.implementable { 276 g.genInterfaceStub(o, iface) 277 } 278 279 g.Printf(javaProxyPreamble, o.Name()) 280 g.Indent() 281 282 for _, m := range summary.callable { 283 g.genFunc(m, true) 284 } 285 for i, m := range summary.callable { 286 g.Printf("static final int CALL_%s = 0x%x0a;\n", m.Name(), i+1) 287 } 288 289 g.Outdent() 290 g.Printf("}\n") 291 292 g.Outdent() 293 g.Printf("}\n\n") 294 } 295 296 func isJavaPrimitive(T types.Type) bool { 297 b, ok := T.(*types.Basic) 298 if !ok { 299 return false 300 } 301 switch b.Kind() { 302 case types.Bool, types.Uint8, types.Float32, types.Float64, 303 types.Int, types.Int8, types.Int16, types.Int32, types.Int64: 304 return true 305 } 306 return false 307 } 308 309 // javaType returns a string that can be used as a Java type. 310 func (g *javaGen) javaType(T types.Type) string { 311 if isErrorType(T) { 312 // The error type is usually translated into an exception in 313 // Java, however the type can be exposed in other ways, such 314 // as an exported field. 315 return "String" 316 } 317 switch T := T.(type) { 318 case *types.Basic: 319 switch T.Kind() { 320 case types.Bool, types.UntypedBool: 321 return "boolean" 322 case types.Int: 323 return "long" 324 case types.Int8: 325 return "byte" 326 case types.Int16: 327 return "short" 328 case types.Int32, types.UntypedRune: // types.Rune 329 return "int" 330 case types.Int64, types.UntypedInt: 331 return "long" 332 case types.Uint8: // types.Byte 333 // TODO(crawshaw): Java bytes are signed, so this is 334 // questionable, but vital. 335 return "byte" 336 // TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64: 337 case types.Float32: 338 return "float" 339 case types.Float64, types.UntypedFloat: 340 return "double" 341 case types.String, types.UntypedString: 342 return "String" 343 default: 344 g.errorf("unsupported basic type: %s", T) 345 return "TODO" 346 } 347 case *types.Slice: 348 elem := g.javaType(T.Elem()) 349 return elem + "[]" 350 351 case *types.Pointer: 352 if _, ok := T.Elem().(*types.Named); ok { 353 return g.javaType(T.Elem()) 354 } 355 panic(fmt.Sprintf("unsupported pointer to type: %s", T)) 356 case *types.Named: 357 n := T.Obj() 358 if n.Pkg() != g.pkg { 359 nPkgName := "<nilpkg>" 360 if nPkg := n.Pkg(); nPkg != nil { 361 nPkgName = nPkg.Name() 362 } 363 panic(fmt.Sprintf("type %s is in package %s, must be defined in package %s", n.Name(), nPkgName, g.pkg.Name())) 364 } 365 // TODO(crawshaw): more checking here 366 return n.Name() 367 default: 368 g.errorf("unsupported javaType: %#+v, %s\n", T, T) 369 return "TODO" 370 } 371 } 372 373 // javaTypeDefault returns a string that represents the default value of the mapped java type. 374 // TODO(hyangah): Combine javaType and javaTypeDefault? 375 func (g *javaGen) javaTypeDefault(T types.Type) string { 376 switch T := T.(type) { 377 case *types.Basic: 378 switch T.Kind() { 379 case types.Bool: 380 return "false" 381 case types.Int, types.Int8, types.Int16, types.Int32, 382 types.Int64, types.Uint8: 383 return "0" 384 case types.Float32, types.Float64: 385 return "0.0" 386 case types.String: 387 return "null" 388 default: 389 g.errorf("unsupported return type: %s", T) 390 return "TODO" 391 } 392 case *types.Slice, *types.Pointer, *types.Named: 393 return "null" 394 395 default: 396 g.errorf("unsupported javaType: %#+v, %s\n", T, T) 397 return "TODO" 398 } 399 } 400 401 var paramRE = regexp.MustCompile(`^p[0-9]*$`) 402 403 // paramName replaces incompatible name with a p0-pN name. 404 // Missing names, or existing names of the form p[0-9] are incompatible. 405 // TODO(crawshaw): Replace invalid unicode names. 406 func paramName(params *types.Tuple, pos int) string { 407 name := params.At(pos).Name() 408 if name == "" || name == "_" || paramRE.MatchString(name) { 409 name = fmt.Sprintf("p%d", pos) 410 } 411 return name 412 } 413 414 func (g *javaGen) funcSignature(o *types.Func, static bool) error { 415 sig := o.Type().(*types.Signature) 416 res := sig.Results() 417 418 var returnsError bool 419 var ret string 420 switch res.Len() { 421 case 2: 422 if !isErrorType(res.At(1).Type()) { 423 return fmt.Errorf("second result value must be of type error: %s", o) 424 } 425 returnsError = true 426 ret = g.javaType(res.At(0).Type()) 427 case 1: 428 if isErrorType(res.At(0).Type()) { 429 returnsError = true 430 ret = "void" 431 } else { 432 ret = g.javaType(res.At(0).Type()) 433 } 434 case 0: 435 ret = "void" 436 default: 437 return fmt.Errorf("too many result values: %s", o) 438 } 439 440 g.Printf("public ") 441 if static { 442 g.Printf("static ") 443 } 444 g.Printf("%s %s(", ret, o.Name()) 445 params := sig.Params() 446 for i := 0; i < params.Len(); i++ { 447 if i > 0 { 448 g.Printf(", ") 449 } 450 v := sig.Params().At(i) 451 name := paramName(params, i) 452 jt := g.javaType(v.Type()) 453 g.Printf("%s %s", jt, name) 454 } 455 g.Printf(")") 456 if returnsError { 457 g.Printf(" throws Exception") 458 } 459 return nil 460 } 461 462 func (g *javaGen) genVar(o *types.Var) { 463 jType := g.javaType(o.Type()) 464 varDesc := fmt.Sprintf("%s.%s", g.pkg.Name(), o.Name()) 465 466 // setter 467 g.Printf("public static void set%s(%s v) {\n", o.Name(), jType) 468 g.Indent() 469 g.Printf("Seq in = new Seq();\n") 470 g.Printf("Seq out = new Seq();\n") 471 g.Printf("in.write%s;\n", seqWrite(o.Type(), "v")) 472 g.Printf("Seq.send(%q, 1, in, out);\n", varDesc) 473 g.Outdent() 474 g.Printf("}\n") 475 g.Printf("\n") 476 477 // getter 478 g.Printf("public static %s get%s() {\n", jType, o.Name()) 479 g.Indent() 480 g.Printf("Seq in = new Seq();\n") 481 g.Printf("Seq out = new Seq();\n") 482 g.Printf("Seq.send(%q, 2, in, out);\n", varDesc) 483 g.Printf("%s ", jType) 484 g.genRead("v", "out", o.Type()) 485 g.Printf("return v;\n") 486 g.Outdent() 487 g.Printf("}\n") 488 g.Printf("\n") 489 } 490 491 func (g *javaGen) genFunc(o *types.Func, method bool) { 492 if err := g.funcSignature(o, !method); err != nil { 493 g.errorf("%v", err) 494 return 495 } 496 sig := o.Type().(*types.Signature) 497 res := sig.Results() 498 499 g.Printf(" {\n") 500 g.Indent() 501 g.Printf("go.Seq _in = new go.Seq();\n") 502 g.Printf("go.Seq _out = new go.Seq();\n") 503 504 returnsError := false 505 var resultType types.Type 506 if res.Len() > 0 { 507 if !isErrorType(res.At(0).Type()) { 508 resultType = res.At(0).Type() 509 } 510 if res.Len() > 1 || isErrorType(res.At(0).Type()) { 511 returnsError = true 512 } 513 } 514 if resultType != nil { 515 t := g.javaType(resultType) 516 g.Printf("%s _result;\n", t) 517 } 518 519 if method { 520 g.Printf("_in.writeRef(ref);\n") 521 } 522 params := sig.Params() 523 for i := 0; i < params.Len(); i++ { 524 p := params.At(i) 525 g.Printf("_in.write%s;\n", seqWrite(p.Type(), paramName(params, i))) 526 } 527 g.Printf("Seq.send(DESCRIPTOR, CALL_%s, _in, _out);\n", o.Name()) 528 if resultType != nil { 529 g.genRead("_result", "_out", resultType) 530 } 531 if returnsError { 532 g.Printf(`String _err = _out.readString(); 533 if (_err != null && !_err.isEmpty()) { 534 throw new Exception(_err); 535 } 536 `) 537 } 538 if resultType != nil { 539 g.Printf("return _result;\n") 540 } 541 g.Outdent() 542 g.Printf("}\n\n") 543 } 544 545 func (g *javaGen) genRead(resName, seqName string, T types.Type) { 546 switch T := T.(type) { 547 case *types.Pointer: 548 // TODO(crawshaw): test *int 549 // TODO(crawshaw): test **Generator 550 switch T := T.Elem().(type) { 551 case *types.Named: 552 o := T.Obj() 553 if o.Pkg() != g.pkg { 554 g.errorf("type %s not defined in %s", T, g.pkg) 555 return 556 } 557 g.Printf("%s = new %s(%s.readRef());\n", resName, o.Name(), seqName) 558 default: 559 g.errorf("unsupported type %s", T) 560 } 561 case *types.Named: 562 switch T.Underlying().(type) { 563 case *types.Interface, *types.Pointer: 564 o := T.Obj() 565 if o.Pkg() != g.pkg { 566 g.errorf("type %s not defined in %s", T, g.pkg) 567 return 568 } 569 g.Printf("%s = new %s.Proxy(%s.readRef());\n", resName, o.Name(), seqName) 570 default: 571 g.errorf("unsupported, direct named type %s", T) 572 } 573 default: 574 g.Printf("%s = %s.read%s();\n", resName, seqName, seqType(T)) 575 } 576 } 577 578 func (g *javaGen) errorf(format string, args ...interface{}) { 579 g.err = append(g.err, fmt.Errorf(format, args...)) 580 } 581 582 func (g *javaGen) gobindOpts() string { 583 opts := []string{"-lang=java"} 584 if g.javaPkg != javaPkgName(g.pkg.Name()) { 585 opts = append(opts, "-javapkg="+g.javaPkg) 586 } 587 return strings.Join(opts, " ") 588 } 589 590 const javaPreamble = `// Java class %[1]s.%[2]s is a proxy for talking to a Go program. 591 // gobind %[3]s %[4]s 592 // 593 // File is generated by gobind. Do not edit. 594 package %[1]s; 595 596 import go.Seq; 597 598 ` 599 600 var javaNameReplacer = strings.NewReplacer( 601 "-", "_", 602 ".", "_", 603 ) 604 605 func javaPkgName(pkgName string) string { 606 s := javaNameReplacer.Replace(pkgName) 607 // Look for Java keywords that are not Go keywords, and avoid using 608 // them as a package name. 609 // 610 // This is not a problem for normal Go identifiers as we only expose 611 // exported symbols. The upper case first letter saves everything 612 // from accidentally matching except for the package name. 613 // 614 // Note that basic type names (like int) are not keywords in Go. 615 switch s { 616 case "abstract", "assert", "boolean", "byte", "catch", "char", "class", 617 "do", "double", "enum", "extends", "final", "finally", "float", 618 "implements", "instanceof", "int", "long", "native", "private", 619 "protected", "public", "short", "static", "strictfp", "super", 620 "synchronized", "this", "throw", "throws", "transient", "try", 621 "void", "volatile", "while": 622 s += "_" 623 } 624 return "go." + s 625 } 626 627 func (g *javaGen) className() string { 628 return strings.Title(javaNameReplacer.Replace(g.pkg.Name())) 629 } 630 631 func (g *javaGen) genConst(o *types.Const) { 632 // TODO(hyangah): should const names use upper cases + "_"? 633 // TODO(hyangah): check invalid names. 634 jType := g.javaType(o.Type()) 635 val := o.Val().String() 636 switch b := o.Type().(*types.Basic); b.Kind() { 637 case types.Int64, types.UntypedInt: 638 i, exact := constant.Int64Val(o.Val()) 639 if !exact { 640 g.errorf("const value %s for %s cannot be represented as %s", val, o.Name(), jType) 641 return 642 } 643 val = fmt.Sprintf("%dL", i) 644 645 case types.Float32: 646 f, _ := constant.Float32Val(o.Val()) 647 val = fmt.Sprintf("%gf", f) 648 649 case types.Float64, types.UntypedFloat: 650 f, _ := constant.Float64Val(o.Val()) 651 if math.IsInf(f, 0) || math.Abs(f) > math.MaxFloat64 { 652 g.errorf("const value %s for %s cannot be represented as %s", val, o.Name(), jType) 653 return 654 } 655 val = fmt.Sprintf("%g", f) 656 } 657 g.Printf("public static final %s %s = %s;\n", g.javaType(o.Type()), o.Name(), val) 658 } 659 660 func (g *javaGen) gen() error { 661 g.Printf(javaPreamble, g.javaPkg, g.className(), g.gobindOpts(), g.pkg.Path()) 662 663 g.Printf("public abstract class %s {\n", g.className()) 664 g.Indent() 665 g.Printf("private %s() {} // uninstantiable\n\n", g.className()) 666 667 var funcs []string 668 669 scope := g.pkg.Scope() 670 names := scope.Names() 671 hasExported := false 672 for _, name := range names { 673 obj := scope.Lookup(name) 674 if !obj.Exported() { 675 continue 676 } 677 hasExported = true 678 679 switch o := obj.(type) { 680 // TODO(crawshaw): case *types.Var: 681 case *types.Func: 682 if isCallable(o) { 683 g.genFunc(o, false) 684 funcs = append(funcs, o.Name()) 685 } 686 case *types.TypeName: 687 named := o.Type().(*types.Named) 688 switch t := named.Underlying().(type) { 689 case *types.Struct: 690 g.genStruct(o, t) 691 case *types.Interface: 692 g.genInterface(o) 693 default: 694 g.errorf("%s: cannot generate binding for %s: %T", g.fset.Position(o.Pos()), o.Name(), t) 695 continue 696 } 697 case *types.Const: 698 g.genConst(o) 699 case *types.Var: 700 g.genVar(o) 701 default: 702 g.errorf("unsupported exported type: %T", obj) 703 } 704 } 705 if !hasExported { 706 g.errorf("no exported names in the package %q", g.pkg.Path()) 707 } 708 709 for i, name := range funcs { 710 g.Printf("private static final int CALL_%s = %d;\n", name, i+1) 711 } 712 713 g.Printf("private static final String DESCRIPTOR = %q;\n", g.pkg.Name()) 714 g.Outdent() 715 g.Printf("}\n") 716 717 if len(g.err) > 0 { 718 return g.err 719 } 720 return nil 721 }