| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package bind |
| |
| import ( |
| "fmt" |
| "go/types" |
| "log" |
| "strings" |
| ) |
| |
| type ifaceSummary struct { |
| iface *types.Interface |
| callable []*types.Func |
| implementable bool |
| } |
| |
| func makeIfaceSummary(iface *types.Interface) ifaceSummary { |
| summary := ifaceSummary{ |
| iface: iface, |
| implementable: true, |
| } |
| methodset := types.NewMethodSet(iface) |
| for i := 0; i < methodset.Len(); i++ { |
| obj := methodset.At(i).Obj() |
| if !obj.Exported() { |
| summary.implementable = false |
| continue |
| } |
| m, ok := obj.(*types.Func) |
| if !ok { |
| log.Panicf("unexpected methodset obj: %s (%T)", obj, obj) |
| } |
| if !isImplementable(m.Type().(*types.Signature)) { |
| summary.implementable = false |
| } |
| if isCallable(m) { |
| summary.callable = append(summary.callable, m) |
| } |
| } |
| return summary |
| } |
| |
| func isCallable(t *types.Func) bool { |
| // TODO(crawshaw): functions that are not implementable from |
| // another language may still be callable (for example, a |
| // returned value with an unexported type can be treated as |
| // an opaque value by the caller). This restriction could be |
| // lifted. |
| return isImplementable(t.Type().(*types.Signature)) |
| } |
| |
| func isImplementable(sig *types.Signature) bool { |
| params := sig.Params() |
| for i := 0; i < params.Len(); i++ { |
| if !isExported(params.At(i).Type()) { |
| return false |
| } |
| } |
| res := sig.Results() |
| for i := 0; i < res.Len(); i++ { |
| if !isExported(res.At(i).Type()) { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func exportedMethodSet(T types.Type) []*types.Func { |
| var methods []*types.Func |
| methodset := types.NewMethodSet(T) |
| for i := 0; i < methodset.Len(); i++ { |
| obj := methodset.At(i).Obj() |
| if !obj.Exported() { |
| continue |
| } |
| // Skip methods from the embedded classes, so that |
| // only methods that are implemented in Go are included. |
| if pref := pkgFirstElem(obj.Pkg()); pref == "Java" || pref == "ObjC" { |
| continue |
| } |
| switch obj := obj.(type) { |
| case *types.Func: |
| methods = append(methods, obj) |
| default: |
| log.Panicf("unexpected methodset obj: %s", obj) |
| } |
| } |
| return methods |
| } |
| |
| func exportedFields(T *types.Struct) []*types.Var { |
| var fields []*types.Var |
| for i := 0; i < T.NumFields(); i++ { |
| f := T.Field(i) |
| if !f.Exported() { |
| continue |
| } |
| fields = append(fields, f) |
| } |
| return fields |
| } |
| |
| func isErrorType(t types.Type) bool { |
| return types.Identical(t, types.Universe.Lookup("error").Type()) |
| } |
| |
| 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 isRefType(t types.Type) bool { |
| if isErrorType(t) { |
| return false |
| } |
| switch t := t.(type) { |
| case *types.Named: |
| switch u := t.Underlying().(type) { |
| case *types.Interface: |
| return true |
| default: |
| panic(fmt.Sprintf("unsupported named type: %s / %T", u, u)) |
| } |
| case *types.Pointer: |
| return isRefType(t.Elem()) |
| default: |
| return false |
| } |
| } |
| |
| func isNullableType(t types.Type) bool { |
| return types.AssignableTo(types.Typ[types.UntypedNil].Underlying(), t) || t.String() == "string" // string is mapped to NSString*, which is nullable |
| } |
| |
| func typePkgFirstElem(t types.Type) string { |
| nt, ok := t.(*types.Named) |
| if !ok { |
| return "" |
| } |
| return pkgFirstElem(nt.Obj().Pkg()) |
| } |
| |
| func pkgFirstElem(p *types.Package) string { |
| if p == nil { |
| return "" |
| } |
| path := p.Path() |
| idx := strings.Index(path, "/") |
| if idx == -1 { |
| return path |
| } |
| return path[:idx] |
| } |
| |
| func isWrapperType(t types.Type) bool { |
| e := typePkgFirstElem(t) |
| return e == "Java" || e == "ObjC" |
| } |