bind,cmd,internal: generate reverse bindings for exported Go structs

Before this CL, the type of the implicit "this" parameter to Java methods
implemented in Go could only be a super class of the generated Java
class. For example, the following GoRunnable type is an implementation of
the Java interface java.lang.Runnable with a toString method:

package somepkg

import "Java/java/lang"

type GoRunnable struct {
    lang.Runnable
}

func (r *GoRunnable) ToString(this lang.Runnable) string {
    ...
}

The "this" parameter is implicit in the sense that the reverse generator
automatically fills it with a reference to the Java instance of
GoRunnable.

Note that "this" has the type Java/java/lang.Runnable, not
Java/go/somepkg.GoRunnable, which renders it impossible to call Java
methods and functions that expect GoRunnable. The most practical example
of this is the Android databinding libraries.

This CL changes the implicit this parameter to always match the exact
type. In the example, the toString implementation becomes:

import gopkg "Java/go/somepkg"

func (r *GoRunnable) ToString(this gopkg.GoRunnable) string {
    ...
}

One strategy would be to simply treat the generated Java classes
(GoRunnable in our example) as any other Java class and import it
through javap. However, since the Java classes are generated after
importing, this present a chicken-and-egg problem.

Instead, use the newly added support for structs with embedded prefixed types
and synthesize class descriptors for every exported Go struct type.

Change-Id: Ic5ce4a151312bd89f91798ed4088c9959225b448
Reviewed-on: https://go-review.googlesource.com/34776
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/bind/bind_test.go b/bind/bind_test.go
index 73bca05..cf6d009 100644
--- a/bind/bind_test.go
+++ b/bind/bind_test.go
@@ -223,14 +223,7 @@
 	return cg
 }
 
-func genJavaPackages(t *testing.T, dir string, classes []*java.Class, buf *bytes.Buffer) *ClassGen {
-	cg := &ClassGen{
-		Printer: &Printer{
-			IndentEach: []byte("\t"),
-			Buf:        buf,
-		},
-	}
-	cg.Init(classes)
+func genJavaPackages(t *testing.T, dir string, cg *ClassGen) {
 	pkgBase := filepath.Join(dir, "src", "Java")
 	if err := os.MkdirAll(pkgBase, 0700); err != nil {
 		t.Fatal(err)
@@ -241,16 +234,16 @@
 			t.Fatal(err)
 		}
 		pkgFile := filepath.Join(pkgDir, "package.go")
-		buf.Reset()
+		cg.Buf.Reset()
 		cg.GenPackage(i)
-		if err := ioutil.WriteFile(pkgFile, buf.Bytes(), 0600); err != nil {
+		if err := ioutil.WriteFile(pkgFile, cg.Buf.Bytes(), 0600); err != nil {
 			t.Fatal(err)
 		}
 	}
-	buf.Reset()
+	cg.Buf.Reset()
 	cg.GenInterfaces()
 	clsFile := filepath.Join(pkgBase, "interfaces.go")
-	if err := ioutil.WriteFile(clsFile, buf.Bytes(), 0600); err != nil {
+	if err := ioutil.WriteFile(clsFile, cg.Buf.Bytes(), 0600); err != nil {
 		t.Fatal(err)
 	}
 
@@ -264,7 +257,6 @@
 	if out, err := cmd.CombinedOutput(); err != nil {
 		t.Fatalf("failed to go install the generated Java wrappers: %v: %s", err, string(out))
 	}
-	return cg
 }
 
 func TestGenJava(t *testing.T) {
@@ -274,7 +266,10 @@
 	}
 	for _, filename := range allTests {
 		refs := fileRefs(t, filename, "Java/")
-		classes, err := java.Import("", "", refs)
+		imp := &java.Importer{
+			JavaPkgPrefix: "go.",
+		}
+		classes, err := imp.Import(refs)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -287,7 +282,18 @@
 				t.Fatal(err)
 			}
 			defer os.RemoveAll(tmpGopath)
-			cg = genJavaPackages(t, tmpGopath, classes, new(bytes.Buffer))
+			cg = &ClassGen{
+				Printer: &Printer{
+					IndentEach: []byte("\t"),
+					Buf:        new(bytes.Buffer),
+				},
+			}
+			var genNames []string
+			for _, emb := range refs.Embedders {
+				genNames = append(genNames, imp.JavaPkgPrefix+emb.Pkg+"."+emb.Name)
+			}
+			cg.Init(classes, genNames)
+			genJavaPackages(t, tmpGopath, cg)
 			cg.Buf = &buf
 		}
 		pkg := typeCheck(t, filename, tmpGopath)
@@ -374,7 +380,10 @@
 	for _, filename := range javaTests {
 		var buf bytes.Buffer
 		refs := fileRefs(t, filename, "Java/")
-		classes, err := java.Import("", "", refs)
+		imp := &java.Importer{
+			JavaPkgPrefix: "go.",
+		}
+		classes, err := imp.Import(refs)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -383,7 +392,18 @@
 			t.Fatal(err)
 		}
 		defer os.RemoveAll(tmpGopath)
-		cg := genJavaPackages(t, tmpGopath, classes, &buf)
+		cg := &ClassGen{
+			Printer: &Printer{
+				IndentEach: []byte("\t"),
+				Buf:        &buf,
+			},
+		}
+		var genNames []string
+		for _, emb := range refs.Embedders {
+			genNames = append(genNames, imp.JavaPkgPrefix+emb.Pkg+"."+emb.Name)
+		}
+		cg.Init(classes, genNames)
+		genJavaPackages(t, tmpGopath, cg)
 		pkg := typeCheck(t, filename, tmpGopath)
 		cg.GenGo()
 		testGenGo(t, filename, &buf, pkg)
diff --git a/bind/genclasses.go b/bind/genclasses.go
index 409e5eb..c7a144c 100644
--- a/bind/genclasses.go
+++ b/bind/genclasses.go
@@ -34,6 +34,8 @@
 		// For each Go package path, the Java class with static functions
 		// or constants.
 		clsPkgs map[string]*java.Class
+		// supers is the map of classes that need Super methods
+		supers map[string]struct{}
 	}
 )
 
@@ -95,7 +97,14 @@
 	}
 }
 
