2
0
mirror of synced 2025-02-24 07:18:15 +00:00
mobile/bind/types.go
Elias Naur bdf873ed8f bind,cmd: accept Java API in bound packages
Accept Java API interface types as arguments and return values from
bound Go package functions and methods. Also, allow Go structs
to extend Java classes and implement Java interfaces as well as override
and implement methods.

This is the third and final part of the implementation of the golang/go#16876
proposal.

Fixes golang/go#16876

Change-Id: I6951dd87235553ce09abe5117a39a503466163c0
Reviewed-on: https://go-review.googlesource.com/28597
Reviewed-by: David Crawshaw <crawshaw@golang.org>
2016-09-22 10:16:33 +00:00

156 lines
3.3 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 java classes, so that
// only methods that are implemented in Go are included.
if isJavaPkg(obj.Pkg()) {
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 isJavaType(t types.Type) bool {
nt, ok := t.(*types.Named)
if !ok {
return false
}
return isJavaPkg(nt.Obj().Pkg())
}
func isJavaPkg(p *types.Package) bool {
return p != nil && strings.HasPrefix(p.Path(), "Java/")
}