func (g *goGen) genWrite(valName, seqName string, T types.Type) { if isErrorType(T) { g.Printf("if %s == nil {\n", valName) g.Printf(" %s.WriteString(\"\");\n", seqName) g.Printf("} else {\n") g.Printf(" %s.WriteString(%s.Error());\n", seqName, valName) g.Printf("}\n") return } switch T := T.(type) { case *types.Pointer: // TODO(crawshaw): test *int // TODO(crawshaw): test **Generator switch T := T.Elem().(type) { case *types.Named: obj := T.Obj() if obj.Pkg() != g.pkg { g.errorf("type %s not defined in package %s", T, g.pkg) return } g.Printf("%s.WriteGoRef(%s)\n", seqName, valName) default: g.errorf("unsupported type %s", T) } case *types.Named: switch u := T.Underlying().(type) { case *types.Interface, *types.Pointer: g.Printf("%s.WriteGoRef(%s)\n", seqName, valName) default: g.errorf("unsupported, direct named type %s: %s", T, u) } default: g.Printf("%s.Write%s(%s);\n", seqName, seqType(T), valName) } }
func (g *javaGen) genRead(resName, seqName string, T types.Type) { switch T := T.(type) { case *types.Pointer: // TODO(crawshaw): test *int // TODO(crawshaw): test **Generator switch T := T.Elem().(type) { case *types.Named: o := T.Obj() if o.Pkg() != g.pkg { g.errorf("type %s not defined in %s", T, g.pkg) return } g.Printf("%s = new %s(%s.readRef());\n", resName, o.Name(), seqName) default: g.errorf("unsupported type %s", T) } case *types.Named: switch T.Underlying().(type) { case *types.Interface, *types.Pointer: o := T.Obj() if o.Pkg() != g.pkg { g.errorf("type %s not defined in %s", T, g.pkg) return } g.Printf("%s = new %s.Proxy(%s.readRef());\n", resName, o.Name(), seqName) default: g.errorf("unsupported, direct named type %s", T) } default: g.Printf("%s = %s.read%s();\n", resName, seqName, seqType(T)) } }
// isSupported returns whether the generators can handle the type. func (g *generator) isSupported(t types.Type) bool { if isErrorType(t) { return true } switch t := t.(type) { case *types.Basic: return true case *types.Slice: switch e := t.Elem().(type) { case *types.Basic: return e.Kind() == types.Uint8 } case *types.Pointer: switch t := t.Elem().(type) { case *types.Named: return g.validPkg(t.Obj().Pkg()) } case *types.Named: switch t.Underlying().(type) { case *types.Interface, *types.Pointer: return g.validPkg(t.Obj().Pkg()) } } return false }
// CanHaveDynamicTypes reports whether the type T can "hold" dynamic types, // i.e. is an interface (incl. reflect.Type) or a reflect.Value. // func CanHaveDynamicTypes(T types.Type) bool { switch T := T.(type) { case *types.Named: if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { return true // reflect.Value } return CanHaveDynamicTypes(T.Underlying()) case *types.Interface: return true } return false }
// javaType returns a string that can be used as a Java type. func (g *javaGen) javaType(T types.Type) string { switch T := T.(type) { case *types.Basic: switch T.Kind() { case types.Bool: return "boolean" case types.Int: return "long" case types.Int8: return "byte" case types.Int16: return "short" case types.Int32: return "int" case types.Int64: return "long" case types.Uint8: // TODO(crawshaw): Java bytes are signed, so this is // questionable, but vital. return "byte" // TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64: case types.Float32: return "float" case types.Float64: return "double" case types.String: return "String" default: g.errorf("unsupported return type: %s", T) return "TODO" } case *types.Slice: elem := g.javaType(T.Elem()) return elem + "[]" case *types.Pointer: if _, ok := T.Elem().(*types.Named); ok { return g.javaType(T.Elem()) } panic(fmt.Sprintf("unsupporter pointer to type: %s", T)) case *types.Named: n := T.Obj() if n.Pkg() != g.pkg { panic(fmt.Sprintf("type %s is in package %s, must be defined in package %s", n.Name(), n.Pkg().Name(), g.pkg.Name())) } // TODO(crawshaw): more checking here return n.Name() default: g.errorf("unsupported javaType: %#+v, %s\n", T, T) return "TODO" } }
// CanPoint reports whether the type T is pointerlike, // for the purposes of this analysis. func CanPoint(T types.Type) bool { switch T := T.(type) { case *types.Named: if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { return true // treat reflect.Value like interface{} } return CanPoint(T.Underlying()) case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice: return true } return false // array struct tuple builtin basic }
func isExported(t types.Type) bool { if isErrorType(t) { return true } switch t := t.(type) { case *types.Basic: return true case *types.Named: return t.Obj().Exported() case *types.Pointer: return isExported(t.Elem()) default: return true } }
func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) { var description string var t types.Type switch n := path[0].(type) { case *ast.Ident: t = qpos.info.TypeOf(n) switch t := t.(type) { case *types.Basic: description = "reference to built-in " case *types.Named: isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above if isDef { description = "definition of " } else if _, ok := qpos.info.ObjectOf(n).(*types.Alias); ok { description = "alias of " } else { description = "reference to " } } case ast.Expr: t = qpos.info.TypeOf(n) default: // Unreachable? return nil, fmt.Errorf("unexpected AST for type: %T", n) } description = description + "type " + qpos.typeString(t) // Show sizes for structs and named types (it's fairly obvious for others). switch t.(type) { case *types.Named, *types.Struct: szs := types.StdSizes{WordSize: 8, MaxAlign: 8} // assume amd64 description = fmt.Sprintf("%s (size %d, align %d)", description, szs.Sizeof(t), szs.Alignof(t)) } return &describeTypeResult{ qpos: qpos, node: path[0], description: description, typ: t, methods: accessibleMethods(t, qpos.info.Pkg), fields: accessibleFields(t, qpos.info.Pkg), }, nil }
func (g *javaGen) genCToJava(toName, fromName string, t types.Type, mode varMode) { if isErrorType(t) { g.genCToJava(toName, fromName, types.Typ[types.String], mode) return } switch t := t.(type) { case *types.Basic: switch t.Kind() { case types.String: g.Printf("jstring %s = go_seq_to_java_string(env, %s);\n", toName, fromName) case types.Bool: g.Printf("jboolean %s = %s ? JNI_TRUE : JNI_FALSE;\n", toName, fromName) default: g.Printf("%s %s = (%s)%s;\n", g.jniType(t), toName, g.jniType(t), fromName) } case *types.Slice: switch e := t.Elem().(type) { case *types.Basic: switch e.Kind() { case types.Uint8: // Byte. g.Printf("jbyteArray %s = go_seq_to_java_bytearray(env, %s, %d);\n", toName, fromName, g.toCFlag(mode == modeRetained)) default: g.errorf("unsupported type: %s", t) } default: g.errorf("unsupported type: %s", t) } case *types.Pointer: // TODO(crawshaw): test *int // TODO(crawshaw): test **Generator switch t := t.Elem().(type) { case *types.Named: g.genFromRefnum(toName, fromName, t, t.Obj()) default: g.errorf("unsupported type %s", t) } case *types.Named: switch t.Underlying().(type) { case *types.Interface, *types.Pointer: g.genFromRefnum(toName, fromName, t, t.Obj()) default: g.errorf("unsupported, direct named type %s", t) } default: g.Printf("%s %s = (%s)%s;\n", g.jniType(t), toName, g.jniType(t), fromName) } }
func (g *javaGen) jniSigType(T types.Type) string { if isErrorType(T) { return g.jniSigType(types.Typ[types.String]) } switch T := T.(type) { case *types.Basic: switch T.Kind() { case types.Bool, types.UntypedBool: return "Z" case types.Int: return "J" case types.Int8: return "B" case types.Int16: return "S" case types.Int32, types.UntypedRune: // types.Rune return "I" case types.Int64, types.UntypedInt: return "J" case types.Uint8: // types.Byte return "B" case types.Float32: return "F" case types.Float64, types.UntypedFloat: return "D" case types.String, types.UntypedString: return "Ljava/lang/String;" default: g.errorf("unsupported basic type: %s", T) return "TODO" } case *types.Slice: return "[" + g.jniSigType(T.Elem()) case *types.Pointer: if _, ok := T.Elem().(*types.Named); ok { return g.jniSigType(T.Elem()) } g.errorf("unsupported pointer to type: %s", T) case *types.Named: return "L" + g.jniClassSigPrefix(T.Obj().Pkg()) + T.Obj().Name() + ";" default: g.errorf("unsupported jniType: %#+v, %s\n", T, T) } return "TODO" }
func (g *objcGen) refTypeBase(typ types.Type) string { switch typ := typ.(type) { case *types.Pointer: if _, ok := typ.Elem().(*types.Named); ok { return g.objcType(typ.Elem()) } case *types.Named: n := typ.Obj() if n.Pkg() == g.pkg { switch typ.Underlying().(type) { case *types.Interface, *types.Struct: return g.namePrefix + n.Name() } } } // fallback to whatever objcType returns. This must not happen. return g.objcType(typ) }
func (g *ObjcGen) refTypeBase(typ types.Type) string { switch typ := typ.(type) { case *types.Pointer: if _, ok := typ.Elem().(*types.Named); ok { return g.objcType(typ.Elem()) } case *types.Named: n := typ.Obj() if isObjcType(typ) { return g.wrapMap[n.Name()].Name } if isErrorType(typ) || g.validPkg(n.Pkg()) { switch typ.Underlying().(type) { case *types.Interface, *types.Struct: return g.namePrefixOf(n.Pkg()) + n.Name() } } } // fallback to whatever objcType returns. This must not happen. return g.objcType(typ) }
// javaType returns a string that can be used as a Java type. func (g *JavaGen) javaType(T types.Type) string { if isErrorType(T) { // The error type is usually translated into an exception in // Java, however the type can be exposed in other ways, such // as an exported field. return "java.lang.Exception" } else if isJavaType(T) { return classNameFor(T) } switch T := T.(type) { case *types.Basic: return g.javaBasicType(T) case *types.Slice: elem := g.javaType(T.Elem()) return elem + "[]" case *types.Pointer: if _, ok := T.Elem().(*types.Named); ok { return g.javaType(T.Elem()) } g.errorf("unsupported pointer to type: %s", T) case *types.Named: n := T.Obj() nPkg := n.Pkg() if !isErrorType(T) && !g.validPkg(nPkg) { g.errorf("type %s is in %s, which is not bound", n.Name(), nPkg) break } // TODO(crawshaw): more checking here if nPkg != g.Pkg { return fmt.Sprintf("%s.%s", g.javaPkgName(nPkg), n.Name()) } else { return n.Name() } default: g.errorf("unsupported javaType: %#+v, %s\n", T, T) } return "TODO" }
func (g *objcGen) objcType(typ types.Type) string { if isErrorType(typ) { return "NSError*" } switch typ := typ.(type) { case *types.Basic: switch typ.Kind() { case types.Bool: return "BOOL" case types.Int: return "int" case types.Int8: return "int8_t" case types.Int16: return "int16_t" case types.Int32: return "int32_t" case types.Int64: return "int64_t" case types.Uint8: // byte is an alias of uint8, and the alias is lost. return "byte" case types.Uint16: return "uint16_t" case types.Uint32: return "uint32_t" case types.Uint64: return "uint64_t" case types.Float32: return "float" case types.Float64: return "double" case types.String: return "NSString*" default: g.errorf("unsupported type: %s", typ) return "TODO" } case *types.Slice: elem := g.objcType(typ.Elem()) // Special case: NSData seems to be a better option for byte slice. if elem == "byte" { return "NSData*" } // TODO(hyangah): support other slice types: NSArray or CFArrayRef. // Investigate the performance implication. g.errorf("unsupported type: %s", typ) return "TODO" case *types.Pointer: if _, ok := typ.Elem().(*types.Named); ok { return g.objcType(typ.Elem()) + "*" } g.errorf("unsupported pointer to type: %s", typ) return "TODO" case *types.Named: n := typ.Obj() if n.Pkg() != g.pkg { g.errorf("type %s is in package %s; only types defined in package %s is supported", n.Name(), n.Pkg().Name(), g.pkg.Name()) return "TODO" } switch typ.Underlying().(type) { case *types.Interface: return "id<" + g.namePrefix + n.Name() + ">" case *types.Struct: return g.namePrefix + n.Name() } g.errorf("unsupported, named type %s", typ) return "TODO" default: g.errorf("unsupported type: %#+v, %s", typ, typ) return "TODO" } }
func (p *exporter) typ(t types.Type) { if t == nil { log.Fatalf("gcimporter: nil type") } // Possible optimization: Anonymous pointer types *T where // T is a named type are common. We could canonicalize all // such types *T to a single type PT = *T. This would lead // to at most one *T entry in typIndex, and all future *T's // would be encoded as the respective index directly. Would // save 1 byte (pointerTag) per *T and reduce the typIndex // size (at the cost of a canonicalization map). We can do // this later, without encoding format change. // if we saw the type before, write its index (>= 0) if i, ok := p.typIndex[t]; ok { p.index('T', i) return } // otherwise, remember the type, write the type tag (< 0) and type data if trackAllTypes { if trace { p.tracef("T%d = {>\n", len(p.typIndex)) defer p.tracef("<\n} ") } p.typIndex[t] = len(p.typIndex) } switch t := t.(type) { case *types.Named: if !trackAllTypes { // if we don't track all types, track named types now p.typIndex[t] = len(p.typIndex) } p.tag(namedTag) p.pos(t.Obj()) p.qualifiedName(t.Obj()) p.typ(t.Underlying()) if !types.IsInterface(t) { p.assocMethods(t) } case *types.Array: p.tag(arrayTag) p.int64(t.Len()) p.typ(t.Elem()) case *types.Slice: p.tag(sliceTag) p.typ(t.Elem()) case *dddSlice: p.tag(dddTag) p.typ(t.elem) case *types.Struct: p.tag(structTag) p.fieldList(t) case *types.Pointer: p.tag(pointerTag) p.typ(t.Elem()) case *types.Signature: p.tag(signatureTag) p.paramList(t.Params(), t.Variadic()) p.paramList(t.Results(), false) case *types.Interface: p.tag(interfaceTag) p.iface(t) case *types.Map: p.tag(mapTag) p.typ(t.Key()) p.typ(t.Elem()) case *types.Chan: p.tag(chanTag) p.int(int(3 - t.Dir())) // hack p.typ(t.Elem()) default: log.Fatalf("gcimporter: unexpected type %T: %s", t, t) } }
func (g *ObjcGen) objcType(typ types.Type) string { if isErrorType(typ) { return "NSError*" } switch typ := typ.(type) { case *types.Basic: switch typ.Kind() { case types.Bool, types.UntypedBool: return "BOOL" case types.Int: return "long" case types.Int8: return "int8_t" case types.Int16: return "int16_t" case types.Int32, types.UntypedRune: // types.Rune return "int32_t" case types.Int64, types.UntypedInt: return "int64_t" case types.Uint8: // byte is an alias of uint8, and the alias is lost. return "byte" case types.Uint16: return "uint16_t" case types.Uint32: return "uint32_t" case types.Uint64: return "uint64_t" case types.Float32: return "float" case types.Float64, types.UntypedFloat: return "double" case types.String, types.UntypedString: return "NSString*" default: g.errorf("unsupported type: %s", typ) return "TODO" } case *types.Slice: elem := g.objcType(typ.Elem()) // Special case: NSData seems to be a better option for byte slice. if elem == "byte" { return "NSData*" } // TODO(hyangah): support other slice types: NSArray or CFArrayRef. // Investigate the performance implication. g.errorf("unsupported type: %s", typ) return "TODO" case *types.Pointer: if _, ok := typ.Elem().(*types.Named); ok { return g.objcType(typ.Elem()) + "*" } g.errorf("unsupported pointer to type: %s", typ) return "TODO" case *types.Named: n := typ.Obj() if isObjcType(typ) { w := g.wrapMap[n.Name()] return w.ObjcType() } if !isErrorType(typ) && !g.validPkg(n.Pkg()) { g.errorf("type %s is in package %s, which is not bound", n.Name(), n.Pkg().Name()) return "TODO" } switch t := typ.Underlying().(type) { case *types.Interface: if makeIfaceSummary(t).implementable { return "id<" + g.namePrefixOf(n.Pkg()) + n.Name() + ">" } else { return g.namePrefixOf(n.Pkg()) + n.Name() + "*" } case *types.Struct: return g.namePrefixOf(n.Pkg()) + n.Name() } g.errorf("unsupported, named type %s", typ) return "TODO" default: g.errorf("unsupported type: %#+v, %s", typ, typ) return "TODO" } }
// javaType returns a string that can be used as a Java type. func (g *javaGen) javaType(T types.Type) string { if isErrorType(T) { // The error type is usually translated into an exception in // Java, however the type can be exposed in other ways, such // as an exported field. return "String" } switch T := T.(type) { case *types.Basic: switch T.Kind() { case types.Bool, types.UntypedBool: return "boolean" case types.Int: return "long" case types.Int8: return "byte" case types.Int16: return "short" case types.Int32, types.UntypedRune: // types.Rune return "int" case types.Int64, types.UntypedInt: return "long" case types.Uint8: // types.Byte // TODO(crawshaw): Java bytes are signed, so this is // questionable, but vital. return "byte" // TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64: case types.Float32: return "float" case types.Float64, types.UntypedFloat: return "double" case types.String, types.UntypedString: return "String" default: g.errorf("unsupported basic type: %s", T) return "TODO" } case *types.Slice: elem := g.javaType(T.Elem()) return elem + "[]" case *types.Pointer: if _, ok := T.Elem().(*types.Named); ok { return g.javaType(T.Elem()) } panic(fmt.Sprintf("unsupported pointer to type: %s", T)) case *types.Named: n := T.Obj() if n.Pkg() != g.pkg { nPkgName := "<nilpkg>" if nPkg := n.Pkg(); nPkg != nil { nPkgName = nPkg.Name() } panic(fmt.Sprintf("type %s is in package %s, must be defined in package %s", n.Name(), nPkgName, g.pkg.Name())) } // TODO(crawshaw): more checking here return n.Name() default: g.errorf("unsupported javaType: %#+v, %s\n", T, T) return "TODO" } }
func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) { switch typ := typ.(type) { case *types.Basic: s := typ.Name() switch typ.Kind() { case types.UnsafePointer: s = "unsafe.Pointer" case types.UntypedBool: s = "ideal-bool" case types.UntypedInt: s = "ideal-int" case types.UntypedRune: // "ideal-char" for compatibility with old tool // TODO(gri) change to "ideal-rune" s = "ideal-char" case types.UntypedFloat: s = "ideal-float" case types.UntypedComplex: s = "ideal-complex" case types.UntypedString: s = "ideal-string" case types.UntypedNil: panic("should never see untyped nil type") default: switch s { case "byte": s = "uint8" case "rune": s = "int32" } } buf.WriteString(s) case *types.Array: fmt.Fprintf(buf, "[%d]", typ.Len()) w.writeType(buf, typ.Elem()) case *types.Slice: buf.WriteString("[]") w.writeType(buf, typ.Elem()) case *types.Struct: buf.WriteString("struct") case *types.Pointer: buf.WriteByte('*') w.writeType(buf, typ.Elem()) case *types.Tuple: panic("should never see a tuple type") case *types.Signature: buf.WriteString("func") w.writeSignature(buf, typ) case *types.Interface: buf.WriteString("interface{") if typ.NumMethods() > 0 { buf.WriteByte(' ') buf.WriteString(strings.Join(sortedMethodNames(typ), ", ")) buf.WriteByte(' ') } buf.WriteString("}") case *types.Map: buf.WriteString("map[") w.writeType(buf, typ.Key()) buf.WriteByte(']') w.writeType(buf, typ.Elem()) case *types.Chan: var s string switch typ.Dir() { case types.SendOnly: s = "chan<- " case types.RecvOnly: s = "<-chan " case types.SendRecv: s = "chan " default: panic("unreachable") } buf.WriteString(s) w.writeType(buf, typ.Elem()) case *types.Named: obj := typ.Obj() pkg := obj.Pkg() if pkg != nil && pkg != w.current { buf.WriteString(pkg.Name()) buf.WriteByte('.') } buf.WriteString(typ.Obj().Name()) default: panic(fmt.Sprintf("unknown type %T", typ)) } }
// hashFor computes the hash of t. func (h Hasher) hashFor(t types.Type) uint32 { // See Identical for rationale. switch t := t.(type) { case *types.Basic: return uint32(t.Kind()) case *types.Array: return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem()) case *types.Slice: return 9049 + 2*h.Hash(t.Elem()) case *types.Struct: var hash uint32 = 9059 for i, n := 0, t.NumFields(); i < n; i++ { f := t.Field(i) if f.Anonymous() { hash += 8861 } hash += hashString(t.Tag(i)) hash += hashString(f.Name()) // (ignore f.Pkg) hash += h.Hash(f.Type()) } return hash case *types.Pointer: return 9067 + 2*h.Hash(t.Elem()) case *types.Signature: var hash uint32 = 9091 if t.Variadic() { hash *= 8863 } return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) case *types.Interface: var hash uint32 = 9103 for i, n := 0, t.NumMethods(); i < n; i++ { // See go/types.identicalMethods for rationale. // Method order is not significant. // Ignore m.Pkg(). m := t.Method(i) hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type()) } return hash case *types.Map: return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem()) case *types.Chan: return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem()) case *types.Named: // Not safe with a copying GC; objects may move. return uint32(reflect.ValueOf(t.Obj()).Pointer()) case *types.Tuple: return h.hashTuple(t) } panic(t) }