bind: support underscores in identifiers

Fixes golang/go#18536

Change-Id: I82c5993547e4d1d0df14726ccc569e1f57128072
Reviewed-on: https://go-review.googlesource.com/101156
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/bind/bind_test.go b/bind/bind_test.go
index 0635bca..02bfe6d 100644
--- a/bind/bind_test.go
+++ b/bind/bind_test.go
@@ -44,6 +44,7 @@
 	"testdata/vars.go",
 	"testdata/ignore.go",
 	"testdata/doc.go",
+	"testdata/underscores.go",
 }
 
 var javaTests = []string{
diff --git a/bind/genjava.go b/bind/genjava.go
index 8991466..970dc81 100644
--- a/bind/genjava.go
+++ b/bind/genjava.go
@@ -749,10 +749,7 @@
 		g.Printf(jm.JNIName)
 	} else {
 		oName := javaNameReplacer(lowerFirst(o.Name()))
-		if strings.HasSuffix(oName, "_") {
-			oName += "1" // JNI doesn't like methods ending with underscore, needs the _1 suffixing
-		}
-		g.Printf(oName)
+		g.Printf(java.JNIMangle(oName))
 	}
 	g.Printf("(JNIEnv* env, ")
 	if sName != "" {
@@ -777,7 +774,7 @@
 }
 
 func (g *JavaGen) jniPkgName() string {
-	return strings.Replace(g.javaPkgName(g.Pkg), ".", "_", -1)
+	return strings.Replace(java.JNIMangle(g.javaPkgName(g.Pkg)), ".", "_", -1)
 }
 
 var javaLetterDigitRE = regexp.MustCompile(`[0-9a-zA-Z$_]`)
@@ -1064,7 +1061,7 @@
 	n := java.JNIMangle(g.javaTypeName(o.Name()))
 	// setter
 	g.Printf("JNIEXPORT void JNICALL\n")
-	g.Printf("Java_%s_%s_set%s(JNIEnv *env, jobject this, %s v) {\n", g.jniPkgName(), n, f.Name(), g.jniType(f.Type()))
+	g.Printf("Java_%s_%s_set%s(JNIEnv *env, jobject this, %s v) {\n", g.jniPkgName(), n, java.JNIMangle(f.Name()), g.jniType(f.Type()))
 	g.Indent()
 	g.Printf("int32_t o = go_seq_to_refnum_go(env, this);\n")
 	g.genJavaToC("v", f.Type(), modeRetained)
@@ -1075,7 +1072,7 @@
 
 	// getter
 	g.Printf("JNIEXPORT %s JNICALL\n", g.jniType(f.Type()))
-	g.Printf("Java_%s_%s_get%s(JNIEnv *env, jobject this) {\n", g.jniPkgName(), n, f.Name())
+	g.Printf("Java_%s_%s_get%s(JNIEnv *env, jobject this) {\n", g.jniPkgName(), n, java.JNIMangle(f.Name()))
 	g.Indent()
 	g.Printf("int32_t o = go_seq_to_refnum_go(env, this);\n")
 	g.Printf("%s r0 = ", g.cgoType(f.Type()))
@@ -1471,7 +1468,7 @@
 	}
 	g.Printf("\n")
 	g.Printf("JNIEXPORT void JNICALL\n")