-func (g *ClassGen) Init(classes []*java.Class) {
+// Init initializes the class wrapper generator. Classes is the
+// list of classes to wrap, supers is the list of class names
+// that need Super methods.
+func (g *ClassGen) Init(classes []*java.Class, supers []string) {
+	g.supers = make(map[string]struct{})
+	for _, s := range supers {
+		g.supers[s] = struct{}{}
+	}
 	g.classes = classes
 	g.imported = make(map[string]struct{})
 	g.typePkgs = make(map[string][]*java.Class)
@@ -210,7 +219,7 @@
 			g.Printf("extern ")
 			g.genCMethodDecl("cproxy", cls.JNIName, f)
 			g.Printf(";\n")
-			if cls.HasSuper() {
+			if _, ok := g.supers[cls.Name]; ok {
 				g.Printf("extern ")
 				g.genCMethodDecl("csuper", cls.JNIName, f)
 				g.Printf(";\n")
@@ -227,9 +236,15 @@
 	for _, cls := range g.classes {
 		g.genC(cls)
 		g.Printf("static jclass class_%s;\n", cls.JNIName)
+		if _, ok := g.supers[cls.Name]; ok {
+			g.Printf("static jclass sclass_%s;\n", cls.JNIName)
+		}
 		for _, f := range cls.AllMethods {
 			if g.isFuncSupported(f) {
 				g.Printf("static jmethodID m_%s_%s;\n", cls.JNIName, f.JNIName)
+				if _, ok := g.supers[cls.Name]; ok {
+					g.Printf("static jmethodID sm_%s_%s;\n", cls.JNIName, f.JNIName)
+				}
 			}
 		}
 	}
@@ -241,9 +256,16 @@
 	for _, cls := range g.classes {
 		g.Printf("clazz = (*env)->FindClass(env, %q);\n", strings.Replace(cls.FindName, ".", "/", -1))
 		g.Printf("class_%s = (*env)->NewGlobalRef(env, clazz);\n", cls.JNIName)
+		if _, ok := g.supers[cls.Name]; ok {
+			g.Printf("sclass_%s = (*env)->GetSuperclass(env, clazz);\n", cls.JNIName)
+			g.Printf("sclass_%s = (*env)->NewGlobalRef(env, sclass_%s);\n", cls.JNIName, cls.JNIName)
+		}
 		for _, f := range cls.AllMethods {
 			if g.isFuncSupported(f) {
 				g.Printf("m_%s_%s = go_seq_get_method_id(clazz, %q, %q);\n", cls.JNIName, f.JNIName, f.Name, f.Desc)
+				if _, ok := g.supers[cls.Name]; ok {
+					g.Printf("sm_%s_%s = go_seq_get_method_id(sclass_%s, %q, %q);\n", cls.JNIName, f.JNIName, cls.JNIName, f.Name, f.Desc)
+				}
 			}
 		}
 	}
@@ -257,7 +279,7 @@
 			}
 			g.genCMethodDecl("cproxy", cls.JNIName, f)
 			g.genCMethodBody(cls, f, false)
-			if cls.HasSuper() {
+			if _, ok := g.supers[cls.Name]; ok {
 				g.genCMethodDecl("csuper", cls.JNIName, f)
 				g.genCMethodBody(cls, f, true)
 			}
@@ -296,9 +318,10 @@
 	}
 	g.Printf("Method(env, _this, ")
 	if virtual {
-		g.Printf("class_%s, ", cls.JNIName)
+		g.Printf("sclass_%s, sm_%s_%s", cls.JNIName, cls.JNIName, f.JNIName)
+	} else {
+		g.Printf("m_%s_%s", cls.JNIName, f.JNIName)
 	}
-	g.Printf("m_%s_%s", cls.JNIName, f.JNIName)
 	for i := range f.Params {
 		g.Printf(", _a%d", i)
 	}
@@ -509,7 +532,7 @@
 		g.Printf("	return p.ToString()\n")
 		g.Printf("}\n")
 	}
-	if cls.HasSuper() {
+	if _, ok := g.supers[cls.Name]; ok {
 		g.Printf("func (p *proxy_class_%s) Super() Java.%s {\n", cls.JNIName, goClsName(cls.Name))
 		g.Printf("	return &super_%s{p}\n", cls.JNIName)
 		g.Printf("}\n\n")
@@ -693,7 +716,7 @@
 		g.genFuncDecl(true, f)
 		g.Printf("\n")
 	}
-	if cls.HasSuper() {
+	if _, ok := g.supers[cls.Name]; ok {
 		g.Printf("Super() %s\n", goClsName(cls.Name))
 	}
 	if cls.Throwable {
diff --git a/bind/genjava.go b/bind/genjava.go
index 780eeb6..bcd0a4c 100644
--- a/bind/genjava.go
+++ b/bind/genjava.go
@@ -228,21 +228,18 @@
 				v := params.At(0)
 				if v.Name() == "this" {
 					t := v.Type()
-					if isJavaType(t) {
-						clsName := classNameFor(t)
-						cls := g.clsMap[clsName]
-						found := false
-						for _, sup := range jinf.supers {
-							if cls == sup {
-								found = true
-								break
+					if t, ok := t.(*types.Named); ok {
+						obj := t.Obj()
+						pkg := obj.Pkg()
+						if pkgFirstElem(pkg) == "Java" {
+							clsName := classNameFor(t)
+							exp := g.javaPkgName(g.Pkg) + "." + s.obj.Name()
+							if clsName != exp {
+								g.errorf("the type %s of the `this` argument to method %s.%s is not %s", clsName, n, m.Name(), exp)
+								continue
 							}
+							hasThis = true
 						}
-						if !found {
-							g.errorf("the type %s of the `this` argument to method %s.%s is not a super class to %s", cls.Name, n, m.Name(), n)
-							continue
-						}
-						hasThis = true
 					}
 				}
 			}
diff --git a/bind/testdata/classes.go b/bind/testdata/classes.go
index 696507e..8e531c0 100644
--- a/bind/testdata/classes.go
+++ b/bind/testdata/classes.go
@@ -5,6 +5,7 @@
 package java
 
 import (
+	gopkg "Java/go/java"
 	"Java/java/io"
 	"Java/java/lang"
 	"Java/java/util/Spliterators"
@@ -15,7 +16,7 @@
 	lang.Runnable
 }
 
-func (r *Runnable) Run(this lang.Runnable) {
+func (r *Runnable) Run(this gopkg.Runnable) {
 }
 
 type InputStream struct {
diff --git a/bind/testdata/classes.go.golden b/bind/testdata/classes.go.golden
index 2875ff7..fc10f31 100644
--- a/bind/testdata/classes.go.golden
+++ b/bind/testdata/classes.go.golden
@@ -11,7 +11,6 @@
 
 type Java_io_InputStream interface {
 	Read() (int32, error)
-	Super() Java_io_InputStream
 }
 
 type Java_util_concurrent_Future interface {
@@ -20,16 +19,34 @@
 }
 
 type Java_lang_Object interface {
-	Super() Java_lang_Object
 }
 
 type Java_util_concurrent_TimeUnit interface {
-	Super() Java_util_concurrent_TimeUnit
 }
 
 type Java_util_Spliterators interface {
 }
 
+type Go_java_Future interface {
+	Get() (Java_lang_Object, error)
+	Get2(a0 int64, a1 Java_util_concurrent_TimeUnit) (Java_lang_Object, error)
+	Super() Go_java_Future
+}
+
+type Go_java_InputStream interface {
+	Read() (int32, error)
+	Super() Go_java_InputStream
+}
+
+type Go_java_Object interface {
+	Super() Go_java_Object
+}
+
+type Go_java_Runnable interface {
+	Run()
+	Super() Go_java_Runnable
+}
+
 // File is generated by gobind. Do not edit.
 
 package gomobile_bind
@@ -53,6 +70,10 @@
 import "Java/java/lang/Object"
 import "Java/java/util/concurrent/TimeUnit"
 import "Java/java/util/Spliterators"
+import "Java/go/java/Future"
+import "Java/go/java/InputStream"
+import "Java/go/java/Object"
+import "Java/go/java/Runnable"
 import "unsafe"
 
 import "reflect"
@@ -75,6 +96,10 @@
 	init_java_lang_Object()
 	init_java_util_concurrent_TimeUnit()
 	init_java_util_Spliterators()
+	init_go_java_Future()
+	init_go_java_InputStream()
+	init_go_java_Object()
+	init_go_java_Runnable()
 }
 
 var class_java_lang_Runnable C.jclass
@@ -156,27 +181,6 @@
 	return _res, _exc
 }
 
-func (p *proxy_class_java_io_InputStream) Super() Java.Java_io_InputStream {
-	return &super_java_io_InputStream{p}
-}
-
-type super_java_io_InputStream struct {*proxy_class_java_io_InputStream}
-
-func (p *super_java_io_InputStream) Read() (int32, error) {
-	res := C.csuper_java_io_InputStream_read__(C.jint(p.Bind_proxy_refnum__()))
-	_res := int32(res.res)
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
-		}
-	}
-	return _res, _exc
-}
-
 var class_java_util_concurrent_Future C.jclass
 
 func init_java_util_concurrent_Future() {
@@ -278,12 +282,6 @@
 
 func (p *proxy_class_java_lang_Object) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-func (p *proxy_class_java_lang_Object) Super() Java.Java_lang_Object {
-	return &super_java_lang_Object{p}
-}
-
-type super_java_lang_Object struct {*proxy_class_java_lang_Object}
-
 var class_java_util_concurrent_TimeUnit C.jclass
 
 func init_java_util_concurrent_TimeUnit() {
@@ -309,12 +307,6 @@
 
 func (p *proxy_class_java_util_concurrent_TimeUnit) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-func (p *proxy_class_java_util_concurrent_TimeUnit) Super() Java.Java_util_concurrent_TimeUnit {
-	return &super_java_util_concurrent_TimeUnit{p}
-}
-
-type super_java_util_concurrent_TimeUnit struct {*proxy_class_java_util_concurrent_TimeUnit}
-
 var class_java_util_Spliterators C.jclass
 
 func init_java_util_Spliterators() {
@@ -376,6 +368,290 @@
 
 func (p *proxy_class_java_util_Spliterators) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
+var class_go_java_Future C.jclass
+
+func init_go_java_Future() {
+	cls := C.CString("go/java/Future")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_go_java_Future = clazz
+	Future.Cast = func(v interface{}) Java.Go_java_Future {
+		t := reflect.TypeOf((*proxy_class_go_java_Future)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_go_java_Future)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_go_java_Future) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "go.java.Future"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_go_java_Future _seq.Ref
+
+func (p *proxy_class_go_java_Future) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
+func (p *proxy_class_go_java_Future) Get() (Java.Java_lang_Object, error) {
+	res := C.cproxy_go_java_Future_get__(C.jint(p.Bind_proxy_refnum__()))
+	var _res Java.Java_lang_Object
+	_res_ref := _seq.FromRefNum(int32(res.res))
+	if _res_ref != nil {
+		if res.res < 0 { // go object
+			_res = _res_ref.Get().(Java.Java_lang_Object)
+		} else { // foreign object
+			_res = (*proxy_class_java_lang_Object)(_res_ref)
+		}
+	}
+	var _exc error
+	_exc_ref := _seq.FromRefNum(int32(res.exc))
+	if _exc_ref != nil {
+		if res.exc < 0 { // go object
+			_exc = _exc_ref.Get().(error)
+		} else { // foreign object
+			_exc = (*proxy_error)(_exc_ref)
+		}
+	}
+	return _res, _exc
+}
+
+func (p *proxy_class_go_java_Future) Get2(a0 int64, a1 Java.Java_util_concurrent_TimeUnit) (Java.Java_lang_Object, error) {
+	_a0 := C.jlong(a0)
+	var _a1 C.jint = _seq.NullRefNum
+	if a1 != nil {
+		_a1 = C.jint(_seq.ToRefNum(a1))
+	}
+	res := C.cproxy_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2(C.jint(p.Bind_proxy_refnum__()), _a0, _a1)
+	var _res Java.Java_lang_Object
+	_res_ref := _seq.FromRefNum(int32(res.res))
+	if _res_ref != nil {
+		if res.res < 0 { // go object
+			_res = _res_ref.Get().(Java.Java_lang_Object)
+		} else { // foreign object
+			_res = (*proxy_class_java_lang_Object)(_res_ref)
+		}
+	}
+	var _exc error
+	_exc_ref := _seq.FromRefNum(int32(res.exc))
+	if _exc_ref != nil {
+		if res.exc < 0 { // go object
+			_exc = _exc_ref.Get().(error)
+		} else { // foreign object
+			_exc = (*proxy_error)(_exc_ref)
+		}
+	}
+	return _res, _exc
+}
+
+func (p *proxy_class_go_java_Future) Super() Java.Go_java_Future {
+	return &super_go_java_Future{p}
+}
+
+type super_go_java_Future struct {*proxy_class_go_java_Future}
+
+func (p *super_go_java_Future) Get() (Java.Java_lang_Object, error) {
+	res := C.csuper_go_java_Future_get__(C.jint(p.Bind_proxy_refnum__()))
+	var _res Java.Java_lang_Object
+	_res_ref := _seq.FromRefNum(int32(res.res))
+	if _res_ref != nil {
+		if res.res < 0 { // go object
+			_res = _res_ref.Get().(Java.Java_lang_Object)
+		} else { // foreign object
+			_res = (*proxy_class_java_lang_Object)(_res_ref)
+		}
+	}
+	var _exc error
+	_exc_ref := _seq.FromRefNum(int32(res.exc))
+	if _exc_ref != nil {
+		if res.exc < 0 { // go object
+			_exc = _exc_ref.Get().(error)
+		} else { // foreign object
+			_exc = (*proxy_error)(_exc_ref)
+		}
+	}
+	return _res, _exc
+}
+
+func (p *super_go_java_Future) Get2(a0 int64, a1 Java.Java_util_concurrent_TimeUnit) (Java.Java_lang_Object, error) {
+	_a0 := C.jlong(a0)
+	var _a1 C.jint = _seq.NullRefNum
+	if a1 != nil {
+		_a1 = C.jint(_seq.ToRefNum(a1))
+	}
+	res := C.csuper_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2(C.jint(p.Bind_proxy_refnum__()), _a0, _a1)
+	var _res Java.Java_lang_Object
+	_res_ref := _seq.FromRefNum(int32(res.res))
+	if _res_ref != nil {
+		if res.res < 0 { // go object
+			_res = _res_ref.Get().(Java.Java_lang_Object)
+		} else { // foreign object
+			_res = (*proxy_class_java_lang_Object)(_res_ref)
+		}
+	}
+	var _exc error
+	_exc_ref := _seq.FromRefNum(int32(res.exc))
+	if _exc_ref != nil {
+		if res.exc < 0 { // go object
+			_exc = _exc_ref.Get().(error)
+		} else { // foreign object
+			_exc = (*proxy_error)(_exc_ref)
+		}
+	}
+	return _res, _exc
+}
+
+var class_go_java_InputStream C.jclass
+
+func init_go_java_InputStream() {
+	cls := C.CString("go/java/InputStream")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_go_java_InputStream = clazz
+	InputStream.Cast = func(v interface{}) Java.Go_java_InputStream {
+		t := reflect.TypeOf((*proxy_class_go_java_InputStream)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_go_java_InputStream)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_go_java_InputStream) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "go.java.InputStream"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_go_java_InputStream _seq.Ref
+
+func (p *proxy_class_go_java_InputStream) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
+func (p *proxy_class_go_java_InputStream) Read() (int32, error) {
+	res := C.cproxy_go_java_InputStream_read__(C.jint(p.Bind_proxy_refnum__()))
+	_res := int32(res.res)
+	var _exc error
+	_exc_ref := _seq.FromRefNum(int32(res.exc))
+	if _exc_ref != nil {
+		if res.exc < 0 { // go object
+			_exc = _exc_ref.Get().(error)
+		} else { // foreign object
+			_exc = (*proxy_error)(_exc_ref)
+		}
+	}
+	return _res, _exc
+}
+
+func (p *proxy_class_go_java_InputStream) Super() Java.Go_java_InputStream {
+	return &super_go_java_InputStream{p}
+}
+
+type super_go_java_InputStream struct {*proxy_class_go_java_InputStream}
+
+func (p *super_go_java_InputStream) Read() (int32, error) {
+	res := C.csuper_go_java_InputStream_read__(C.jint(p.Bind_proxy_refnum__()))
+	_res := int32(res.res)
+	var _exc error
+	_exc_ref := _seq.FromRefNum(int32(res.exc))
+	if _exc_ref != nil {
+		if res.exc < 0 { // go object
+			_exc = _exc_ref.Get().(error)
+		} else { // foreign object
+			_exc = (*proxy_error)(_exc_ref)
+		}
+	}
+	return _res, _exc
+}
+
+var class_go_java_Object C.jclass
+
+func init_go_java_Object() {
+	cls := C.CString("go/java/Object")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_go_java_Object = clazz
+	Object.Cast = func(v interface{}) Java.Go_java_Object {
+		t := reflect.TypeOf((*proxy_class_go_java_Object)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_go_java_Object)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_go_java_Object) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "go.java.Object"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_go_java_Object _seq.Ref
+
+func (p *proxy_class_go_java_Object) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
+func (p *proxy_class_go_java_Object) Super() Java.Go_java_Object {
+	return &super_go_java_Object{p}
+}
+
+type super_go_java_Object struct {*proxy_class_go_java_Object}
+
+var class_go_java_Runnable C.jclass
+
+func init_go_java_Runnable() {
+	cls := C.CString("go/java/Runnable")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_go_java_Runnable = clazz
+	Runnable.Cast = func(v interface{}) Java.Go_java_Runnable {
+		t := reflect.TypeOf((*proxy_class_go_java_Runnable)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_go_java_Runnable)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_go_java_Runnable) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "go.java.Runnable"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_go_java_Runnable _seq.Ref
+
+func (p *proxy_class_go_java_Runnable) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
+func (p *proxy_class_go_java_Runnable) Run() {
+	res := C.cproxy_go_java_Runnable_run(C.jint(p.Bind_proxy_refnum__()))
+	var _exc error
+	_exc_ref := _seq.FromRefNum(int32(res))
+	if _exc_ref != nil {
+		if res < 0 { // go object
+			_exc = _exc_ref.Get().(error)
+		} else { // foreign object
+			_exc = (*proxy_error)(_exc_ref)
+		}
+	}
+	if (_exc != nil) { panic(_exc) }
+}
+
+func (p *proxy_class_go_java_Runnable) Super() Java.Go_java_Runnable {
+	return &super_go_java_Runnable{p}
+}
+
+type super_go_java_Runnable struct {*proxy_class_go_java_Runnable}
+
+func (p *super_go_java_Runnable) Run() {
+	res := C.csuper_go_java_Runnable_run(C.jint(p.Bind_proxy_refnum__()))
+	var _exc error
+	_exc_ref := _seq.FromRefNum(int32(res))
+	if _exc_ref != nil {
+		if res < 0 { // go object
+			_exc = _exc_ref.Get().(error)
+		} else { // foreign object
+			_exc = (*proxy_error)(_exc_ref)
+		}
+	}
+	if (_exc != nil) { panic(_exc) }
+}
+
 // Package gomobile_bind is an autogenerated binder stub for package java.
 //   gobind -lang=go classes
 //
@@ -392,6 +668,7 @@
 import "C"
 
 import (
+	java_1 "Java/go/java"
 	"Java/java/io"
 	"Java/java/lang"
 	"Java/java/util/concurrent"
@@ -572,13 +849,13 @@
 func proxyjava_Runnable_Run(refnum C.int32_t, param_this C.int32_t) {
 	ref := _seq.FromRefNum(int32(refnum))
 	v := ref.Get().(*java.Runnable)
-	var _param_this lang.Runnable
+	var _param_this java_1.Runnable
 	_param_this_ref := _seq.FromRefNum(int32(param_this))
 	if _param_this_ref != nil {
 		if param_this < 0 { // go object
-			_param_this = _param_this_ref.Get().(lang.Runnable)
+			_param_this = _param_this_ref.Get().(java_1.Runnable)
 		} else { // foreign object
-			_param_this = (*proxy_class_java_lang_Runnable)(_param_this_ref)
+			_param_this = (*proxy_class_go_java_Runnable)(_param_this_ref)
 		}
 	}
 	v.Run(_param_this)
diff --git a/bind/testdata/classes.java.c.golden b/bind/testdata/classes.java.c.golden
index 013041d..2e7f7d8 100644
--- a/bind/testdata/classes.java.c.golden
+++ b/bind/testdata/classes.java.c.golden
@@ -29,9 +29,25 @@
 }
 
 static jclass class_java_util_Spliterators;
+static jclass class_go_java_Future;
+static jclass sclass_go_java_Future;
+static jmethodID m_go_java_Future_get__;
+static jmethodID sm_go_java_Future_get__;
+static jmethodID m_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2;
+static jmethodID sm_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2;
+static jclass class_go_java_InputStream;
+static jclass sclass_go_java_InputStream;
+static jmethodID m_go_java_InputStream_read__;
+static jmethodID sm_go_java_InputStream_read__;
+static jclass class_go_java_Object;
+static jclass sclass_go_java_Object;
+static jclass class_go_java_Runnable;
+static jclass sclass_go_java_Runnable;
+static jmethodID m_go_java_Runnable_run;
+static jmethodID sm_go_java_Runnable_run;
 
 void init_proxies() {
-	JNIEnv *env = go_seq_push_local_frame(6);
+	JNIEnv *env = go_seq_push_local_frame(10);
 	jclass clazz;
 	clazz = (*env)->FindClass(env, "java/lang/Runnable");
 	class_java_lang_Runnable = (*env)->NewGlobalRef(env, clazz);
@@ -49,6 +65,30 @@
 	class_java_util_concurrent_TimeUnit = (*env)->NewGlobalRef(env, clazz);
 	clazz = (*env)->FindClass(env, "java/util/Spliterators");
 	class_java_util_Spliterators = (*env)->NewGlobalRef(env, clazz);
+	clazz = (*env)->FindClass(env, "go/java/Future");
+	class_go_java_Future = (*env)->NewGlobalRef(env, clazz);
+	sclass_go_java_Future = (*env)->GetSuperclass(env, clazz);
+	sclass_go_java_Future = (*env)->NewGlobalRef(env, sclass_go_java_Future);
+	m_go_java_Future_get__ = go_seq_get_method_id(clazz, "get", "()Ljava/lang/Object;");
+	sm_go_java_Future_get__ = go_seq_get_method_id(sclass_go_java_Future, "get", "()Ljava/lang/Object;");
+	m_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(clazz, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
+	sm_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(sclass_go_java_Future, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
+	clazz = (*env)->FindClass(env, "go/java/InputStream");
+	class_go_java_InputStream = (*env)->NewGlobalRef(env, clazz);
+	sclass_go_java_InputStream = (*env)->GetSuperclass(env, clazz);
+	sclass_go_java_InputStream = (*env)->NewGlobalRef(env, sclass_go_java_InputStream);
+	m_go_java_InputStream_read__ = go_seq_get_method_id(clazz, "read", "()I");
+	sm_go_java_InputStream_read__ = go_seq_get_method_id(sclass_go_java_InputStream, "read", "()I");
+	clazz = (*env)->FindClass(env, "go/java/Object");
+	class_go_java_Object = (*env)->NewGlobalRef(env, clazz);
+	sclass_go_java_Object = (*env)->GetSuperclass(env, clazz);
+	sclass_go_java_Object = (*env)->NewGlobalRef(env, sclass_go_java_Object);
+	clazz = (*env)->FindClass(env, "go/java/Runnable");
+	class_go_java_Runnable = (*env)->NewGlobalRef(env, clazz);
+	sclass_go_java_Runnable = (*env)->GetSuperclass(env, clazz);
+	sclass_go_java_Runnable = (*env)->NewGlobalRef(env, sclass_go_java_Runnable);
+	m_go_java_Runnable_run = go_seq_get_method_id(clazz, "run", "()V");
+	sm_go_java_Runnable_run = go_seq_get_method_id(sclass_go_java_Runnable, "run", "()V");
 	go_seq_pop_local_frame(env);
 }
 
@@ -79,22 +119,6 @@
 	return __res;
 }
 
-ret_jint csuper_java_io_InputStream_read__(jint this) {
-	JNIEnv *env = go_seq_push_local_frame(1);
-	// Must be a Java object
-	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
-	jint res = (*env)->CallNonvirtualIntMethod(env, _this, class_java_io_InputStream, m_java_io_InputStream_read__);
-	jobject _exc = go_seq_get_exception(env);
-	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
-	if (_exc != NULL) {
-		res = 0;
-	}
-	jint _res = res;
-	go_seq_pop_local_frame(env);
-	ret_jint __res = {_res, _exc_ref};
-	return __res;
-}
-
 ret_jint cproxy_java_util_concurrent_Future_get__(jint this) {
 	JNIEnv *env = go_seq_push_local_frame(1);
 	// Must be a Java object
@@ -129,6 +153,128 @@
 	return __res;
 }
 
+ret_jint cproxy_go_java_Future_get__(jint this) {
+	JNIEnv *env = go_seq_push_local_frame(1);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jobject res = (*env)->CallObjectMethod(env, _this, m_go_java_Future_get__);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = NULL;
+	}
+	jint _res = go_seq_to_refnum(env, res);
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint csuper_go_java_Future_get__(jint this) {
+	JNIEnv *env = go_seq_push_local_frame(1);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jobject res = (*env)->CallNonvirtualObjectMethod(env, _this, sclass_go_java_Future, sm_go_java_Future_get__);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = NULL;
+	}
+	jint _res = go_seq_to_refnum(env, res);
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint cproxy_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2(jint this, jlong a0, jint a1) {
+	JNIEnv *env = go_seq_push_local_frame(3);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jlong _a0 = a0;
+	jobject _a1 = go_seq_from_refnum(env, a1, NULL, NULL);
+	jobject res = (*env)->CallObjectMethod(env, _this, m_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2, _a0, _a1);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = NULL;
+	}
+	jint _res = go_seq_to_refnum(env, res);
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint csuper_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2(jint this, jlong a0, jint a1) {
+	JNIEnv *env = go_seq_push_local_frame(3);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jlong _a0 = a0;
+	jobject _a1 = go_seq_from_refnum(env, a1, NULL, NULL);
+	jobject res = (*env)->CallNonvirtualObjectMethod(env, _this, sclass_go_java_Future, sm_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2, _a0, _a1);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = NULL;
+	}
+	jint _res = go_seq_to_refnum(env, res);
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint cproxy_go_java_InputStream_read__(jint this) {
+	JNIEnv *env = go_seq_push_local_frame(1);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jint res = (*env)->CallIntMethod(env, _this, m_go_java_InputStream_read__);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = 0;
+	}
+	jint _res = res;
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint csuper_go_java_InputStream_read__(jint this) {
+	JNIEnv *env = go_seq_push_local_frame(1);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jint res = (*env)->CallNonvirtualIntMethod(env, _this, sclass_go_java_InputStream, sm_go_java_InputStream_read__);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = 0;
+	}
+	jint _res = res;
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+jint cproxy_go_java_Runnable_run(jint this) {
+	JNIEnv *env = go_seq_push_local_frame(1);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	(*env)->CallVoidMethod(env, _this, m_go_java_Runnable_run);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	go_seq_pop_local_frame(env);
+	return _exc_ref;
+}
+
+jint csuper_go_java_Runnable_run(jint this) {
+	JNIEnv *env = go_seq_push_local_frame(1);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	(*env)->CallNonvirtualVoidMethod(env, _this, sclass_go_java_Runnable, sm_go_java_Runnable_run);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	go_seq_pop_local_frame(env);
+	return _exc_ref;
+}
+
 // JNI functions for the Go <=> Java bridge.
 //   gobind -lang=java classes
 //
diff --git a/bind/testdata/classes.java.h.golden b/bind/testdata/classes.java.h.golden
index 173d7c2..a110ec3 100644
--- a/bind/testdata/classes.java.h.golden
+++ b/bind/testdata/classes.java.h.golden
@@ -48,9 +48,16 @@
 
 extern jint cproxy_java_lang_Runnable_run(jint this);
 extern ret_jint cproxy_java_io_InputStream_read__(jint this);
-extern ret_jint csuper_java_io_InputStream_read__(jint this);
 extern ret_jint cproxy_java_util_concurrent_Future_get__(jint this);
 extern ret_jint cproxy_java_util_concurrent_Future_get__JLjava_util_concurrent_TimeUnit_2(jint this, jlong a0, jint a1);
+extern ret_jint cproxy_go_java_Future_get__(jint this);
+extern ret_jint csuper_go_java_Future_get__(jint this);
+extern ret_jint cproxy_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2(jint this, jlong a0, jint a1);
+extern ret_jint csuper_go_java_Future_get__JLjava_util_concurrent_TimeUnit_2(jint this, jlong a0, jint a1);
+extern ret_jint cproxy_go_java_InputStream_read__(jint this);
+extern ret_jint csuper_go_java_InputStream_read__(jint this);
+extern jint cproxy_go_java_Runnable_run(jint this);
+extern jint csuper_go_java_Runnable_run(jint this);
 extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2(jclass clazz, jmethodID m, jint a0);
 // JNI function headers for the Go <=> Java bridge.
 //   gobind -lang=java classes
diff --git a/bind/testdata/java.go.golden b/bind/testdata/java.go.golden
index 0ba36a4..d40fa3f 100644
--- a/bind/testdata/java.go.golden
+++ b/bind/testdata/java.go.golden
@@ -9,7 +9,6 @@
 }
 
 type Java_lang_Object interface {
-	Super() Java_lang_Object
 }
 
 type Java_lang_Runnable interface {
@@ -19,7 +18,6 @@
 }
 
 type Java_lang_Character_Subset interface {
-	Super() Java_lang_Character_Subset
 }
 
 // File is generated by gobind. Do not edit.
@@ -117,12 +115,6 @@
 
 func (p *proxy_class_java_lang_Object) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-func (p *proxy_class_java_lang_Object) Super() Java.Java_lang_Object {
-	return &super_java_lang_Object{p}
-}
-
-type super_java_lang_Object struct {*proxy_class_java_lang_Object}
-
 var class_java_lang_Runnable C.jclass
 
 func init_java_lang_Runnable() {
@@ -198,12 +190,6 @@
 
 func (p *proxy_class_java_lang_Character_Subset) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-func (p *proxy_class_java_lang_Character_Subset) Super() Java.Java_lang_Character_Subset {
-	return &super_java_lang_Character_Subset{p}
-}
-
-type super_java_lang_Character_Subset struct {*proxy_class_java_lang_Character_Subset}
-
 // Package gomobile_bind is an autogenerated binder stub for package java.
 //   gobind -lang=go java
 //
@@ -230,21 +216,14 @@
 
 func (p *proxyjava_F) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-// skipped method O.Super with unsupported parameter or return types
-
 type proxyjava_O _seq.Ref
 
 func (p *proxyjava_O) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-// skipped method O.Super with unsupported parameter or result types
 type proxyjava_R _seq.Ref
 
 func (p *proxyjava_R) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-// skipped method S.Super with unsupported parameter or return types
-
 type proxyjava_S _seq.Ref
 
 func (p *proxyjava_S) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
-
-// skipped method S.Super with unsupported parameter or result types
diff --git a/bind/testdata/java.java.c.golden b/bind/testdata/java.java.c.golden
index 814478f..26b7f24 100644
--- a/bind/testdata/java.java.c.golden
+++ b/bind/testdata/java.java.c.golden
@@ -41,14 +41,10 @@
 jmethodID proxy_class_java_F_cons;
 jclass proxy_class_java_O;
 jmethodID proxy_class_java_O_cons;
-// skipped method O.Super with unsupported parameter or return types
-
 jclass proxy_class_java_R;
 jmethodID proxy_class_java_R_cons;
 jclass proxy_class_java_S;
 jmethodID proxy_class_java_S_cons;
-// skipped method S.Super with unsupported parameter or return types
-
 
 JNIEXPORT void JNICALL
 Java_go_java_Java__1init(JNIEnv *env, jclass _unused) {
@@ -62,8 +58,6 @@
     proxy_class_java_O = (*env)->NewGlobalRef(env, clazz);
     proxy_class_java_O_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
     clazz = (*env)->FindClass(env, "go/java/O");
-    // skipped method O.Super with unsupported parameter or return types
-    
     
     clazz = (*env)->FindClass(env, "go/java/Java$proxyR");
     proxy_class_java_R = (*env)->NewGlobalRef(env, clazz);
@@ -74,16 +68,6 @@
     proxy_class_java_S = (*env)->NewGlobalRef(env, clazz);
     proxy_class_java_S_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
     clazz = (*env)->FindClass(env, "go/java/S");
-    // skipped method S.Super with unsupported parameter or return types
-    
     
 }
 
-// skipped function O.Super with unsupported parameter or return types
-
-// skipped method O with unsupported parameter or return types
-
-// skipped function S.Super with unsupported parameter or return types
-
-// skipped method S with unsupported parameter or return types
-
diff --git a/bind/testdata/java.java.golden b/bind/testdata/java.java.golden
index 74d9694..7a01da8 100644
--- a/bind/testdata/java.java.golden
+++ b/bind/testdata/java.java.golden
@@ -18,9 +18,7 @@
 
 import go.Seq;
 
-public interface O extends F, R {
-    // skipped method O.Super with unsupported parameter or return types
-    
+public interface O {
     
 }
 
@@ -44,9 +42,7 @@
 
 import go.Seq;
 
-public interface S extends F, R {
-    // skipped method S.Super with unsupported parameter or return types
-    
+public interface S {
     
 }
 
@@ -94,8 +90,6 @@
         
         proxyO(Seq.Ref ref) { this.ref = ref; }
         
-        // skipped method O.Super with unsupported parameter or return types
-        
     }
     private static final class proxyR implements Seq.Proxy, R {
         private final Seq.Ref ref;
@@ -120,8 +114,6 @@
         
         proxyS(Seq.Ref ref) { this.ref = ref; }
         
-        // skipped method S.Super with unsupported parameter or return types
-        
     }
     
     
diff --git a/bind/testdata/java.java.h.golden b/bind/testdata/java.java.h.golden
index 03bbe6f..a95fe25 100644
--- a/bind/testdata/java.java.h.golden
+++ b/bind/testdata/java.java.h.golden
@@ -62,14 +62,10 @@
 extern jclass proxy_class_java_O;
 extern jmethodID proxy_class_java_O_cons;
 
-// skipped method O.Super with unsupported parameter or return types
-
 extern jclass proxy_class_java_R;
 extern jmethodID proxy_class_java_R_cons;
 
 extern jclass proxy_class_java_S;
 extern jmethodID proxy_class_java_S_cons;
 
-// skipped method S.Super with unsupported parameter or return types
-
 #endif
diff --git a/bind/testpkg/javapkg/classes.go b/bind/testpkg/javapkg/classes.go
index 7043c5f..4c79199 100644
--- a/bind/testpkg/javapkg/classes.go
+++ b/bind/testpkg/javapkg/classes.go
@@ -7,6 +7,7 @@
 package javapkg
 
 import (
+	gopkg "Java/go/javapkg"
 	"Java/java/beans"
 	"Java/java/io"
 	"Java/java/io/IOException"
@@ -34,14 +35,14 @@
 	Field string
 }
 
-func (r *GoRunnable) ToString(this lang.Runnable) string {
+func (r *GoRunnable) ToString(this gopkg.GoRunnable) string {
 	return ToStringPrefix
 }
 
-func (r *GoRunnable) Run(this lang.Runnable) {
+func (r *GoRunnable) Run(this gopkg.GoRunnable) {
 }
 
-func (r *GoRunnable) GetThis(this lang.Runnable) lang.Runnable {
+func (r *GoRunnable) GetThis(this gopkg.GoRunnable) lang.Runnable {
 	return this
 }
 
@@ -86,7 +87,7 @@
 	this lang.Object
 }
 
-func (o *GoObject) ToString(this lang.Object) string {
+func (o *GoObject) ToString(this gopkg.GoObject) string {
 	o.this = this
 	return ToStringPrefix + this.Super().ToString()
 }
@@ -171,7 +172,7 @@
 	util.Random
 }
 
-func (_ *GoRand) Next(this util.Random, i int32) int32 {
+func (_ *GoRand) Next(this gopkg.GoRand, i int32) int32 {
 	return this.Super().Next(i)
 }
 
diff --git a/cmd/gobind/gen.go b/cmd/gobind/gen.go
index 8325b84..b8215d0 100644
--- a/cmd/gobind/gen.go
+++ b/cmd/gobind/gen.go
@@ -140,7 +140,7 @@
 	}
 }
 
-func genJavaPackages(ctx *build.Context, dir string, classes []*java.Class) error {
+func genJavaPackages(ctx *build.Context, dir string, classes []*java.Class, genNames []string) error {
 	var buf bytes.Buffer
 	cg := &bind.ClassGen{
 		Printer: &bind.Printer{
@@ -148,7 +148,7 @@
 			Buf:        &buf,
 		},
 	}
-	cg.Init(classes)
+	cg.Init(classes, genNames)
 	pkgBase := filepath.Join(dir, "src", "Java")
 	if err := os.MkdirAll(pkgBase, 0700); err != nil {
 		return err
diff --git a/cmd/gobind/main.go b/cmd/gobind/main.go
index ef6e808..86f02d3 100644
--- a/cmd/gobind/main.go
+++ b/cmd/gobind/main.go
@@ -60,7 +60,19 @@
 		log.Fatal(err)
 	}
 	if len(refs.Refs) > 0 {
-		classes, err = java.Import(*bootclasspath, *classpath, refs)
+		pref := *javaPkg
+		if pref == "" {
+			pref = "go"
+		}
+		if pref != "" {
+			pref = pref + "."
+		}
+		imp := &java.Importer{
+			Bootclasspath: *bootclasspath,
+			Classpath:     *classpath,
+			JavaPkgPrefix: pref,
+		}
+		classes, err = imp.Import(refs)
 		if err != nil {
 			log.Fatal(err)
 		}
@@ -70,7 +82,11 @@
 				log.Fatal(err)
 			}
 			defer os.RemoveAll(tmpGopath)
-			if err := genJavaPackages(ctx, tmpGopath, classes); err != nil {
+			var genNames []string
+			for _, emb := range refs.Embedders {
+				genNames = append(genNames, pref+emb.Pkg+"."+emb.Name)
+			}
+			if err := genJavaPackages(ctx, tmpGopath, classes, genNames); err != nil {
 				log.Fatal(err)
 			}
 			gopath := ctx.GOPATH
diff --git a/cmd/gomobile/bind.go b/cmd/gomobile/bind.go
index a3202f7..9bef68a 100644
--- a/cmd/gomobile/bind.go
+++ b/cmd/gomobile/bind.go
@@ -391,7 +391,19 @@
 	if err != nil {
 		return nil, err
 	}
-	classes, err := java.Import(bClspath, bindClasspath, refs)
+	pref := bindJavaPkg
+	if pref == "" {
+		pref = "go"
+	}
+	if pref != "" {
+		pref = pref + "."
+	}
+	imp := &java.Importer{
+		Bootclasspath: bClspath,
+		Classpath:     bindClasspath,
+		JavaPkgPrefix: pref,
+	}
+	classes, err := imp.Import(refs)
 	if err != nil {
 		return nil, err
 	}
@@ -402,7 +414,11 @@
 			Buf:        &buf,
 		},
 	}
-	g.Init(classes)
+	var genNames []string
+	for _, emb := range refs.Embedders {
+		genNames = append(genNames, pref+emb.Pkg+"."+emb.Name)
+	}
+	g.Init(classes, genNames)
 	for i, jpkg := range g.Packages() {
 		pkgDir := filepath.Join(jpkgSrc, "src", "Java", jpkg)
 		if err := os.MkdirAll(pkgDir, 0700); err != nil {
diff --git a/example/reverse/reverse/reverse.go b/example/reverse/reverse/reverse.go
index c4c3377..a07d834 100644
--- a/example/reverse/reverse/reverse.go
+++ b/example/reverse/reverse/reverse.go
@@ -9,6 +9,7 @@
 	"Java/android/databinding/DataBindingUtil"
 	"Java/android/os"
 	"Java/android/support/v7/app"
+	gopkg "Java/go/reverse"
 	rlayout "Java/go/reverse/R/layout"
 	"Java/go/reverse/databinding/ActivityMainBinding"
 )
@@ -17,7 +18,7 @@
 	app.AppCompatActivity
 }
 
-func (a *MainActivity) OnCreate1(this app.AppCompatActivity, b os.Bundle) {
+func (a *MainActivity) OnCreate1(this gopkg.MainActivity, b os.Bundle) {
 	this.Super().OnCreate1(b)
 	db := DataBindingUtil.SetContentView2(this, rlayout.Activity_main)
 	mainBind := ActivityMainBinding.Cast(db)
diff --git a/internal/importers/java/java.go b/internal/importers/java/java.go
index a84a673..890ec46 100644
--- a/internal/importers/java/java.go
+++ b/internal/importers/java/java.go
@@ -93,10 +93,13 @@
 
 type TypeKind int
 
-type importer struct {
-	bootclspath string
-	clspath     string
-	clsMap      map[string]*Class
+type Importer struct {
+	Bootclasspath string
+	Classpath     string
+	// JavaPkgPrefix is java package name for generated classes.
+	JavaPkgPrefix string
+
+	clsMap map[string]*Class
 }
 
 const (
@@ -148,11 +151,9 @@
 //   ...
 //
 // }
-func Import(bootclasspath, classpath string, refs *importers.References) ([]*Class, error) {
-	imp := &importer{
-		bootclspath: bootclasspath,
-		clspath:     classpath,
-		clsMap:      make(map[string]*Class),
+func (j *Importer) Import(refs *importers.References) ([]*Class, error) {
+	if j.clsMap == nil {
+		j.clsMap = make(map[string]*Class)
 	}
 	clsSet := make(map[string]struct{})
 	var names []string
@@ -166,19 +167,44 @@
 			}
 		}
 	}
-	classes, err := imp.importClasses(names, true)
+	classes, err := j.importClasses(names, true)
 	if err != nil {
 		return nil, err
 	}
-	for _, cls := range classes {
-		imp.fillAllMethods(cls)
+	// Embedders refer to every exported Go struct that will have its class
+	// generated. Allow Go code to reverse bind to those classes by synthesizing
+	// their class descriptors.
+	for _, emb := range refs.Embedders {
+		n := j.JavaPkgPrefix + emb.Pkg + "." + emb.Name
+		if _, exists := j.clsMap[n]; exists {
+			continue
+		}
+		cls := &Class{
+			Name:     n,
+			FindName: n,
+			JNIName:  JNIMangle(n),
+			PkgName:  emb.Name,
+		}
+		for _, ref := range emb.Refs {
+			jpkg := strings.Replace(ref.Pkg, "/", ".", -1)
+			super := jpkg + "." + ref.Name
+			if _, exists := j.clsMap[super]; !exists {
+				return nil, fmt.Errorf("class %q not found", super)
+			}
+			cls.Supers = append(cls.Supers, super)
+		}
+		classes = append(classes, cls)
+		j.clsMap[cls.Name] = cls
 	}
-	imp.fillThrowables()
 	for _, cls := range classes {
-		imp.mangleOverloads(cls.AllMethods)
-		imp.mangleOverloads(cls.Funcs)
+		j.fillAllMethods(cls)
 	}
-	imp.filterReferences(classes, refs)
+	j.fillThrowables()
+	for _, cls := range classes {
+		j.mangleOverloads(cls.AllMethods)
+		j.mangleOverloads(cls.Funcs)
+	}
+	j.filterReferences(classes, refs)
 	return classes, nil
 }
 
@@ -211,10 +237,6 @@
 	return string(m)
 }
 
-func (c *Class) HasSuper() bool {
-	return !c.Final && !c.Interface
-}
-
 func (t *Type) Type() string {
 	switch t.Kind {
 	case Int:
@@ -316,7 +338,7 @@
 	}
 }
 
-func (j *importer) filterReferences(classes []*Class, refs *importers.References) {
+func (j *Importer) filterReferences(classes []*Class, refs *importers.References) {
 	refFuncs := make(map[[2]string]struct{})
 	for _, ref := range refs.Refs {
 		pkgName := strings.Replace(ref.Pkg, "/", ".", -1)
@@ -351,16 +373,16 @@
 	}
 }
 
-func (j *importer) importClasses(names []string, allowMissingClasses bool) ([]*Class, error) {
+func (j *Importer) importClasses(names []string, allowMissingClasses bool) ([]*Class, error) {
 	if len(names) == 0 {
 		return nil, nil
 	}
 	args := []string{"-J-Duser.language=en", "-s", "-protected", "-constants"}
-	if j.clspath != "" {
-		args = append(args, "-classpath", j.clspath)
+	if j.Classpath != "" {
+		args = append(args, "-classpath", j.Classpath)
 	}
-	if j.bootclspath != "" {
-		args = append(args, "-bootclasspath", j.bootclspath)
+	if j.Bootclasspath != "" {
+		args = append(args, "-bootclasspath", j.Bootclasspath)
 	}
 	args = append(args, names...)
 	javapPath, err := javapPath()
@@ -411,7 +433,7 @@
 	return classes, nil
 }
 
-func (j *importer) unknownSuperClasses(cls *Class, unk []string) []string {
+func (j *Importer) unknownSuperClasses(cls *Class, unk []string) []string {
 loop:
 	for _, n := range cls.Supers {
 		if s, exists := j.clsMap[n]; exists {
@@ -428,7 +450,7 @@
 	return unk
 }
 
-func (j *importer) scanClass(s *bufio.Scanner, name string) (*Class, error) {
+func (j *Importer) scanClass(s *bufio.Scanner, name string) (*Class, error) {
 	if !s.Scan() {
 		return nil, fmt.Errorf("%s: missing javap header", name)
 	}
@@ -544,7 +566,7 @@
 	return cls, nil
 }
 
-func (j *importer) scanClassDecl(name string, decl string) (*Class, error) {
+func (j *Importer) scanClassDecl(name string, decl string) (*Class, error) {
 	cls := &Class{
 		Name: name,
 	}
@@ -633,7 +655,7 @@
 	return nil, fmt.Errorf("missing ending { in class declaration: %q", decl)
 }
 
-func (j *importer) scanVar(decl, desc string) (*Var, error) {
+func (j *Importer) scanVar(decl, desc string) (*Var, error) {
 	v := new(Var)
 	const eq = " = "
 	idx := strings.Index(decl, eq)
@@ -664,7 +686,7 @@
 	return v, nil
 }
 
-func (j *importer) scanMethod(decl, desc string, parenIdx int) (*Func, error) {
+func (j *Importer) scanMethod(decl, desc string, parenIdx int) (*Func, error) {
 	// Member is a method
 	f := new(Func)
 	f.Desc = desc
@@ -705,7 +727,7 @@
 	return f, nil
 }
 
-func (j *importer) fillThrowables() {
+func (j *Importer) fillThrowables() {
 	thrCls, ok := j.clsMap["java.lang.Throwable"]
 	if !ok {
 		// If Throwable isn't in the class map
@@ -717,7 +739,7 @@
 	}
 }
 
-func (j *importer) fillThrowableFor(cls, thrCls *Class) {
+func (j *Importer) fillThrowableFor(cls, thrCls *Class) {
 	if cls.Interface || cls.Throwable {
 		return
 	}
@@ -729,7 +751,7 @@
 	}
 }
 
-func (j *importer) fillAllMethods(cls *Class) {
+func (j *Importer) fillAllMethods(cls *Class) {
 	if len(cls.AllMethods) > 0 {
 		return
 	}
@@ -764,7 +786,7 @@
 // mangleOverloads assigns unique names to overloaded Java functions by appending
 // the argument count. If multiple methods have the same name and argument count,
 // the method signature is appended in JNI mangled form.
-func (j *importer) mangleOverloads(allFuncs []*Func) {
+func (j *Importer) mangleOverloads(allFuncs []*Func) {
 	overloads := make(map[string][]*Func)
 	for _, f := range allFuncs {
 		overloads[f.Name] = append(overloads[f.Name], f)
@@ -795,7 +817,7 @@
 	}
 }
 
-func (j *importer) parseJavaValue(v string) (string, bool) {
+func (j *Importer) parseJavaValue(v string) (string, bool) {
 	v = strings.TrimRight(v, "df")
 	switch v {
 	case "", "NaN", "Infinity", "-Infinity":
@@ -810,7 +832,7 @@
 	}
 }
 
-func (j *importer) parseJavaType(desc string) (*Type, int, error) {
+func (j *Importer) parseJavaType(desc string) (*Type, int, error) {
 	t := new(Type)
 	var n int
 	if desc == "" {
diff --git a/internal/importers/java/java_test.go b/internal/importers/java/java_test.go
index 7cd4c86..c970a5a 100644
--- a/internal/importers/java/java_test.go
+++ b/internal/importers/java/java_test.go
@@ -43,7 +43,7 @@
 		for _, m := range test.methods {
 			refs.Names[m.GoName] = struct{}{}
 		}
-		classes, err := Import("", "", refs)
+		classes, err := (&Importer{}).Import(refs)
 		if err != nil {
 			t.Fatal(err)
 		}