Accept ObjC API wrapper types as arguments and return values from bound Go package functions and methods. Also, allow Go structs to extend ObjC classes and implement ObjC protocols as well as override and implement methods. This is the third and final part of the implementation of the golang/go#17102 proposal. Fixes golang/go#17102 Change-Id: I601d90fb6d22b8d6f8b7d5fe0130daa1a4dd4734 Reviewed-on: https://go-review.googlesource.com/29175 Reviewed-by: David Crawshaw <crawshaw@golang.org>
169 lines
3.5 KiB
Go
169 lines
3.5 KiB
Go
// 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 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 ""
|
|
}
|
|
return path[:idx]
|
|
}
|
|
|
|
func isWrapperType(t types.Type) bool {
|
|
e := typePkgFirstElem(t)
|
|
return e == "Java" || e == "ObjC"
|
|
}
|