-	g.Printf("Java_%s_%s__1init(JNIEnv *env, jclass _unused) {\n", g.jniPkgName(), g.className())
+	g.Printf("Java_%s_%s__1init(JNIEnv *env, jclass _unused) {\n", g.jniPkgName(), java.JNIMangle(g.className()))
 	g.Indent()
 	g.Printf("jclass clazz;\n")
 	for _, s := range g.structs {
diff --git a/bind/testdata/try.java.c.golden b/bind/testdata/try.java.c.golden
index 232822f..54023f5 100644
--- a/bind/testdata/try.java.c.golden
+++ b/bind/testdata/try.java.c.golden
@@ -11,12 +11,12 @@
 
 
 JNIEXPORT void JNICALL
-Java_try__Try__1init(JNIEnv *env, jclass _unused) {
+Java_try_1_Try__1init(JNIEnv *env, jclass _unused) {
     jclass clazz;
 }
 
 JNIEXPORT jstring JNICALL
-Java_try__Try_this_1(JNIEnv* env, jclass _clazz) {
+Java_try_1_Try_this_1(JNIEnv* env, jclass _clazz) {
     nstring r0 = proxytry__This();
     jstring _r0 = go_seq_to_java_string(env, r0);
     return _r0;
diff --git a/bind/testdata/underscores.go b/bind/testdata/underscores.go
new file mode 100644
index 0000000..e13f25d
--- /dev/null
+++ b/bind/testdata/underscores.go
@@ -0,0 +1,13 @@
+// Copyright 2018 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 underscore_pkg
+
+type Underscore_struct struct {
+	Underscore_field string
+}
+
+var Underscore_var int
+
+func Underscore_func() {}
diff --git a/bind/testdata/underscores.go.golden b/bind/testdata/underscores.go.golden
new file mode 100644
index 0000000..5fcf4be
--- /dev/null
+++ b/bind/testdata/underscores.go.golden
@@ -0,0 +1,60 @@
+// Package main is an autogenerated binder stub for package underscore_pkg.
+//   gobind -lang=go underscores
+//
+// File is generated by gobind. Do not edit.
+package main
+
+/*
+#include <stdlib.h>
+#include <stdint.h>
+#include "seq.h"
+#include "underscore_pkg.h"
+
+*/
+import "C"
+
+import (
+	_seq "golang.org/x/mobile/bind/seq"
+	"underscores"
+)
+
+// suppress the error if seq ends up unused
+var _ = _seq.FromRefNum
+
+//export proxyunderscore_pkg_Underscore_struct_Underscore_field_Set
+func proxyunderscore_pkg_Underscore_struct_Underscore_field_Set(refnum C.int32_t, v C.nstring) {
+	ref := _seq.FromRefNum(int32(refnum))
+	_v := decodeString(v)
+	ref.Get().(*underscore_pkg.Underscore_struct).Underscore_field = _v
+}
+
+//export proxyunderscore_pkg_Underscore_struct_Underscore_field_Get
+func proxyunderscore_pkg_Underscore_struct_Underscore_field_Get(refnum C.int32_t) C.nstring {
+	ref := _seq.FromRefNum(int32(refnum))
+	v := ref.Get().(*underscore_pkg.Underscore_struct).Underscore_field
+	_v := encodeString(v)
+	return _v
+}
+
+//export new_underscore_pkg_Underscore_struct
+func new_underscore_pkg_Underscore_struct() C.int32_t {
+	return C.int32_t(_seq.ToRefNum(new(underscore_pkg.Underscore_struct)))
+}
+
+//export var_setunderscore_pkg_Underscore_var
+func var_setunderscore_pkg_Underscore_var(v C.nint) {
+	_v := int(v)
+	underscore_pkg.Underscore_var = _v
+}
+
+//export var_getunderscore_pkg_Underscore_var
+func var_getunderscore_pkg_Underscore_var() C.nint {
+	v := underscore_pkg.Underscore_var
+	_v := C.nint(v)
+	return _v
+}
+
+//export proxyunderscore_pkg__Underscore_func
+func proxyunderscore_pkg__Underscore_func() {
+	underscore_pkg.Underscore_func()
+}
diff --git a/bind/testdata/underscores.java.c.golden b/bind/testdata/underscores.java.c.golden
new file mode 100644
index 0000000..a9d18f7
--- /dev/null
+++ b/bind/testdata/underscores.java.c.golden
@@ -0,0 +1,61 @@
+// JNI functions for the Go <=> Java bridge.
+//   gobind -lang=java underscores
+//
+// File is generated by gobind. Do not edit.
+
+#include <android/log.h>
+#include <stdint.h>
+#include "seq.h"
+#include "_cgo_export.h"
+#include "underscore_pkg.h"
+
+jclass proxy_class_underscore_pkg_Underscore_struct;
+jmethodID proxy_class_underscore_pkg_Underscore_struct_cons;
+
+JNIEXPORT void JNICALL
+Java_underscore_1pkg_Underscore_1pkg__1init(JNIEnv *env, jclass _unused) {
+    jclass clazz;
+    clazz = (*env)->FindClass(env, "underscore_pkg/Underscore_struct");
+    proxy_class_underscore_pkg_Underscore_struct = (*env)->NewGlobalRef(env, clazz);
+    proxy_class_underscore_pkg_Underscore_struct_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
+}
+
+JNIEXPORT void JNICALL
+Java_underscore_1pkg_Underscore_pkg_underscore_1func(JNIEnv* env, jclass _clazz) {
+    proxyunderscore_pkg__Underscore_func();
+}
+
+JNIEXPORT jobject JNICALL
+Java_underscore_1pkg_Underscore_1struct__1_1New(JNIEnv *env, jclass clazz) {
+    int32_t refnum = new_underscore_pkg_Underscore_struct();
+    return go_seq_from_refnum(env, refnum, NULL, NULL);
+}
+
+JNIEXPORT void JNICALL
+Java_underscore_1pkg_Underscore_1struct_setUnderscore_1field(JNIEnv *env, jobject this, jstring v) {
+    int32_t o = go_seq_to_refnum_go(env, this);
+    nstring _v = go_seq_from_java_string(env, v);
+    proxyunderscore_pkg_Underscore_struct_Underscore_field_Set(o, _v);
+}
+
+JNIEXPORT jstring JNICALL
+Java_underscore_1pkg_Underscore_1struct_getUnderscore_1field(JNIEnv *env, jobject this) {
+    int32_t o = go_seq_to_refnum_go(env, this);
+    nstring r0 = proxyunderscore_pkg_Underscore_struct_Underscore_field_Get(o);
+    jstring _r0 = go_seq_to_java_string(env, r0);
+    return _r0;
+}
+
+JNIEXPORT void JNICALL
+Java_underscore_1pkg_Underscore_pkg_setUnderscore_1var(JNIEnv *env, jclass clazz, jlong v) {
+    nint _v = (nint)v;
+    var_setunderscore_pkg_Underscore_var(_v);
+}
+
+JNIEXPORT jlong JNICALL
+Java_underscore_1pkg_Underscore_pkg_getUnderscore_1var(JNIEnv *env, jclass clazz) {
+    nint r0 = var_getunderscore_pkg_Underscore_var();
+    jlong _r0 = (jlong)r0;
+    return _r0;
+}
+
diff --git a/bind/testdata/underscores.java.golden b/bind/testdata/underscores.java.golden
new file mode 100644
index 0000000..fa1f8da
--- /dev/null
+++ b/bind/testdata/underscores.java.golden
@@ -0,0 +1,85 @@
+// Java class underscore_pkg.Underscore_struct is a proxy for talking to a Go program.
+//   gobind -lang=java underscores
+//
+// File is generated by gobind. Do not edit.
+package underscore_pkg;
+
+import go.Seq;
+
+public final class Underscore_struct implements Seq.Proxy {
+    static { Underscore_pkg.touch(); }
+    
+    private final Seq.Ref ref;
+    
+    @Override public final int incRefnum() {
+          int refnum = ref.refnum;
+          Seq.incGoRef(refnum);
+          return refnum;
+    }
+    
+    Underscore_struct(Seq.Ref ref) { this.ref = ref; }
+    
+    public Underscore_struct() { this.ref = __New(); }
+    
+    private static native Seq.Ref __New();
+    
+    public final native String getUnderscore_field();
+    public final native void setUnderscore_field(String v);
+    
+    @Override public boolean equals(Object o) {
+        if (o == null || !(o instanceof Underscore_struct)) {
+            return false;
+        }
+        Underscore_struct that = (Underscore_struct)o;
+        String thisUnderscore_field = getUnderscore_field();
+        String thatUnderscore_field = that.getUnderscore_field();
+        if (thisUnderscore_field == null) {
+            if (thatUnderscore_field != null) {
+                return false;
+            }
+        } else if (!thisUnderscore_field.equals(thatUnderscore_field)) {
+            return false;
+        }
+        return true;
+    }
+    
+    @Override public int hashCode() {
+        return java.util.Arrays.hashCode(new Object[] {getUnderscore_field()});
+    }
+    
+    @Override public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("Underscore_struct").append("{");
+        b.append("Underscore_field:").append(getUnderscore_field()).append(",");
+        return b.append("}").toString();
+    }
+}
+
+// Java class underscore_pkg.Underscore_pkg is a proxy for talking to a Go program.
+//   gobind -lang=java underscores
+//
+// File is generated by gobind. Do not edit.
+package underscore_pkg;
+
+import go.Seq;
+
+public abstract class Underscore_pkg {
+    static {
+        Seq.touch(); // for loading the native library
+        _init();
+    }
+    
+    private Underscore_pkg() {} // uninstantiable
+    
+    // touch is called from other bound packages to initialize this package
+    public static void touch() {}
+    
+    private static native void _init();
+    
+    
+    
+    public static native void setUnderscore_var(long v);
+    public static native long getUnderscore_var();
+    
+    public static native void underscore_func();
+}
diff --git a/bind/testdata/underscores.java.h.golden b/bind/testdata/underscores.java.h.golden
new file mode 100644
index 0000000..6bf8948
--- /dev/null
+++ b/bind/testdata/underscores.java.h.golden
@@ -0,0 +1,13 @@
+// JNI function headers for the Go <=> Java bridge.
+//   gobind -lang=java underscores
+//
+// File is generated by gobind. Do not edit.
+
+#ifndef __Underscore_pkg_H__
+#define __Underscore_pkg_H__
+
+#include <jni.h>
+
+extern jclass proxy_class_underscore_pkg_Underscore_struct;
+extern jmethodID proxy_class_underscore_pkg_Underscore_struct_cons;
+#endif
diff --git a/bind/testdata/underscores.objc.go.h.golden b/bind/testdata/underscores.objc.go.h.golden
new file mode 100644
index 0000000..d7c8dfe
--- /dev/null
+++ b/bind/testdata/underscores.objc.go.h.golden
@@ -0,0 +1,11 @@
+// Objective-C API for talking to underscores Go package.
+//   gobind -lang=objc underscores
+//
+// File is generated by gobind. Do not edit.
+
+#ifndef __GO_underscore_pkg_H__
+#define __GO_underscore_pkg_H__
+
+#include <stdint.h>
+#include <objc/objc.h>
+#endif
diff --git a/bind/testdata/underscores.objc.h.golden b/bind/testdata/underscores.objc.h.golden
new file mode 100644
index 0000000..783ef1a
--- /dev/null
+++ b/bind/testdata/underscores.objc.h.golden
@@ -0,0 +1,33 @@
+// Objective-C API for talking to underscores Go package.
+//   gobind -lang=objc underscores
+//
+// File is generated by gobind. Do not edit.
+
+#ifndef __Underscore_pkg_H__
+#define __Underscore_pkg_H__
+
+@import Foundation;
+#include "Universe.objc.h"
+
+
+@class Underscore_pkgUnderscore_struct;
+
+@interface Underscore_pkgUnderscore_struct : NSObject <goSeqRefInterface> {
+}
+@property(strong, readonly) id _ref;
+
+- (instancetype)initWithRef:(id)ref;
+- (instancetype)init;
+- (NSString*)underscore_field;
+- (void)setUnderscore_field:(NSString*)v;
+@end
+
+@interface Underscore_pkg : NSObject
++ (long) underscore_var;
++ (void) setUnderscore_var:(long)v;
+
+@end
+
+FOUNDATION_EXPORT void Underscore_pkgUnderscore_func(void);
+
+#endif
diff --git a/bind/testdata/underscores.objc.m.golden b/bind/testdata/underscores.objc.m.golden
new file mode 100644
index 0000000..4cac5a5
--- /dev/null
+++ b/bind/testdata/underscores.objc.m.golden
@@ -0,0 +1,66 @@
+// Objective-C API for talking to underscores Go package.
+//   gobind -lang=objc underscores
+//
+// File is generated by gobind. Do not edit.
+
+#include <Foundation/Foundation.h>
+#include "seq.h"
+#include "_cgo_export.h"
+#include "Underscore_pkg.objc.h"
+
+
+@implementation Underscore_pkgUnderscore_struct {
+}
+
+- (instancetype)initWithRef:(id)ref {
+	self = [super init];
+	if (self) { __ref = ref; }
+	return self;
+}
+
+- (instancetype)init {
+	self = [super init];
+	if (self) {
+		__ref = go_seq_from_refnum(new_underscore_pkg_Underscore_struct());
+	}
+	return self;
+}
+
+- (NSString*)underscore_field {
+	int32_t refnum = go_seq_go_to_refnum(self._ref);
+	nstring r0 = proxyunderscore_pkg_Underscore_struct_Underscore_field_Get(refnum);
+	NSString *_r0 = go_seq_to_objc_string(r0);
+	return _r0;
+}
+
+- (void)setUnderscore_field:(NSString*)v {
+	int32_t refnum = go_seq_go_to_refnum(self._ref);
+	nstring _v = go_seq_from_objc_string(v);
+	proxyunderscore_pkg_Underscore_struct_Underscore_field_Set(refnum, _v);
+}
+
+@end
+
+
+@implementation Underscore_pkg
++ (void) setUnderscore_var:(long)v {
+	nint _v = (nint)v;
+	var_setunderscore_pkg_Underscore_var(_v);
+}
+
++ (long) underscore_var {
+	nint r0 = var_getunderscore_pkg_Underscore_var();
+	long _r0 = (long)r0;
+	return _r0;
+}
+
+@end
+
+
+void Underscore_pkgUnderscore_func(void) {
+	proxyunderscore_pkg__Underscore_func();
+}
+
+__attribute__((constructor)) static void init() {
+	init_seq();
+}