1
0
Fork 0

Adding upstream version 0.0~git20250520.a1d9079+dfsg.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 19:46:29 +02:00
parent 590ac7ff5f
commit 20149b7f3a
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
456 changed files with 70406 additions and 0 deletions

10
.gitattributes vendored Normal file
View file

@ -0,0 +1,10 @@
# Treat all files in this repo as binary, with no git magic updating
# line endings. Windows users contributing to Go will need to use a
# modern version of git and editors capable of LF line endings.
#
# We'll prevent accidental CRLF line endings from entering the repo
# via the git-review gofmt checks.
#
# See golang.org/issue/9281
* -text

27
.gitignore vendored Normal file
View file

@ -0,0 +1,27 @@
# Add no patterns to .gitignore except for files generated by the build.
last-change
*.apk
*.app
*.framework
*.xcframework
*.aar
*.jar
*.iml
.idea
.gradle
*.properties
.DS_Store
example/ivy/**/build/
example/ivy/android/gradle/
# Xcode configuration files.
*.xcworkspace
xcuserdata
*.entitlements
# Android Studio build and IDE configuration files.
example/bind/android/local.properties
example/bind/android/gradlew*
example/bind/android/gradle
example/bind/android/build
example/bind/android/app/build

26
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,26 @@
# Contributing to Go
Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help!
## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using?
3. What did you do?
4. What did you expect to see?
5. What did you see instead?
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
## Contributing code
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches.
Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file.

27
LICENSE Normal file
View file

@ -0,0 +1,27 @@
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
PATENTS Normal file
View file

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

34
README.md Normal file
View file

@ -0,0 +1,34 @@
# Go support for Mobile devices
[![Go Reference](https://pkg.go.dev/badge/golang.org/x/mobile.svg)](https://pkg.go.dev/golang.org/x/mobile)
The Go mobile repository holds packages and build tools for using Go on mobile platforms.
Package documentation as a starting point:
- [Building all-Go apps](https://golang.org/x/mobile/app)
- [Building libraries for SDK apps](https://golang.org/x/mobile/cmd/gobind)
![Caution image](doc/caution.png)
The Go Mobile project is experimental. Use this at your own risk.
While we are working hard to improve it, neither Google nor the Go
team can provide end-user support.
This is early work and installing the build system requires Go 1.5.
Follow the instructions on
[golang.org/wiki/Mobile](https://golang.org/wiki/Mobile)
to install the gomobile command, build the
[basic](https://golang.org/x/mobile/example/basic)
and the [bind](https://golang.org/x/mobile/example/bind) example apps.
--
Contributions to Go are appreciated. See https://go.dev/doc/contribute.
The git repository is https://go.googlesource.com/mobile.
* Bugs can be filed at the [Go issue tracker](https://go.dev/issue/new?title=x/mobile:+).
* Feature requests should preliminary be discussed on
[golang-nuts](https://groups.google.com/forum/#!forum/golang-nuts)
mailing list.

67
app/GoNativeActivity.java Normal file
View file

@ -0,0 +1,67 @@
package org.golang.app;
import android.app.Activity;
import android.app.NativeActivity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyCharacterMap;
public class GoNativeActivity extends NativeActivity {
private static GoNativeActivity goNativeActivity;
public GoNativeActivity() {
super();
goNativeActivity = this;
}
String getTmpdir() {
return getCacheDir().getAbsolutePath();
}
static int getRune(int deviceId, int keyCode, int metaState) {
try {
int rune = KeyCharacterMap.load(deviceId).get(keyCode, metaState);
if (rune == 0) {
return -1;
}
return rune;
} catch (KeyCharacterMap.UnavailableException e) {
return -1;
} catch (Exception e) {
Log.e("Go", "exception reading KeyCharacterMap", e);
return -1;
}
}
private void load() {
// Interestingly, NativeActivity uses a different method
// to find native code to execute, avoiding
// System.loadLibrary. The result is Java methods
// implemented in C with JNIEXPORT (and JNI_OnLoad) are not
// available unless an explicit call to System.loadLibrary
// is done. So we do it here, borrowing the name of the
// library from the same AndroidManifest.xml metadata used
// by NativeActivity.
try {
ActivityInfo ai = getPackageManager().getActivityInfo(
getIntent().getComponent(), PackageManager.GET_META_DATA);
if (ai.metaData == null) {
Log.e("Go", "loadLibrary: no manifest metadata found");
return;
}
String libName = ai.metaData.getString("android.app.lib_name");
System.loadLibrary(libName);
} catch (Exception e) {
Log.e("Go", "loadLibrary failed", e);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
load();
super.onCreate(savedInstanceState);
}
}

201
app/android.c Normal file
View file

@ -0,0 +1,201 @@
// Copyright 2014 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.
//go:build android
// +build android
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include "_cgo_export.h"
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Go", __VA_ARGS__)
#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go", __VA_ARGS__)
static jclass current_class;
static jclass find_class(JNIEnv *env, const char *class_name) {
jclass clazz = (*env)->FindClass(env, class_name);
if (clazz == NULL) {
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find %s", class_name);
return NULL;
}
return clazz;
}
static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
if (m == 0) {
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find method %s %s", name, sig);
return 0;
}
return m;
}
static jmethodID find_static_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
if (m == 0) {
(*env)->ExceptionClear(env);
LOG_FATAL("cannot find method %s %s", name, sig);
return 0;
}
return m;
}
static jmethodID key_rune_method;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
return JNI_VERSION_1_6;
}
static int main_running = 0;
// Entry point from our subclassed NativeActivity.
//
// By here, the Go runtime has been initialized (as we are running in
// -buildmode=c-shared) but the first time it is called, Go's main.main
// hasn't been called yet.
//
// The Activity may be created and destroyed multiple times throughout
// the life of a single process. Each time, onCreate is called.
void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
if (!main_running) {
JNIEnv* env = activity->env;
// Note that activity->clazz is mis-named.
current_class = (*env)->GetObjectClass(env, activity->clazz);
current_class = (*env)->NewGlobalRef(env, current_class);
key_rune_method = find_static_method(env, current_class, "getRune", "(III)I");
setCurrentContext(activity->vm, (*env)->NewGlobalRef(env, activity->clazz));
// Set TMPDIR.
jmethodID gettmpdir = find_method(env, current_class, "getTmpdir", "()Ljava/lang/String;");
jstring jpath = (jstring)(*env)->CallObjectMethod(env, activity->clazz, gettmpdir, NULL);
const char* tmpdir = (*env)->GetStringUTFChars(env, jpath, NULL);
if (setenv("TMPDIR", tmpdir, 1) != 0) {
LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", tmpdir, errno);
}
(*env)->ReleaseStringUTFChars(env, jpath, tmpdir);
// Call the Go main.main.
uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main");
if (!mainPC) {
LOG_FATAL("missing main.main");
}
callMain(mainPC);
main_running = 1;
}
// These functions match the methods on Activity, described at
// http://developer.android.com/reference/android/app/Activity.html
//
// Note that onNativeWindowResized is not called on resize. Avoid it.
// https://code.google.com/p/android/issues/detail?id=180645
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
onCreate(activity);
}
// TODO(crawshaw): Test configuration on more devices.
static const EGLint RGB_888[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_NONE
};
EGLDisplay display = NULL;
EGLSurface surface = NULL;
static char* initEGLDisplay() {
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(display, 0, 0)) {
return "EGL initialize failed";
}
return NULL;
}
char* createEGLSurface(ANativeWindow* window) {
char* err;
EGLint numConfigs, format;
EGLConfig config;
EGLContext context;
if (display == 0) {
if ((err = initEGLDisplay()) != NULL) {
return err;
}
}
if (!eglChooseConfig(display, RGB_888, &config, 1, &numConfigs)) {
return "EGL choose RGB_888 config failed";
}
if (numConfigs <= 0) {
return "EGL no config found";
}
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
if (ANativeWindow_setBuffersGeometry(window, 0, 0, format) != 0) {
return "EGL set buffers geometry failed";
}
surface = eglCreateWindowSurface(display, config, window, NULL);
if (surface == EGL_NO_SURFACE) {
return "EGL create surface failed";
}
const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
return "eglMakeCurrent failed";
}
return NULL;
}
char* destroyEGLSurface() {
if (!eglDestroySurface(display, surface)) {
return "EGL destroy surface failed";
}
return NULL;
}
int32_t getKeyRune(JNIEnv* env, AInputEvent* e) {
return (int32_t)(*env)->CallStaticIntMethod(
env,
current_class,
key_rune_method,
AInputEvent_getDeviceId(e),
AKeyEvent_getKeyCode(e),
AKeyEvent_getMetaState(e)
);
}

824
app/android.go Normal file
View file

@ -0,0 +1,824 @@
// Copyright 2014 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.
//go:build android
/*
Android Apps are built with -buildmode=c-shared. They are loaded by a
running Java process.
Before any entry point is reached, a global constructor initializes the
Go runtime, calling all Go init functions. All cgo calls will block
until this is complete. Next JNI_OnLoad is called. When that is
complete, one of two entry points is called.
All-Go apps built using NativeActivity enter at ANativeActivity_onCreate.
Go libraries (for example, those built with gomobile bind) do not use
the app package initialization.
*/
package app
/*
#cgo LDFLAGS: -landroid -llog -lEGL -lGLESv2
#include <android/configuration.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android/looper.h>
#include <android/native_activity.h>
#include <android/native_window.h>
#include <EGL/egl.h>
#include <jni.h>
#include <pthread.h>
#include <stdlib.h>
extern EGLDisplay display;
extern EGLSurface surface;
char* createEGLSurface(ANativeWindow* window);
char* destroyEGLSurface();
int32_t getKeyRune(JNIEnv* env, AInputEvent* e);
*/
import "C"
import (
"fmt"
"log"
"os"
"time"
"unsafe"
"golang.org/x/mobile/app/internal/callfn"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/geom"
"golang.org/x/mobile/internal/mobileinit"
)
// RunOnJVM runs fn on a new goroutine locked to an OS thread with a JNIEnv.
//
// RunOnJVM blocks until the call to fn is complete. Any Java
// exception or failure to attach to the JVM is returned as an error.
//
// The function fn takes vm, the current JavaVM*,
// env, the current JNIEnv*, and
// ctx, a jobject representing the global android.context.Context.
func RunOnJVM(fn func(vm, jniEnv, ctx uintptr) error) error {
return mobileinit.RunOnJVM(fn)
}
//export setCurrentContext
func setCurrentContext(vm *C.JavaVM, ctx C.jobject) {
mobileinit.SetCurrentContext(unsafe.Pointer(vm), uintptr(ctx))
}
//export callMain
func callMain(mainPC uintptr) {
for _, name := range []string{"TMPDIR", "PATH", "LD_LIBRARY_PATH"} {
n := C.CString(name)
os.Setenv(name, C.GoString(C.getenv(n)))
C.free(unsafe.Pointer(n))
}
// Set timezone.
//
// Note that Android zoneinfo is stored in /system/usr/share/zoneinfo,
// but it is in some kind of packed TZiff file that we do not support
// yet. As a stopgap, we build a fixed zone using the tm_zone name.
var curtime C.time_t
var curtm C.struct_tm
C.time(&curtime)
C.localtime_r(&curtime, &curtm)
tzOffset := int(curtm.tm_gmtoff)
tz := C.GoString(curtm.tm_zone)
time.Local = time.FixedZone(tz, tzOffset)
go callfn.CallFn(mainPC)
}
//export onStart
func onStart(activity *C.ANativeActivity) {
}
//export onResume
func onResume(activity *C.ANativeActivity) {
}
//export onSaveInstanceState
func onSaveInstanceState(activity *C.ANativeActivity, outSize *C.size_t) unsafe.Pointer {
return nil
}
//export onPause
func onPause(activity *C.ANativeActivity) {
}
//export onStop
func onStop(activity *C.ANativeActivity) {
}
//export onCreate
func onCreate(activity *C.ANativeActivity) {
// Set the initial configuration.
//
// Note we use unbuffered channels to talk to the activity loop, and
// NativeActivity calls these callbacks sequentially, so configuration
// will be set before <-windowRedrawNeeded is processed.
windowConfigChange <- windowConfigRead(activity)
}
//export onDestroy
func onDestroy(activity *C.ANativeActivity) {
}
//export onWindowFocusChanged
func onWindowFocusChanged(activity *C.ANativeActivity, hasFocus C.int) {
}
//export onNativeWindowCreated
func onNativeWindowCreated(activity *C.ANativeActivity, window *C.ANativeWindow) {
}
//export onNativeWindowRedrawNeeded
func onNativeWindowRedrawNeeded(activity *C.ANativeActivity, window *C.ANativeWindow) {
// Called on orientation change and window resize.
// Send a request for redraw, and block this function
// until a complete draw and buffer swap is completed.
// This is required by the redraw documentation to
// avoid bad draws.
windowRedrawNeeded <- window
<-windowRedrawDone
}
//export onNativeWindowDestroyed
func onNativeWindowDestroyed(activity *C.ANativeActivity, window *C.ANativeWindow) {
windowDestroyed <- window
}
//export onInputQueueCreated
func onInputQueueCreated(activity *C.ANativeActivity, q *C.AInputQueue) {
inputQueue <- q
<-inputQueueDone
}
//export onInputQueueDestroyed
func onInputQueueDestroyed(activity *C.ANativeActivity, q *C.AInputQueue) {
inputQueue <- nil
<-inputQueueDone
}
//export onContentRectChanged
func onContentRectChanged(activity *C.ANativeActivity, rect *C.ARect) {
}
type windowConfig struct {
orientation size.Orientation
pixelsPerPt float32
}
func windowConfigRead(activity *C.ANativeActivity) windowConfig {
aconfig := C.AConfiguration_new()
C.AConfiguration_fromAssetManager(aconfig, activity.assetManager)
orient := C.AConfiguration_getOrientation(aconfig)
density := C.AConfiguration_getDensity(aconfig)
C.AConfiguration_delete(aconfig)
// Calculate the screen resolution. This value is approximate. For example,
// a physical resolution of 200 DPI may be quantized to one of the
// ACONFIGURATION_DENSITY_XXX values such as 160 or 240.
//
// A more accurate DPI could possibly be calculated from
// https://developer.android.com/reference/android/util/DisplayMetrics.html#xdpi
// but this does not appear to be accessible via the NDK. In any case, the
// hardware might not even provide a more accurate number, as the system
// does not apparently use the reported value. See golang.org/issue/13366
// for a discussion.
var dpi int
switch density {
case C.ACONFIGURATION_DENSITY_DEFAULT:
dpi = 160
case C.ACONFIGURATION_DENSITY_LOW,
C.ACONFIGURATION_DENSITY_MEDIUM,
213, // C.ACONFIGURATION_DENSITY_TV
C.ACONFIGURATION_DENSITY_HIGH,
320, // ACONFIGURATION_DENSITY_XHIGH
480, // ACONFIGURATION_DENSITY_XXHIGH
640: // ACONFIGURATION_DENSITY_XXXHIGH
dpi = int(density)
case C.ACONFIGURATION_DENSITY_NONE:
log.Print("android device reports no screen density")
dpi = 72
default:
log.Printf("android device reports unknown density: %d", density)
// All we can do is guess.
if density > 0 {
dpi = int(density)
} else {
dpi = 72
}
}
o := size.OrientationUnknown
switch orient {
case C.ACONFIGURATION_ORIENTATION_PORT:
o = size.OrientationPortrait
case C.ACONFIGURATION_ORIENTATION_LAND:
o = size.OrientationLandscape
}
return windowConfig{
orientation: o,
pixelsPerPt: float32(dpi) / 72,
}
}
//export onConfigurationChanged
func onConfigurationChanged(activity *C.ANativeActivity) {
// A rotation event first triggers onConfigurationChanged, then
// calls onNativeWindowRedrawNeeded. We extract the orientation
// here and save it for the redraw event.
windowConfigChange <- windowConfigRead(activity)
}
//export onLowMemory
func onLowMemory(activity *C.ANativeActivity) {
}
var (
inputQueue = make(chan *C.AInputQueue)
inputQueueDone = make(chan struct{})
windowDestroyed = make(chan *C.ANativeWindow)
windowRedrawNeeded = make(chan *C.ANativeWindow)
windowRedrawDone = make(chan struct{})
windowConfigChange = make(chan windowConfig)
)
func init() {
theApp.registerGLViewportFilter()
}
func main(f func(App)) {
mainUserFn = f
// TODO: merge the runInputQueue and mainUI functions?
go func() {
if err := mobileinit.RunOnJVM(runInputQueue); err != nil {
log.Fatalf("app: %v", err)
}
}()
// Preserve this OS thread for:
// 1. the attached JNI thread
// 2. the GL context
if err := mobileinit.RunOnJVM(mainUI); err != nil {
log.Fatalf("app: %v", err)
}
}
var mainUserFn func(App)
func mainUI(vm, jniEnv, ctx uintptr) error {
workAvailable := theApp.worker.WorkAvailable()
donec := make(chan struct{})
go func() {
// close the donec channel in a defer statement
// so that we could still be able to return even
// if mainUserFn panics.
defer close(donec)
mainUserFn(theApp)
}()
var pixelsPerPt float32
var orientation size.Orientation
for {
select {
case <-donec:
return nil
case cfg := <-windowConfigChange:
pixelsPerPt = cfg.pixelsPerPt
orientation = cfg.orientation
case w := <-windowRedrawNeeded:
if C.surface == nil {
if errStr := C.createEGLSurface(w); errStr != nil {
return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
}
}
theApp.sendLifecycle(lifecycle.StageFocused)
widthPx := int(C.ANativeWindow_getWidth(w))
heightPx := int(C.ANativeWindow_getHeight(w))
theApp.eventsIn <- size.Event{
WidthPx: widthPx,
HeightPx: heightPx,
WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
PixelsPerPt: pixelsPerPt,
Orientation: orientation,
}
theApp.eventsIn <- paint.Event{External: true}
case <-windowDestroyed:
if C.surface != nil {
if errStr := C.destroyEGLSurface(); errStr != nil {
return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
}
}
C.surface = nil
theApp.sendLifecycle(lifecycle.StageAlive)
case <-workAvailable:
theApp.worker.DoWork()
case <-theApp.publish:
// TODO: compare a generation number to redrawGen for stale paints?
if C.surface != nil {
// eglSwapBuffers blocks until vsync.
if C.eglSwapBuffers(C.display, C.surface) == C.EGL_FALSE {
log.Printf("app: failed to swap buffers (%s)", eglGetError())
}
}
select {
case windowRedrawDone <- struct{}{}:
default:
}
theApp.publishResult <- PublishResult{}
}
}
}
func runInputQueue(vm, jniEnv, ctx uintptr) error {
env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer
// Android loopers select on OS file descriptors, not Go channels, so we
// translate the inputQueue channel to an ALooper_wake call.
l := C.ALooper_prepare(C.ALOOPER_PREPARE_ALLOW_NON_CALLBACKS)
pending := make(chan *C.AInputQueue, 1)
go func() {
for q := range inputQueue {
pending <- q
C.ALooper_wake(l)
}
}()
var q *C.AInputQueue
for {
if C.ALooper_pollOnce(-1, nil, nil, nil) == C.ALOOPER_POLL_WAKE {
select {
default:
case p := <-pending:
if q != nil {
processEvents(env, q)
C.AInputQueue_detachLooper(q)
}
q = p
if q != nil {
C.AInputQueue_attachLooper(q, l, 0, nil, nil)
}
inputQueueDone <- struct{}{}
}
}
if q != nil {
processEvents(env, q)
}
}
}
func processEvents(env *C.JNIEnv, q *C.AInputQueue) {
var e *C.AInputEvent
for C.AInputQueue_getEvent(q, &e) >= 0 {
if C.AInputQueue_preDispatchEvent(q, e) != 0 {
continue
}
processEvent(env, e)
C.AInputQueue_finishEvent(q, e, 0)
}
}
func processEvent(env *C.JNIEnv, e *C.AInputEvent) {
switch C.AInputEvent_getType(e) {
case C.AINPUT_EVENT_TYPE_KEY:
processKey(env, e)
case C.AINPUT_EVENT_TYPE_MOTION:
// At most one of the events in this batch is an up or down event; get its index and change.
upDownIndex := C.size_t(C.AMotionEvent_getAction(e)&C.AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> C.AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT
upDownType := touch.TypeMove
switch C.AMotionEvent_getAction(e) & C.AMOTION_EVENT_ACTION_MASK {
case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN:
upDownType = touch.TypeBegin
case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP:
upDownType = touch.TypeEnd
}
for i, n := C.size_t(0), C.AMotionEvent_getPointerCount(e); i < n; i++ {
t := touch.TypeMove
if i == upDownIndex {
t = upDownType
}
theApp.eventsIn <- touch.Event{
X: float32(C.AMotionEvent_getX(e, i)),
Y: float32(C.AMotionEvent_getY(e, i)),
Sequence: touch.Sequence(C.AMotionEvent_getPointerId(e, i)),
Type: t,
}
}
default:
log.Printf("unknown input event, type=%d", C.AInputEvent_getType(e))
}
}
func processKey(env *C.JNIEnv, e *C.AInputEvent) {
deviceID := C.AInputEvent_getDeviceId(e)
if deviceID == 0 {
// Software keyboard input, leaving for scribe/IME.
return
}
k := key.Event{
Rune: rune(C.getKeyRune(env, e)),
Code: convAndroidKeyCode(int32(C.AKeyEvent_getKeyCode(e))),
}
switch C.AKeyEvent_getAction(e) {
case C.AKEY_EVENT_ACTION_DOWN:
k.Direction = key.DirPress
case C.AKEY_EVENT_ACTION_UP:
k.Direction = key.DirRelease
default:
k.Direction = key.DirNone
}
// TODO(crawshaw): set Modifiers.
theApp.eventsIn <- k
}
func eglGetError() string {
switch errNum := C.eglGetError(); errNum {
case C.EGL_SUCCESS:
return "EGL_SUCCESS"
case C.EGL_NOT_INITIALIZED:
return "EGL_NOT_INITIALIZED"
case C.EGL_BAD_ACCESS:
return "EGL_BAD_ACCESS"
case C.EGL_BAD_ALLOC:
return "EGL_BAD_ALLOC"
case C.EGL_BAD_ATTRIBUTE:
return "EGL_BAD_ATTRIBUTE"
case C.EGL_BAD_CONTEXT:
return "EGL_BAD_CONTEXT"
case C.EGL_BAD_CONFIG:
return "EGL_BAD_CONFIG"
case C.EGL_BAD_CURRENT_SURFACE:
return "EGL_BAD_CURRENT_SURFACE"
case C.EGL_BAD_DISPLAY:
return "EGL_BAD_DISPLAY"
case C.EGL_BAD_SURFACE:
return "EGL_BAD_SURFACE"
case C.EGL_BAD_MATCH:
return "EGL_BAD_MATCH"
case C.EGL_BAD_PARAMETER:
return "EGL_BAD_PARAMETER"
case C.EGL_BAD_NATIVE_PIXMAP:
return "EGL_BAD_NATIVE_PIXMAP"
case C.EGL_BAD_NATIVE_WINDOW:
return "EGL_BAD_NATIVE_WINDOW"
case C.EGL_CONTEXT_LOST:
return "EGL_CONTEXT_LOST"
default:
return fmt.Sprintf("Unknown EGL err: %d", errNum)
}
}
func convAndroidKeyCode(aKeyCode int32) key.Code {
// Many Android key codes do not map into USB HID codes.
// For those, key.CodeUnknown is returned. This switch has all
// cases, even the unknown ones, to serve as a documentation
// and search aid.
switch aKeyCode {
case C.AKEYCODE_UNKNOWN:
case C.AKEYCODE_SOFT_LEFT:
case C.AKEYCODE_SOFT_RIGHT:
case C.AKEYCODE_HOME:
return key.CodeHome
case C.AKEYCODE_BACK:
case C.AKEYCODE_CALL:
case C.AKEYCODE_ENDCALL:
case C.AKEYCODE_0:
return key.Code0
case C.AKEYCODE_1:
return key.Code1
case C.AKEYCODE_2:
return key.Code2
case C.AKEYCODE_3:
return key.Code3
case C.AKEYCODE_4:
return key.Code4
case C.AKEYCODE_5:
return key.Code5
case C.AKEYCODE_6:
return key.Code6
case C.AKEYCODE_7:
return key.Code7
case C.AKEYCODE_8:
return key.Code8
case C.AKEYCODE_9:
return key.Code9
case C.AKEYCODE_STAR:
case C.AKEYCODE_POUND:
case C.AKEYCODE_DPAD_UP:
case C.AKEYCODE_DPAD_DOWN:
case C.AKEYCODE_DPAD_LEFT:
case C.AKEYCODE_DPAD_RIGHT:
case C.AKEYCODE_DPAD_CENTER:
case C.AKEYCODE_VOLUME_UP:
return key.CodeVolumeUp
case C.AKEYCODE_VOLUME_DOWN:
return key.CodeVolumeDown
case C.AKEYCODE_POWER:
case C.AKEYCODE_CAMERA:
case C.AKEYCODE_CLEAR:
case C.AKEYCODE_A:
return key.CodeA
case C.AKEYCODE_B:
return key.CodeB
case C.AKEYCODE_C:
return key.CodeC
case C.AKEYCODE_D:
return key.CodeD
case C.AKEYCODE_E:
return key.CodeE
case C.AKEYCODE_F:
return key.CodeF
case C.AKEYCODE_G:
return key.CodeG
case C.AKEYCODE_H:
return key.CodeH
case C.AKEYCODE_I:
return key.CodeI
case C.AKEYCODE_J:
return key.CodeJ
case C.AKEYCODE_K:
return key.CodeK
case C.AKEYCODE_L:
return key.CodeL
case C.AKEYCODE_M:
return key.CodeM
case C.AKEYCODE_N:
return key.CodeN
case C.AKEYCODE_O:
return key.CodeO
case C.AKEYCODE_P:
return key.CodeP
case C.AKEYCODE_Q:
return key.CodeQ
case C.AKEYCODE_R:
return key.CodeR
case C.AKEYCODE_S:
return key.CodeS
case C.AKEYCODE_T:
return key.CodeT
case C.AKEYCODE_U:
return key.CodeU
case C.AKEYCODE_V:
return key.CodeV
case C.AKEYCODE_W:
return key.CodeW
case C.AKEYCODE_X:
return key.CodeX
case C.AKEYCODE_Y:
return key.CodeY
case C.AKEYCODE_Z:
return key.CodeZ
case C.AKEYCODE_COMMA:
return key.CodeComma
case C.AKEYCODE_PERIOD:
return key.CodeFullStop
case C.AKEYCODE_ALT_LEFT:
return key.CodeLeftAlt
case C.AKEYCODE_ALT_RIGHT:
return key.CodeRightAlt
case C.AKEYCODE_SHIFT_LEFT:
return key.CodeLeftShift
case C.AKEYCODE_SHIFT_RIGHT:
return key.CodeRightShift
case C.AKEYCODE_TAB:
return key.CodeTab
case C.AKEYCODE_SPACE:
return key.CodeSpacebar
case C.AKEYCODE_SYM:
case C.AKEYCODE_EXPLORER:
case C.AKEYCODE_ENVELOPE:
case C.AKEYCODE_ENTER:
return key.CodeReturnEnter
case C.AKEYCODE_DEL:
return key.CodeDeleteBackspace
case C.AKEYCODE_GRAVE:
return key.CodeGraveAccent
case C.AKEYCODE_MINUS:
return key.CodeHyphenMinus
case C.AKEYCODE_EQUALS:
return key.CodeEqualSign
case C.AKEYCODE_LEFT_BRACKET:
return key.CodeLeftSquareBracket
case C.AKEYCODE_RIGHT_BRACKET:
return key.CodeRightSquareBracket
case C.AKEYCODE_BACKSLASH:
return key.CodeBackslash
case C.AKEYCODE_SEMICOLON:
return key.CodeSemicolon
case C.AKEYCODE_APOSTROPHE:
return key.CodeApostrophe
case C.AKEYCODE_SLASH:
return key.CodeSlash
case C.AKEYCODE_AT:
case C.AKEYCODE_NUM:
case C.AKEYCODE_HEADSETHOOK:
case C.AKEYCODE_FOCUS:
case C.AKEYCODE_PLUS:
case C.AKEYCODE_MENU:
case C.AKEYCODE_NOTIFICATION:
case C.AKEYCODE_SEARCH:
case C.AKEYCODE_MEDIA_PLAY_PAUSE:
case C.AKEYCODE_MEDIA_STOP:
case C.AKEYCODE_MEDIA_NEXT:
case C.AKEYCODE_MEDIA_PREVIOUS:
case C.AKEYCODE_MEDIA_REWIND:
case C.AKEYCODE_MEDIA_FAST_FORWARD:
case C.AKEYCODE_MUTE:
case C.AKEYCODE_PAGE_UP:
return key.CodePageUp
case C.AKEYCODE_PAGE_DOWN:
return key.CodePageDown
case C.AKEYCODE_PICTSYMBOLS:
case C.AKEYCODE_SWITCH_CHARSET:
case C.AKEYCODE_BUTTON_A:
case C.AKEYCODE_BUTTON_B:
case C.AKEYCODE_BUTTON_C:
case C.AKEYCODE_BUTTON_X:
case C.AKEYCODE_BUTTON_Y:
case C.AKEYCODE_BUTTON_Z:
case C.AKEYCODE_BUTTON_L1:
case C.AKEYCODE_BUTTON_R1:
case C.AKEYCODE_BUTTON_L2:
case C.AKEYCODE_BUTTON_R2:
case C.AKEYCODE_BUTTON_THUMBL:
case C.AKEYCODE_BUTTON_THUMBR:
case C.AKEYCODE_BUTTON_START:
case C.AKEYCODE_BUTTON_SELECT:
case C.AKEYCODE_BUTTON_MODE:
case C.AKEYCODE_ESCAPE:
return key.CodeEscape
case C.AKEYCODE_FORWARD_DEL:
return key.CodeDeleteForward
case C.AKEYCODE_CTRL_LEFT:
return key.CodeLeftControl
case C.AKEYCODE_CTRL_RIGHT:
return key.CodeRightControl
case C.AKEYCODE_CAPS_LOCK:
return key.CodeCapsLock
case C.AKEYCODE_SCROLL_LOCK:
case C.AKEYCODE_META_LEFT:
return key.CodeLeftGUI
case C.AKEYCODE_META_RIGHT:
return key.CodeRightGUI
case C.AKEYCODE_FUNCTION:
case C.AKEYCODE_SYSRQ:
case C.AKEYCODE_BREAK:
case C.AKEYCODE_MOVE_HOME:
case C.AKEYCODE_MOVE_END:
case C.AKEYCODE_INSERT:
return key.CodeInsert
case C.AKEYCODE_FORWARD:
case C.AKEYCODE_MEDIA_PLAY:
case C.AKEYCODE_MEDIA_PAUSE:
case C.AKEYCODE_MEDIA_CLOSE:
case C.AKEYCODE_MEDIA_EJECT:
case C.AKEYCODE_MEDIA_RECORD:
case C.AKEYCODE_F1:
return key.CodeF1
case C.AKEYCODE_F2:
return key.CodeF2
case C.AKEYCODE_F3:
return key.CodeF3
case C.AKEYCODE_F4:
return key.CodeF4
case C.AKEYCODE_F5:
return key.CodeF5
case C.AKEYCODE_F6:
return key.CodeF6
case C.AKEYCODE_F7:
return key.CodeF7
case C.AKEYCODE_F8:
return key.CodeF8
case C.AKEYCODE_F9:
return key.CodeF9
case C.AKEYCODE_F10:
return key.CodeF10
case C.AKEYCODE_F11:
return key.CodeF11
case C.AKEYCODE_F12:
return key.CodeF12
case C.AKEYCODE_NUM_LOCK:
return key.CodeKeypadNumLock
case C.AKEYCODE_NUMPAD_0:
return key.CodeKeypad0
case C.AKEYCODE_NUMPAD_1:
return key.CodeKeypad1
case C.AKEYCODE_NUMPAD_2:
return key.CodeKeypad2
case C.AKEYCODE_NUMPAD_3:
return key.CodeKeypad3
case C.AKEYCODE_NUMPAD_4:
return key.CodeKeypad4
case C.AKEYCODE_NUMPAD_5:
return key.CodeKeypad5
case C.AKEYCODE_NUMPAD_6:
return key.CodeKeypad6
case C.AKEYCODE_NUMPAD_7:
return key.CodeKeypad7
case C.AKEYCODE_NUMPAD_8:
return key.CodeKeypad8
case C.AKEYCODE_NUMPAD_9:
return key.CodeKeypad9
case C.AKEYCODE_NUMPAD_DIVIDE:
return key.CodeKeypadSlash
case C.AKEYCODE_NUMPAD_MULTIPLY:
return key.CodeKeypadAsterisk
case C.AKEYCODE_NUMPAD_SUBTRACT:
return key.CodeKeypadHyphenMinus
case C.AKEYCODE_NUMPAD_ADD:
return key.CodeKeypadPlusSign
case C.AKEYCODE_NUMPAD_DOT:
return key.CodeKeypadFullStop
case C.AKEYCODE_NUMPAD_COMMA:
case C.AKEYCODE_NUMPAD_ENTER:
return key.CodeKeypadEnter
case C.AKEYCODE_NUMPAD_EQUALS:
return key.CodeKeypadEqualSign
case C.AKEYCODE_NUMPAD_LEFT_PAREN:
case C.AKEYCODE_NUMPAD_RIGHT_PAREN:
case C.AKEYCODE_VOLUME_MUTE:
return key.CodeMute
case C.AKEYCODE_INFO:
case C.AKEYCODE_CHANNEL_UP:
case C.AKEYCODE_CHANNEL_DOWN:
case C.AKEYCODE_ZOOM_IN:
case C.AKEYCODE_ZOOM_OUT:
case C.AKEYCODE_TV:
case C.AKEYCODE_WINDOW:
case C.AKEYCODE_GUIDE:
case C.AKEYCODE_DVR:
case C.AKEYCODE_BOOKMARK:
case C.AKEYCODE_CAPTIONS:
case C.AKEYCODE_SETTINGS:
case C.AKEYCODE_TV_POWER:
case C.AKEYCODE_TV_INPUT:
case C.AKEYCODE_STB_POWER:
case C.AKEYCODE_STB_INPUT:
case C.AKEYCODE_AVR_POWER:
case C.AKEYCODE_AVR_INPUT:
case C.AKEYCODE_PROG_RED:
case C.AKEYCODE_PROG_GREEN:
case C.AKEYCODE_PROG_YELLOW:
case C.AKEYCODE_PROG_BLUE:
case C.AKEYCODE_APP_SWITCH:
case C.AKEYCODE_BUTTON_1:
case C.AKEYCODE_BUTTON_2:
case C.AKEYCODE_BUTTON_3:
case C.AKEYCODE_BUTTON_4:
case C.AKEYCODE_BUTTON_5:
case C.AKEYCODE_BUTTON_6:
case C.AKEYCODE_BUTTON_7:
case C.AKEYCODE_BUTTON_8:
case C.AKEYCODE_BUTTON_9:
case C.AKEYCODE_BUTTON_10:
case C.AKEYCODE_BUTTON_11:
case C.AKEYCODE_BUTTON_12:
case C.AKEYCODE_BUTTON_13:
case C.AKEYCODE_BUTTON_14:
case C.AKEYCODE_BUTTON_15:
case C.AKEYCODE_BUTTON_16:
case C.AKEYCODE_LANGUAGE_SWITCH:
case C.AKEYCODE_MANNER_MODE:
case C.AKEYCODE_3D_MODE:
case C.AKEYCODE_CONTACTS:
case C.AKEYCODE_CALENDAR:
case C.AKEYCODE_MUSIC:
case C.AKEYCODE_CALCULATOR:
}
/* Defined in an NDK API version beyond what we use today:
C.AKEYCODE_ASSIST
C.AKEYCODE_BRIGHTNESS_DOWN
C.AKEYCODE_BRIGHTNESS_UP
C.AKEYCODE_EISU
C.AKEYCODE_HENKAN
C.AKEYCODE_KANA
C.AKEYCODE_KATAKANA_HIRAGANA
C.AKEYCODE_MEDIA_AUDIO_TRACK
C.AKEYCODE_MUHENKAN
C.AKEYCODE_RO
C.AKEYCODE_YEN
C.AKEYCODE_ZENKAKU_HANKAKU
*/
return key.CodeUnknown
}

213
app/app.go Normal file
View file

@ -0,0 +1,213 @@
// Copyright 2014 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.
//go:build linux || darwin || windows
package app
import (
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/gl"
_ "golang.org/x/mobile/internal/mobileinit"
)
// Main is called by the main.main function to run the mobile application.
//
// It calls f on the App, in a separate goroutine, as some OS-specific
// libraries require being on 'the main thread'.
func Main(f func(App)) {
main(f)
}
// App is how a GUI mobile application interacts with the OS.
type App interface {
// Events returns the events channel. It carries events from the system to
// the app. The type of such events include:
// - lifecycle.Event
// - mouse.Event
// - paint.Event
// - size.Event
// - touch.Event
// from the golang.org/x/mobile/event/etc packages. Other packages may
// define other event types that are carried on this channel.
Events() <-chan interface{}
// Send sends an event on the events channel. It does not block.
Send(event interface{})
// Publish flushes any pending drawing commands, such as OpenGL calls, and
// swaps the back buffer to the screen.
Publish() PublishResult
// TODO: replace filters (and the Events channel) with a NextEvent method?
// Filter calls each registered event filter function in sequence.
Filter(event interface{}) interface{}
// RegisterFilter registers a event filter function to be called by Filter. The
// function can return a different event, or return nil to consume the event,
// but the function can also return its argument unchanged, where its purpose
// is to trigger a side effect rather than modify the event.
RegisterFilter(f func(interface{}) interface{})
}
// PublishResult is the result of an App.Publish call.
type PublishResult struct {
// BackBufferPreserved is whether the contents of the back buffer was
// preserved. If false, the contents are undefined.
BackBufferPreserved bool
}
var theApp = &app{
eventsOut: make(chan interface{}),
lifecycleStage: lifecycle.StageDead,
publish: make(chan struct{}),
publishResult: make(chan PublishResult),
}
func init() {
theApp.eventsIn = pump(theApp.eventsOut)
theApp.glctx, theApp.worker = gl.NewContext()
}
func (a *app) sendLifecycle(to lifecycle.Stage) {
if a.lifecycleStage == to {
return
}
a.eventsIn <- lifecycle.Event{
From: a.lifecycleStage,
To: to,
DrawContext: a.glctx,
}
a.lifecycleStage = to
}
type app struct {
filters []func(interface{}) interface{}
eventsOut chan interface{}
eventsIn chan interface{}
lifecycleStage lifecycle.Stage
publish chan struct{}
publishResult chan PublishResult
glctx gl.Context
worker gl.Worker
}
func (a *app) Events() <-chan interface{} {
return a.eventsOut
}
func (a *app) Send(event interface{}) {
a.eventsIn <- event
}
func (a *app) Publish() PublishResult {
// gl.Flush is a lightweight (on modern GL drivers) blocking call
// that ensures all GL functions pending in the gl package have
// been passed onto the GL driver before the app package attempts
// to swap the screen buffer.
//
// This enforces that the final receive (for this paint cycle) on
// gl.WorkAvailable happens before the send on endPaint.
a.glctx.Flush()
a.publish <- struct{}{}
return <-a.publishResult
}
func (a *app) Filter(event interface{}) interface{} {
for _, f := range a.filters {
event = f(event)
}
return event
}
func (a *app) RegisterFilter(f func(interface{}) interface{}) {
a.filters = append(a.filters, f)
}
type stopPumping struct{}
// pump returns a channel src such that sending on src will eventually send on
// dst, in order, but that src will always be ready to send/receive soon, even
// if dst currently isn't. It is effectively an infinitely buffered channel.
//
// In particular, goroutine A sending on src will not deadlock even if goroutine
// B that's responsible for receiving on dst is currently blocked trying to
// send to A on a separate channel.
//
// Send a stopPumping on the src channel to close the dst channel after all queued
// events are sent on dst. After that, other goroutines can still send to src,
// so that such sends won't block forever, but such events will be ignored.
func pump(dst chan interface{}) (src chan interface{}) {
src = make(chan interface{})
go func() {
// initialSize is the initial size of the circular buffer. It must be a
// power of 2.
const initialSize = 16
i, j, buf, mask := 0, 0, make([]interface{}, initialSize), initialSize-1
srcActive := true
for {
maybeDst := dst
if i == j {
maybeDst = nil
}
if maybeDst == nil && !srcActive {
// Pump is stopped and empty.
break
}
select {
case maybeDst <- buf[i&mask]:
buf[i&mask] = nil
i++
case e := <-src:
if _, ok := e.(stopPumping); ok {
srcActive = false
continue
}
if !srcActive {
continue
}
// Allocate a bigger buffer if necessary.
if i+len(buf) == j {
b := make([]interface{}, 2*len(buf))
n := copy(b, buf[j&mask:])
copy(b[n:], buf[:j&mask])
i, j = 0, len(buf)
buf, mask = b, len(b)-1
}
buf[j&mask] = e
j++
}
}
close(dst)
// Block forever.
for range src {
}
}()
return src
}
// TODO: do this for all build targets, not just linux (x11 and Android)? If
// so, should package gl instead of this package call RegisterFilter??
//
// TODO: does Android need this?? It seems to work without it (Nexus 7,
// KitKat). If only x11 needs this, should we move this to x11.go??
func (a *app) registerGLViewportFilter() {
a.RegisterFilter(func(e interface{}) interface{} {
if e, ok := e.(size.Event); ok {
a.glctx.Viewport(0, 0, e.WidthPx, e.HeightPx)
}
return e
})
}

237
app/app_test.go Normal file
View file

@ -0,0 +1,237 @@
// 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 app_test
import (
"fmt"
"image"
"image/color"
_ "image/png"
"net"
"os"
"os/exec"
"strings"
"testing"
"time"
"golang.org/x/mobile/app/internal/apptest"
"golang.org/x/mobile/event/size"
)
// TestAndroidApp tests the lifecycle, event, and window semantics of a
// simple android app.
//
// Beyond testing the app package, the goal is to eventually have
// helper libraries that make tests like these easy to write. Hopefully
// having a user of such a fictional package will help illuminate the way.
func TestAndroidApp(t *testing.T) {
t.Skip("see issue #23835")
if _, err := exec.Command("which", "adb").CombinedOutput(); err != nil {
t.Skip("command adb not found, skipping")
}
devicesTxt, err := exec.Command("adb", "devices").CombinedOutput()
if err != nil {
t.Errorf("adb devices failed: %v: %v", err, devicesTxt)
}
deviceCount := 0
for _, d := range strings.Split(strings.TrimSpace(string(devicesTxt)), "\n") {
if strings.Contains(d, "List of devices") {
continue
}
// TODO(crawshaw): I believe some unusable devices can appear in the
// list with note on them, but I cannot reproduce this right now.
deviceCount++
}
if deviceCount == 0 {
t.Skip("no android devices attached")
}
run(t, "gomobile", "version")
origWD, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
tmpdir, err := os.MkdirTemp("", "app-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
if err := os.Chdir(tmpdir); err != nil {
t.Fatal(err)
}
defer os.Chdir(origWD)
run(t, "gomobile", "install", "golang.org/x/mobile/app/internal/testapp")
ln, err := net.Listen("tcp4", "localhost:0")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
localaddr := fmt.Sprintf("tcp:%d", ln.Addr().(*net.TCPAddr).Port)
t.Logf("local address: %s", localaddr)
exec.Command("adb", "reverse", "--remove", "tcp:"+apptest.Port).Run() // ignore failure
run(t, "adb", "reverse", "tcp:"+apptest.Port, localaddr)
const (
KeycodePower = "26"
KeycodeUnlock = "82"
)
run(t, "adb", "shell", "input", "keyevent", KeycodePower)
run(t, "adb", "shell", "input", "keyevent", KeycodeUnlock)
const (
rotationPortrait = "0"
rotationLandscape = "1"
)
rotate := func(rotation string) {
run(t, "adb", "shell", "content", "insert", "--uri", "content://settings/system", "--bind", "name:s:user_rotation", "--bind", "value:i:"+rotation)
}
// turn off automatic rotation and start in portrait
run(t, "adb", "shell", "content", "insert", "--uri", "content://settings/system", "--bind", "name:s:accelerometer_rotation", "--bind", "value:i:0")
rotate(rotationPortrait)
// start testapp
run(t,
"adb", "shell", "am", "start", "-n",
"org.golang.testapp/org.golang.app.GoNativeActivity",
)
var conn net.Conn
connDone := make(chan struct{})
go func() {
conn, err = ln.Accept()
connDone <- struct{}{}
}()
select {
case <-time.After(5 * time.Second):
t.Fatal("timeout waiting for testapp to dial host")
case <-connDone:
if err != nil {
t.Fatalf("ln.Accept: %v", err)
}
}
defer conn.Close()
comm := &apptest.Comm{
Conn: conn,
Fatalf: t.Fatalf,
Printf: t.Logf,
}
var pixelsPerPt float32
var orientation size.Orientation
comm.Recv("hello_from_testapp")
comm.Send("hello_from_host")
comm.Recv("lifecycle_visible")
comm.Recv("size", &pixelsPerPt, &orientation)
if pixelsPerPt < 0.1 {
t.Fatalf("bad pixelsPerPt: %f", pixelsPerPt)
}
// A single paint event is sent when the lifecycle enters
// StageVisible, and after the end of a touch event.
var color string
comm.Recv("paint", &color)
// Ignore the first paint color, it may be slow making it to the screen.
rotate(rotationLandscape)
comm.Recv("size", &pixelsPerPt, &orientation)
if want := size.OrientationLandscape; orientation != want {
t.Errorf("want orientation %d, got %d", want, orientation)
}
var x, y int
var ty string
tap(t, 50, 260)
comm.Recv("touch", &ty, &x, &y)
if ty != "begin" || x != 50 || y != 260 {
t.Errorf("want touch begin(50, 260), got %s(%d,%d)", ty, x, y)
}
comm.Recv("touch", &ty, &x, &y)
if ty != "end" || x != 50 || y != 260 {
t.Errorf("want touch end(50, 260), got %s(%d,%d)", ty, x, y)
}
comm.Recv("paint", &color)
if gotColor := currentColor(t); color != gotColor {
t.Errorf("app reports color %q, but saw %q", color, gotColor)
}
rotate(rotationPortrait)
comm.Recv("size", &pixelsPerPt, &orientation)
if want := size.OrientationPortrait; orientation != want {
t.Errorf("want orientation %d, got %d", want, orientation)
}
tap(t, 50, 260)
comm.Recv("touch", &ty, &x, &y) // touch begin
comm.Recv("touch", &ty, &x, &y) // touch end
comm.Recv("paint", &color)
if gotColor := currentColor(t); color != gotColor {
t.Errorf("app reports color %q, but saw %q", color, gotColor)
}
// TODO: lifecycle testing (NOTE: adb shell input keyevent 4 is the back button)
}
func currentColor(t *testing.T) string {
file := fmt.Sprintf("app-screen-%d.png", time.Now().Unix())
run(t, "adb", "shell", "screencap", "-p", "/data/local/tmp/"+file)
run(t, "adb", "pull", "/data/local/tmp/"+file)
run(t, "adb", "shell", "rm", "/data/local/tmp/"+file)
defer os.Remove(file)
f, err := os.Open(file)
if err != nil {
t.Errorf("currentColor: cannot open screencap: %v", err)
return ""
}
m, _, err := image.Decode(f)
if err != nil {
t.Errorf("currentColor: cannot decode screencap: %v", err)
return ""
}
var center color.Color
{
b := m.Bounds()
x, y := b.Min.X+(b.Max.X-b.Min.X)/2, b.Min.Y+(b.Max.Y-b.Min.Y)/2
center = m.At(x, y)
}
r, g, b, _ := center.RGBA()
switch {
case r == 0xffff && g == 0x0000 && b == 0x0000:
return "red"
case r == 0x0000 && g == 0xffff && b == 0x0000:
return "green"
case r == 0x0000 && g == 0x0000 && b == 0xffff:
return "blue"
default:
return fmt.Sprintf("indeterminate: %v", center)
}
}
func tap(t *testing.T, x, y int) {
run(t, "adb", "shell", "input", "tap", fmt.Sprintf("%d", x), fmt.Sprintf("%d", y))
}
func run(t *testing.T, cmdName string, arg ...string) {
cmd := exec.Command(cmdName, arg...)
t.Log(strings.Join(cmd.Args, " "))
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%s %v: %s", strings.Join(cmd.Args, " "), err, out)
}
}

495
app/darwin_desktop.go Normal file
View file

@ -0,0 +1,495 @@
// Copyright 2014 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.
//go:build darwin && !ios
package app
// Simple on-screen app debugging for OS X. Not an officially supported
// development target for apps, as screens with mice are very different
// than screens with touch panels.
/*
#cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION
#cgo LDFLAGS: -framework Cocoa -framework OpenGL
#import <Carbon/Carbon.h> // for HIToolbox/Events.h
#import <Cocoa/Cocoa.h>
#include <pthread.h>
void runApp(void);
void stopApp(void);
void makeCurrentContext(GLintptr);
uint64 threadID();
*/
import "C"
import (
"log"
"runtime"
"sync"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/geom"
)
var initThreadID uint64
func init() {
// Lock the goroutine responsible for initialization to an OS thread.
// This means the goroutine running main (and calling runApp below)
// is locked to the OS thread that started the program. This is
// necessary for the correct delivery of Cocoa events to the process.
//
// A discussion on this topic:
// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
runtime.LockOSThread()
initThreadID = uint64(C.threadID())
}
func main(f func(App)) {
if tid := uint64(C.threadID()); tid != initThreadID {
log.Fatalf("app.Main called on thread %d, but app.init ran on %d", tid, initThreadID)
}
go func() {
f(theApp)
C.stopApp()
}()
C.runApp()
}
// loop is the primary drawing loop.
//
// After Cocoa has captured the initial OS thread for processing Cocoa
// events in runApp, it starts loop on another goroutine. It is locked
// to an OS thread for its OpenGL context.
//
// The loop processes GL calls until a publish event appears.
// Then it runs any remaining GL calls and flushes the screen.
//
// As NSOpenGLCPSwapInterval is set to 1, the call to CGLFlushDrawable
// blocks until the screen refresh.
func (a *app) loop(ctx C.GLintptr) {
runtime.LockOSThread()
C.makeCurrentContext(ctx)
workAvailable := a.worker.WorkAvailable()
for {
select {
case <-workAvailable:
a.worker.DoWork()
case <-theApp.publish:
loop1:
for {
select {
case <-workAvailable:
a.worker.DoWork()
default:
break loop1
}
}
C.CGLFlushDrawable(C.CGLGetCurrentContext())
theApp.publishResult <- PublishResult{}
select {
case drawDone <- struct{}{}:
default:
}
}
}
}
var drawDone = make(chan struct{})
// drawgl is used by Cocoa to occasionally request screen updates.
//
//export drawgl
func drawgl() {
switch theApp.lifecycleStage {
case lifecycle.StageFocused, lifecycle.StageVisible:
theApp.Send(paint.Event{
External: true,
})
<-drawDone
}
}
//export startloop
func startloop(ctx C.GLintptr) {
go theApp.loop(ctx)
}
var windowHeightPx float32
//export setGeom
func setGeom(pixelsPerPt float32, widthPx, heightPx int) {
windowHeightPx = float32(heightPx)
theApp.eventsIn <- size.Event{
WidthPx: widthPx,
HeightPx: heightPx,
WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
PixelsPerPt: pixelsPerPt,
}
}
var touchEvents struct {
sync.Mutex
pending []touch.Event
}
func sendTouch(t touch.Type, x, y float32) {
theApp.eventsIn <- touch.Event{
X: x,
Y: windowHeightPx - y,
Sequence: 0,
Type: t,
}
}
//export eventMouseDown
func eventMouseDown(x, y float32) { sendTouch(touch.TypeBegin, x, y) }
//export eventMouseDragged
func eventMouseDragged(x, y float32) { sendTouch(touch.TypeMove, x, y) }
//export eventMouseEnd
func eventMouseEnd(x, y float32) { sendTouch(touch.TypeEnd, x, y) }
//export lifecycleDead
func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
//export eventKey
func eventKey(runeVal int32, direction uint8, code uint16, flags uint32) {
var modifiers key.Modifiers
for _, mod := range mods {
if flags&mod.flags == mod.flags {
modifiers |= mod.mod
}
}
theApp.eventsIn <- key.Event{
Rune: convRune(rune(runeVal)),
Code: convVirtualKeyCode(code),
Modifiers: modifiers,
Direction: key.Direction(direction),
}
}
//export eventFlags
func eventFlags(flags uint32) {
for _, mod := range mods {
if flags&mod.flags == mod.flags && lastFlags&mod.flags != mod.flags {
eventKey(-1, uint8(key.DirPress), mod.code, flags)
}
if lastFlags&mod.flags == mod.flags && flags&mod.flags != mod.flags {
eventKey(-1, uint8(key.DirRelease), mod.code, flags)
}
}
lastFlags = flags
}
var lastFlags uint32
var mods = [...]struct {
flags uint32
code uint16
mod key.Modifiers
}{
// Left and right variants of modifier keys have their own masks,
// but they are not documented. These were determined empirically.
{1<<17 | 0x102, C.kVK_Shift, key.ModShift},
{1<<17 | 0x104, C.kVK_RightShift, key.ModShift},
{1<<18 | 0x101, C.kVK_Control, key.ModControl},
// TODO key.ControlRight
{1<<19 | 0x120, C.kVK_Option, key.ModAlt},
{1<<19 | 0x140, C.kVK_RightOption, key.ModAlt},
{1<<20 | 0x108, C.kVK_Command, key.ModMeta},
{1<<20 | 0x110, C.kVK_Command, key.ModMeta}, // TODO: missing kVK_RightCommand
}
//export lifecycleAlive
func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
//export lifecycleVisible
func lifecycleVisible() {
theApp.sendLifecycle(lifecycle.StageVisible)
}
//export lifecycleFocused
func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
// convRune marks the Carbon/Cocoa private-range unicode rune representing
// a non-unicode key event to -1, used for Rune in the key package.
//
// http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
func convRune(r rune) rune {
if '\uE000' <= r && r <= '\uF8FF' {
return -1
}
return r
}
// convVirtualKeyCode converts a Carbon/Cocoa virtual key code number
// into the standard keycodes used by the key package.
//
// To get a sense of the key map, see the diagram on
//
// http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes
func convVirtualKeyCode(vkcode uint16) key.Code {
switch vkcode {
case C.kVK_ANSI_A:
return key.CodeA
case C.kVK_ANSI_B:
return key.CodeB
case C.kVK_ANSI_C:
return key.CodeC
case C.kVK_ANSI_D:
return key.CodeD
case C.kVK_ANSI_E:
return key.CodeE
case C.kVK_ANSI_F:
return key.CodeF
case C.kVK_ANSI_G:
return key.CodeG
case C.kVK_ANSI_H:
return key.CodeH
case C.kVK_ANSI_I:
return key.CodeI
case C.kVK_ANSI_J:
return key.CodeJ
case C.kVK_ANSI_K:
return key.CodeK
case C.kVK_ANSI_L:
return key.CodeL
case C.kVK_ANSI_M:
return key.CodeM
case C.kVK_ANSI_N:
return key.CodeN
case C.kVK_ANSI_O:
return key.CodeO
case C.kVK_ANSI_P:
return key.CodeP
case C.kVK_ANSI_Q:
return key.CodeQ
case C.kVK_ANSI_R:
return key.CodeR
case C.kVK_ANSI_S:
return key.CodeS
case C.kVK_ANSI_T:
return key.CodeT
case C.kVK_ANSI_U:
return key.CodeU
case C.kVK_ANSI_V:
return key.CodeV
case C.kVK_ANSI_W:
return key.CodeW
case C.kVK_ANSI_X:
return key.CodeX
case C.kVK_ANSI_Y:
return key.CodeY
case C.kVK_ANSI_Z:
return key.CodeZ
case C.kVK_ANSI_1:
return key.Code1
case C.kVK_ANSI_2:
return key.Code2
case C.kVK_ANSI_3:
return key.Code3
case C.kVK_ANSI_4:
return key.Code4
case C.kVK_ANSI_5:
return key.Code5
case C.kVK_ANSI_6:
return key.Code6
case C.kVK_ANSI_7:
return key.Code7
case C.kVK_ANSI_8:
return key.Code8
case C.kVK_ANSI_9:
return key.Code9
case C.kVK_ANSI_0:
return key.Code0
// TODO: move the rest of these codes to constants in key.go
// if we are happy with them.
case C.kVK_Return:
return key.CodeReturnEnter
case C.kVK_Escape:
return key.CodeEscape
case C.kVK_Delete:
return key.CodeDeleteBackspace
case C.kVK_Tab:
return key.CodeTab
case C.kVK_Space:
return key.CodeSpacebar
case C.kVK_ANSI_Minus:
return key.CodeHyphenMinus
case C.kVK_ANSI_Equal:
return key.CodeEqualSign
case C.kVK_ANSI_LeftBracket:
return key.CodeLeftSquareBracket
case C.kVK_ANSI_RightBracket:
return key.CodeRightSquareBracket
case C.kVK_ANSI_Backslash:
return key.CodeBackslash
// 50: Keyboard Non-US "#" and ~
case C.kVK_ANSI_Semicolon:
return key.CodeSemicolon
case C.kVK_ANSI_Quote:
return key.CodeApostrophe
case C.kVK_ANSI_Grave:
return key.CodeGraveAccent
case C.kVK_ANSI_Comma:
return key.CodeComma
case C.kVK_ANSI_Period:
return key.CodeFullStop
case C.kVK_ANSI_Slash:
return key.CodeSlash
case C.kVK_CapsLock:
return key.CodeCapsLock
case C.kVK_F1:
return key.CodeF1
case C.kVK_F2:
return key.CodeF2
case C.kVK_F3:
return key.CodeF3
case C.kVK_F4:
return key.CodeF4
case C.kVK_F5:
return key.CodeF5
case C.kVK_F6:
return key.CodeF6
case C.kVK_F7:
return key.CodeF7
case C.kVK_F8:
return key.CodeF8
case C.kVK_F9:
return key.CodeF9
case C.kVK_F10:
return key.CodeF10
case C.kVK_F11:
return key.CodeF11
case C.kVK_F12:
return key.CodeF12
// 70: PrintScreen
// 71: Scroll Lock
// 72: Pause
// 73: Insert
case C.kVK_Home:
return key.CodeHome
case C.kVK_PageUp:
return key.CodePageUp
case C.kVK_ForwardDelete:
return key.CodeDeleteForward
case C.kVK_End:
return key.CodeEnd
case C.kVK_PageDown:
return key.CodePageDown
case C.kVK_RightArrow:
return key.CodeRightArrow
case C.kVK_LeftArrow:
return key.CodeLeftArrow
case C.kVK_DownArrow:
return key.CodeDownArrow
case C.kVK_UpArrow:
return key.CodeUpArrow
case C.kVK_ANSI_KeypadClear:
return key.CodeKeypadNumLock
case C.kVK_ANSI_KeypadDivide:
return key.CodeKeypadSlash
case C.kVK_ANSI_KeypadMultiply:
return key.CodeKeypadAsterisk
case C.kVK_ANSI_KeypadMinus:
return key.CodeKeypadHyphenMinus
case C.kVK_ANSI_KeypadPlus:
return key.CodeKeypadPlusSign
case C.kVK_ANSI_KeypadEnter:
return key.CodeKeypadEnter
case C.kVK_ANSI_Keypad1:
return key.CodeKeypad1
case C.kVK_ANSI_Keypad2:
return key.CodeKeypad2
case C.kVK_ANSI_Keypad3:
return key.CodeKeypad3
case C.kVK_ANSI_Keypad4:
return key.CodeKeypad4
case C.kVK_ANSI_Keypad5:
return key.CodeKeypad5
case C.kVK_ANSI_Keypad6:
return key.CodeKeypad6
case C.kVK_ANSI_Keypad7:
return key.CodeKeypad7
case C.kVK_ANSI_Keypad8:
return key.CodeKeypad8
case C.kVK_ANSI_Keypad9:
return key.CodeKeypad9
case C.kVK_ANSI_Keypad0:
return key.CodeKeypad0
case C.kVK_ANSI_KeypadDecimal:
return key.CodeKeypadFullStop
case C.kVK_ANSI_KeypadEquals:
return key.CodeKeypadEqualSign
case C.kVK_F13:
return key.CodeF13
case C.kVK_F14:
return key.CodeF14
case C.kVK_F15:
return key.CodeF15
case C.kVK_F16:
return key.CodeF16
case C.kVK_F17:
return key.CodeF17
case C.kVK_F18:
return key.CodeF18
case C.kVK_F19:
return key.CodeF19
case C.kVK_F20:
return key.CodeF20
// 116: Keyboard Execute
case C.kVK_Help:
return key.CodeHelp
// 118: Keyboard Menu
// 119: Keyboard Select
// 120: Keyboard Stop
// 121: Keyboard Again
// 122: Keyboard Undo
// 123: Keyboard Cut
// 124: Keyboard Copy
// 125: Keyboard Paste
// 126: Keyboard Find
case C.kVK_Mute:
return key.CodeMute
case C.kVK_VolumeUp:
return key.CodeVolumeUp
case C.kVK_VolumeDown:
return key.CodeVolumeDown
// 130: Keyboard Locking Caps Lock
// 131: Keyboard Locking Num Lock
// 132: Keyboard Locking Scroll Lock
// 133: Keyboard Comma
// 134: Keyboard Equal Sign
// ...: Bunch of stuff
case C.kVK_Control:
return key.CodeLeftControl
case C.kVK_Shift:
return key.CodeLeftShift
case C.kVK_Option:
return key.CodeLeftAlt
case C.kVK_Command:
return key.CodeLeftGUI
case C.kVK_RightControl:
return key.CodeRightControl
case C.kVK_RightShift:
return key.CodeRightShift
case C.kVK_RightOption:
return key.CodeRightAlt
// TODO key.CodeRightGUI
default:
return key.CodeUnknown
}
}

251
app/darwin_desktop.m Normal file
View file

@ -0,0 +1,251 @@
// Copyright 2014 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.
//go:build darwin && !ios
// +build darwin
// +build !ios
#include "_cgo_export.h"
#include <pthread.h>
#include <stdio.h>
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <OpenGL/gl3.h>
void makeCurrentContext(GLintptr context) {
NSOpenGLContext* ctx = (NSOpenGLContext*)context;
[ctx makeCurrentContext];
}
uint64 threadID() {
uint64 id;
if (pthread_threadid_np(pthread_self(), &id)) {
abort();
}
return id;
}
@interface MobileGLView : NSOpenGLView<NSApplicationDelegate, NSWindowDelegate>
{
}
@end
@implementation MobileGLView
- (void)prepareOpenGL {
[super prepareOpenGL];
[self setWantsBestResolutionOpenGLSurface:YES];
GLint swapInt = 1;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
#pragma clang diagnostic pop
// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
// But VBAs don't exist in ES 2. So we bind a default one.
GLuint vba;
glGenVertexArrays(1, &vba);
glBindVertexArray(vba);
startloop((GLintptr)[self openGLContext]);
}
- (void)reshape {
[super reshape];
// Calculate screen PPI.
//
// Note that the backingScaleFactor converts from logical
// pixels to actual pixels, but both of these units vary
// independently from real world size. E.g.
//
// 13" Retina Macbook Pro, 2560x1600, 227ppi, backingScaleFactor=2, scale=3.15
// 15" Retina Macbook Pro, 2880x1800, 220ppi, backingScaleFactor=2, scale=3.06
// 27" iMac, 2560x1440, 109ppi, backingScaleFactor=1, scale=1.51
// 27" Retina iMac, 5120x2880, 218ppi, backingScaleFactor=2, scale=3.03
NSScreen *screen = [NSScreen mainScreen];
double screenPixW = [screen frame].size.width * [screen backingScaleFactor];
CGDirectDisplayID display = (CGDirectDisplayID)[[[screen deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
CGSize screenSizeMM = CGDisplayScreenSize(display); // in millimeters
float ppi = 25.4 * screenPixW / screenSizeMM.width;
float pixelsPerPt = ppi/72.0;
// The width and height reported to the geom package are the
// bounds of the OpenGL view. Several steps are necessary.
// First, [self bounds] gives us the number of logical pixels
// in the view. Multiplying this by the backingScaleFactor
// gives us the number of actual pixels.
NSRect r = [self bounds];
int w = r.size.width * [screen backingScaleFactor];
int h = r.size.height * [screen backingScaleFactor];
setGeom(pixelsPerPt, w, h);
}
- (void)drawRect:(NSRect)theRect {
// Called during resize. This gets rid of flicker when resizing.
drawgl();
}
- (void)mouseDown:(NSEvent *)theEvent {
double scale = [[NSScreen mainScreen] backingScaleFactor];
NSPoint p = [theEvent locationInWindow];
eventMouseDown(p.x * scale, p.y * scale);
}
- (void)mouseUp:(NSEvent *)theEvent {
double scale = [[NSScreen mainScreen] backingScaleFactor];
NSPoint p = [theEvent locationInWindow];
eventMouseEnd(p.x * scale, p.y * scale);
}
- (void)mouseDragged:(NSEvent *)theEvent {
double scale = [[NSScreen mainScreen] backingScaleFactor];
NSPoint p = [theEvent locationInWindow];
eventMouseDragged(p.x * scale, p.y * scale);
}
- (void)windowDidBecomeKey:(NSNotification *)notification {
lifecycleFocused();
}
- (void)windowDidResignKey:(NSNotification *)notification {
if (![NSApp isHidden]) {
lifecycleVisible();
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
lifecycleAlive();
[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
[self.window makeKeyAndOrderFront:self];
lifecycleVisible();
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
lifecycleDead();
}
- (void)applicationDidHide:(NSNotification *)aNotification {
lifecycleAlive();
}
- (void)applicationWillUnhide:(NSNotification *)notification {
lifecycleVisible();
}
- (void)windowWillClose:(NSNotification *)notification {
lifecycleAlive();
}
@end
@interface MobileResponder : NSResponder
{
}
@end
@implementation MobileResponder
- (void)keyDown:(NSEvent *)theEvent {
[self key:theEvent];
}
- (void)keyUp:(NSEvent *)theEvent {
[self key:theEvent];
}
- (void)key:(NSEvent *)theEvent {
NSRange range = [theEvent.characters rangeOfComposedCharacterSequenceAtIndex:0];
uint8_t buf[4] = {0, 0, 0, 0};
if (![theEvent.characters getBytes:buf
maxLength:4
usedLength:nil
encoding:NSUTF32LittleEndianStringEncoding
options:NSStringEncodingConversionAllowLossy
range:range
remainingRange:nil]) {
NSLog(@"failed to read key event %@", theEvent);
return;
}
uint32_t rune = (uint32_t)buf[0]<<0 | (uint32_t)buf[1]<<8 | (uint32_t)buf[2]<<16 | (uint32_t)buf[3]<<24;
uint8_t direction;
if ([theEvent isARepeat]) {
direction = 0;
} else if (theEvent.type == NSEventTypeKeyDown) {
direction = 1;
} else {
direction = 2;
}
eventKey((int32_t)rune, direction, theEvent.keyCode, theEvent.modifierFlags);
}
- (void)flagsChanged:(NSEvent *)theEvent {
eventFlags(theEvent.modifierFlags);
}
@end
void
runApp(void) {
[NSAutoreleasePool new];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
id menuBar = [[NSMenu new] autorelease];
id menuItem = [[NSMenuItem new] autorelease];
[menuBar addItem:menuItem];
[NSApp setMainMenu:menuBar];
id menu = [[NSMenu new] autorelease];
id name = [[NSProcessInfo processInfo] processName];
id hideMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Hide"
action:@selector(hide:) keyEquivalent:@"h"]
autorelease];
[menu addItem:hideMenuItem];
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Quit"
action:@selector(terminate:) keyEquivalent:@"q"]
autorelease];
[menu addItem:quitMenuItem];
[menuItem setSubmenu:menu];
NSRect rect = NSMakeRect(0, 0, 600, 800);
NSWindow* window = [[[NSWindow alloc] initWithContentRect:rect
styleMask:NSWindowStyleMaskTitled
backing:NSBackingStoreBuffered
defer:NO]
autorelease];
window.styleMask |= NSWindowStyleMaskResizable;
window.styleMask |= NSWindowStyleMaskMiniaturizable;
window.styleMask |= NSWindowStyleMaskClosable;
window.title = name;
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
NSOpenGLPixelFormatAttribute attr[] = {
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 16,
NSOpenGLPFAAccelerated,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAllowOfflineRenderers,
0
};
id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
MobileGLView* view = [[MobileGLView alloc] initWithFrame:rect pixelFormat:pixFormat];
[window setContentView:view];
[window setDelegate:view];
[NSApp setDelegate:view];
window.nextResponder = [[[MobileResponder alloc] init] autorelease];
[NSApp run];
}
void stopApp(void) {
[NSApp terminate:nil];
}

215
app/darwin_ios.go Normal file
View file

@ -0,0 +1,215 @@
// 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.
//go:build darwin && ios
package app
/*
#cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION -DGLES_SILENCE_DEPRECATION
#cgo LDFLAGS: -framework Foundation -framework UIKit -framework GLKit -framework OpenGLES -framework QuartzCore
#include <sys/utsname.h>
#include <stdint.h>
#include <pthread.h>
#include <UIKit/UIDevice.h>
#import <GLKit/GLKit.h>
extern struct utsname sysInfo;
void runApp(void);
void makeCurrentContext(GLintptr ctx);
void swapBuffers(GLintptr ctx);
uint64_t threadID();
*/
import "C"
import (
"log"
"runtime"
"strings"
"sync"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/geom"
)
var initThreadID uint64
func init() {
// Lock the goroutine responsible for initialization to an OS thread.
// This means the goroutine running main (and calling the run function
// below) is locked to the OS thread that started the program. This is
// necessary for the correct delivery of UIKit events to the process.
//
// A discussion on this topic:
// https://groups.google.com/forum/#!msg/golang-nuts/IiWZ2hUuLDA/SNKYYZBelsYJ
runtime.LockOSThread()
initThreadID = uint64(C.threadID())
}
func main(f func(App)) {
if tid := uint64(C.threadID()); tid != initThreadID {
log.Fatalf("app.Run called on thread %d, but app.init ran on %d", tid, initThreadID)
}
go func() {
f(theApp)
// TODO(crawshaw): trigger runApp to return
}()
C.runApp()
panic("unexpected return from app.runApp")
}
var pixelsPerPt float32
var screenScale int // [UIScreen mainScreen].scale, either 1, 2, or 3.
//export setScreen
func setScreen(scale int) {
C.uname(&C.sysInfo)
name := C.GoString(&C.sysInfo.machine[0])
var v float32
switch {
case strings.HasPrefix(name, "iPhone"):
v = 163
case strings.HasPrefix(name, "iPad"):
// TODO: is there a better way to distinguish the iPad Mini?
switch name {
case "iPad2,5", "iPad2,6", "iPad2,7", "iPad4,4", "iPad4,5", "iPad4,6", "iPad4,7":
v = 163 // iPad Mini
default:
v = 132
}
default:
v = 163 // names like i386 and x86_64 are the simulator
}
if v == 0 {
log.Printf("unknown machine: %s", name)
v = 163 // emergency fallback
}
pixelsPerPt = v * float32(scale) / 72
screenScale = scale
}
//export updateConfig
func updateConfig(width, height, orientation int32) {
o := size.OrientationUnknown
switch orientation {
case C.UIDeviceOrientationPortrait, C.UIDeviceOrientationPortraitUpsideDown:
o = size.OrientationPortrait
case C.UIDeviceOrientationLandscapeLeft, C.UIDeviceOrientationLandscapeRight:
o = size.OrientationLandscape
}
widthPx := screenScale * int(width)
heightPx := screenScale * int(height)
theApp.eventsIn <- size.Event{
WidthPx: widthPx,
HeightPx: heightPx,
WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt),
HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt),
PixelsPerPt: pixelsPerPt,
Orientation: o,
}
theApp.eventsIn <- paint.Event{External: true}
}
// touchIDs is the current active touches. The position in the array
// is the ID, the value is the UITouch* pointer value.
//
// It is widely reported that the iPhone can handle up to 5 simultaneous
// touch events, while the iPad can handle 11.
var touchIDs [11]uintptr
var touchEvents struct {
sync.Mutex
pending []touch.Event
}
//export sendTouch
func sendTouch(cTouch, cTouchType uintptr, x, y float32) {
id := -1
for i, val := range touchIDs {
if val == cTouch {
id = i
break
}
}
if id == -1 {
for i, val := range touchIDs {
if val == 0 {
touchIDs[i] = cTouch
id = i
break
}
}
if id == -1 {
panic("out of touchIDs")
}
}
t := touch.Type(cTouchType)
if t == touch.TypeEnd {
touchIDs[id] = 0
}
theApp.eventsIn <- touch.Event{
X: x,
Y: y,
Sequence: touch.Sequence(id),
Type: t,
}
}
//export lifecycleDead
func lifecycleDead() { theApp.sendLifecycle(lifecycle.StageDead) }
//export lifecycleAlive
func lifecycleAlive() { theApp.sendLifecycle(lifecycle.StageAlive) }
//export lifecycleVisible
func lifecycleVisible() { theApp.sendLifecycle(lifecycle.StageVisible) }
//export lifecycleFocused
func lifecycleFocused() { theApp.sendLifecycle(lifecycle.StageFocused) }
//export startloop
func startloop(ctx C.GLintptr) {
go theApp.loop(ctx)
}
// loop is the primary drawing loop.
//
// After UIKit has captured the initial OS thread for processing UIKit
// events in runApp, it starts loop on another goroutine. It is locked
// to an OS thread for its OpenGL context.
func (a *app) loop(ctx C.GLintptr) {
runtime.LockOSThread()
C.makeCurrentContext(ctx)
workAvailable := a.worker.WorkAvailable()
for {
select {
case <-workAvailable:
a.worker.DoWork()
case <-theApp.publish:
loop1:
for {
select {
case <-workAvailable:
a.worker.DoWork()
default:
break loop1
}
}
C.swapBuffers(ctx)
theApp.publishResult <- PublishResult{}
}
}
}

167
app/darwin_ios.m Normal file
View file

@ -0,0 +1,167 @@
// 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.
//go:build darwin && ios
// +build darwin
// +build ios
#include "_cgo_export.h"
#include <pthread.h>
#include <stdio.h>
#include <sys/utsname.h>
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
struct utsname sysInfo;
@interface GoAppAppController : GLKViewController<UIContentContainer, GLKViewDelegate>
@end
@interface GoAppAppDelegate : UIResponder<UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) GoAppAppController *controller;
@end
@implementation GoAppAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
lifecycleAlive();
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.controller = [[GoAppAppController alloc] initWithNibName:nil bundle:nil];
self.window.rootViewController = self.controller;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication * )application {
lifecycleFocused();
}
- (void)applicationWillResignActive:(UIApplication *)application {
lifecycleVisible();
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
lifecycleAlive();
}
- (void)applicationWillTerminate:(UIApplication *)application {
lifecycleDead();
}
@end
@interface GoAppAppController ()
@property (strong, nonatomic) EAGLContext *context;
@property (strong, nonatomic) GLKView *glview;
@end
@implementation GoAppAppController
- (void)viewWillAppear:(BOOL)animated
{
// TODO: replace by swapping out GLKViewController for a UIVIewController.
[super viewWillAppear:animated];
self.paused = YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
self.glview = (GLKView*)self.view;
self.glview.drawableDepthFormat = GLKViewDrawableDepthFormat24;
self.glview.multipleTouchEnabled = true; // TODO expose setting to user.
self.glview.context = self.context;
self.glview.userInteractionEnabled = YES;
self.glview.enableSetNeedsDisplay = YES; // only invoked once
// Do not use the GLKViewController draw loop.
self.paused = YES;
self.resumeOnDidBecomeActive = NO;
self.preferredFramesPerSecond = 0;
int scale = 1;
if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)]) {
scale = (int)[UIScreen mainScreen].scale; // either 1.0, 2.0, or 3.0.
}
setScreen(scale);
CGSize size = [UIScreen mainScreen].bounds.size;
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
updateConfig((int)size.width, (int)size.height, orientation);
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// TODO(crawshaw): come up with a plan to handle animations.
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
updateConfig((int)size.width, (int)size.height, orientation);
}];
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
// Now that we have been asked to do the first draw, disable any
// future draw and hand control over to the Go paint.Event cycle.
self.glview.enableSetNeedsDisplay = NO;
startloop((GLintptr)self.context);
}
#define TOUCH_TYPE_BEGIN 0 // touch.TypeBegin
#define TOUCH_TYPE_MOVE 1 // touch.TypeMove
#define TOUCH_TYPE_END 2 // touch.TypeEnd
static void sendTouches(int change, NSSet* touches) {
CGFloat scale = [UIScreen mainScreen].scale;
for (UITouch* touch in touches) {
CGPoint p = [touch locationInView:touch.view];
sendTouch((GoUintptr)touch, (GoUintptr)change, p.x*scale, p.y*scale);
}
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
sendTouches(TOUCH_TYPE_BEGIN, touches);
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
sendTouches(TOUCH_TYPE_MOVE, touches);
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
sendTouches(TOUCH_TYPE_END, touches);
}
- (void)touchesCanceled:(NSSet*)touches withEvent:(UIEvent*)event {
sendTouches(TOUCH_TYPE_END, touches);
}
@end
void runApp(void) {
char* argv[] = {};
@autoreleasepool {
UIApplicationMain(0, argv, nil, NSStringFromClass([GoAppAppDelegate class]));
}
}
void makeCurrentContext(GLintptr context) {
EAGLContext* ctx = (EAGLContext*)context;
if (![EAGLContext setCurrentContext:ctx]) {
// TODO(crawshaw): determine how terrible this is. Exit?
NSLog(@"failed to set current context");
}
}
void swapBuffers(GLintptr context) {
__block EAGLContext* ctx = (EAGLContext*)context;
dispatch_sync(dispatch_get_main_queue(), ^{
[EAGLContext setCurrentContext:ctx];
[ctx presentRenderbuffer:GL_RENDERBUFFER];
});
}
uint64_t threadID() {
uint64_t id;
if (pthread_threadid_np(pthread_self(), &id)) {
abort();
}
return id;
}

88
app/doc.go Normal file
View file

@ -0,0 +1,88 @@
// Copyright 2014 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 app lets you write portable all-Go apps for Android and iOS.
There are typically two ways to use Go on Android and iOS. The first
is to write a Go library and use `gomobile bind` to generate language
bindings for Java and Objective-C. Building a library does not
require the app package. The `gomobile bind` command produces output
that you can include in an Android Studio or Xcode project. For more
on language bindings, see https://golang.org/x/mobile/cmd/gobind.
The second way is to write an app entirely in Go. The APIs are limited
to those that are portable between both Android and iOS, in particular
OpenGL, audio, and other Android NDK-like APIs. An all-Go app should
use this app package to initialize the app, manage its lifecycle, and
receive events.
# Building apps
Apps written entirely in Go have a main function, and can be built
with `gomobile build`, which directly produces runnable output for
Android and iOS.
The gomobile tool can get installed with go get. For reference, see
https://golang.org/x/mobile/cmd/gomobile.
For detailed instructions and documentation, see
https://golang.org/wiki/Mobile.
# Event processing in Native Apps
The Go runtime is initialized on Android when NativeActivity onCreate is
called, and on iOS when the process starts. In both cases, Go init functions
run before the app lifecycle has started.
An app is expected to call the Main function in main.main. When the function
exits, the app exits. Inside the func passed to Main, call Filter on every
event received, and then switch on its type. Registered filters run when the
event is received, not when it is sent, so that filters run in the same
goroutine as other code that calls OpenGL.
package main
import (
"log"
"golang.org/x/mobile/app"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
)
func main() {
app.Main(func(a app.App) {
for e := range a.Events() {
switch e := a.Filter(e).(type) {
case lifecycle.Event:
// ...
case paint.Event:
log.Print("Call OpenGL here.")
a.Publish()
}
}
})
}
An event is represented by the empty interface type interface{}. Any value can
be an event. Commonly used types include Event types defined by the following
packages:
- golang.org/x/mobile/event/lifecycle
- golang.org/x/mobile/event/mouse
- golang.org/x/mobile/event/paint
- golang.org/x/mobile/event/size
- golang.org/x/mobile/event/touch
For example, touch.Event is the type that represents touch events. Other
packages may define their own events, and send them on an app's event channel.
Other packages can also register event filters, e.g. to manage resources in
response to lifecycle events. Such packages should call:
app.RegisterFilter(etc)
in an init function inside that package.
*/
package app

View file

@ -0,0 +1,67 @@
// 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 apptest provides utilities for testing an app.
//
// It is extremely incomplete, hence it being internal.
// For starters, it should support iOS.
package apptest
import (
"bufio"
"bytes"
"fmt"
"net"
)
// Port is the TCP port used to communicate with the test app.
//
// TODO(crawshaw): find a way to make this configurable. adb am extras?
const Port = "12533"
// Comm is a simple text-based communication protocol.
//
// Assumes all sides are friendly and cooperative and that the
// communication is over at the first sign of trouble.
type Comm struct {
Conn net.Conn
Fatalf func(format string, args ...interface{})
Printf func(format string, args ...interface{})
scanner *bufio.Scanner
}
func (c *Comm) Send(cmd string, args ...interface{}) {
buf := new(bytes.Buffer)
buf.WriteString(cmd)
for _, arg := range args {
buf.WriteRune(' ')
fmt.Fprintf(buf, "%v", arg)
}
buf.WriteRune('\n')
b := buf.Bytes()
c.Printf("comm.send: %s\n", b)
if _, err := c.Conn.Write(b); err != nil {
c.Fatalf("failed to send %s: %v", b, err)
}
}
func (c *Comm) Recv(cmd string, a ...interface{}) {
if c.scanner == nil {
c.scanner = bufio.NewScanner(c.Conn)
}
if !c.scanner.Scan() {
c.Fatalf("failed to recv %q: %v", cmd, c.scanner.Err())
}
text := c.scanner.Text()
c.Printf("comm.recv: %s\n", text)
var recvCmd string
args := append([]interface{}{&recvCmd}, a...)
if _, err := fmt.Sscan(text, args...); err != nil {
c.Fatalf("cannot scan recv command %s: %q: %v", cmd, text, err)
}
if cmd != recvCmd {
c.Fatalf("expecting recv %q, got %v", cmd, text)
}
}

View file

@ -0,0 +1,16 @@
// 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.
//go:build android && (arm || 386 || amd64 || arm64)
// Package callfn provides an android entry point.
//
// It is a separate package from app because it contains Go assembly,
// which does not compile in a package using cgo.
package callfn
// CallFn calls a zero-argument function by its program counter.
// It is only intended for calling main.main. Using it for
// anything else will not end well.
func CallFn(fn uintptr)

View file

@ -0,0 +1,11 @@
// 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.
#include "textflag.h"
#include "funcdata.h"
TEXT ·CallFn(SB),$0-4
MOVL fn+0(FP), AX
CALL AX
RET

View file

@ -0,0 +1,11 @@
// 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.
#include "textflag.h"
#include "funcdata.h"
TEXT ·CallFn(SB),$0-8
MOVQ fn+0(FP), AX
CALL AX
RET

View file

@ -0,0 +1,11 @@
// 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.
#include "textflag.h"
#include "funcdata.h"
TEXT ·CallFn(SB),$0-4
MOVW fn+0(FP), R0
BL (R0)
RET

View file

@ -0,0 +1,11 @@
// Copyright 2016 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.
#include "textflag.h"
#include "funcdata.h"
TEXT ·CallFn(SB),$0-8
MOVD fn+0(FP), R0
BL (R0)
RET

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.golang.testapp"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="15" />
<!-- to talk to the host -->
<uses-permission android:name="android.permission.INTERNET" />
<application android:label="testapp" android:debuggable="true">
<activity android:name="org.golang.app.GoNativeActivity"
android:label="testapp"
android:configChanges="orientation|keyboardHidden">
<meta-data android:name="android.app.lib_name" android:value="testapp" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,94 @@
// 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.
//go:build darwin || linux
// Small test app used by app/app_test.go.
package main
import (
"log"
"net"
"golang.org/x/mobile/app"
"golang.org/x/mobile/app/internal/apptest"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/gl"
)
func main() {
app.Main(func(a app.App) {
var (
glctx gl.Context
visible bool
)
addr := "127.0.0.1:" + apptest.Port
log.Printf("addr: %s", addr)
conn, err := net.Dial("tcp", addr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
log.Printf("dialled")
comm := &apptest.Comm{
Conn: conn,
Fatalf: log.Panicf,
Printf: log.Printf,
}
comm.Send("hello_from_testapp")
comm.Recv("hello_from_host")
color := "red"
sendPainting := false
for e := range a.Events() {
switch e := a.Filter(e).(type) {
case lifecycle.Event:
switch e.Crosses(lifecycle.StageVisible) {
case lifecycle.CrossOn:
comm.Send("lifecycle_visible")
sendPainting = true
visible = true
glctx, _ = e.DrawContext.(gl.Context)
case lifecycle.CrossOff:
comm.Send("lifecycle_not_visible")
visible = false
}
case size.Event:
comm.Send("size", e.PixelsPerPt, e.Orientation)
case paint.Event:
if visible {
if color == "red" {
glctx.ClearColor(1, 0, 0, 1)
} else {
glctx.ClearColor(0, 1, 0, 1)
}
glctx.Clear(gl.COLOR_BUFFER_BIT)
a.Publish()
}
if sendPainting {
comm.Send("paint", color)
sendPainting = false
}
case touch.Event:
comm.Send("touch", e.Type, e.X, e.Y)
if e.Type == touch.TypeEnd {
if color == "red" {
color = "green"
} else {
color = "red"
}
sendPainting = true
// Send a paint event so the screen gets redrawn.
a.Send(paint.Event{})
}
}
}
})
}

83
app/shiny.go Normal file
View file

@ -0,0 +1,83 @@
// 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.
//go:build windows
package app
import (
"log"
"golang.org/x/exp/shiny/driver/gldriver"
"golang.org/x/exp/shiny/screen"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/mouse"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/gl"
)
func main(f func(a App)) {
gldriver.Main(func(s screen.Screen) {
w, err := s.NewWindow(nil)
if err != nil {
log.Fatal(err)
}
defer w.Release()
theApp.glctx = nil
theApp.worker = nil // handled by shiny
go func() {
for range theApp.publish {
res := w.Publish()
theApp.publishResult <- PublishResult{
BackBufferPreserved: res.BackBufferPreserved,
}
}
}()
donec := make(chan struct{})
go func() {
// close the donec channel in a defer statement
// so that we could still be able to return even
// if f panics.
defer close(donec)
f(theApp)
}()
for {
select {
case <-donec:
return
default:
theApp.Send(convertEvent(w.NextEvent()))
}
}
})
}
func convertEvent(e interface{}) interface{} {
switch e := e.(type) {
case lifecycle.Event:
if theApp.glctx == nil {
theApp.glctx = e.DrawContext.(gl.Context)
}
case mouse.Event:
te := touch.Event{
X: e.X,
Y: e.Y,
}
switch e.Direction {
case mouse.DirNone:
te.Type = touch.TypeMove
case mouse.DirPress:
te.Type = touch.TypeBegin
case mouse.DirRelease:
te.Type = touch.TypeEnd
}
return te
}
return e
}

175
app/x11.c Normal file
View file

@ -0,0 +1,175 @@
// Copyright 2014 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.
//go:build linux && !android
// +build linux,!android
#include "_cgo_export.h"
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
static Atom wm_delete_window;
static Window
new_window(Display *x_dpy, EGLDisplay e_dpy, int w, int h, EGLContext *ctx, EGLSurface *surf) {
static const EGLint attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_NONE
};
EGLConfig config;
EGLint num_configs;
if (!eglChooseConfig(e_dpy, attribs, &config, 1, &num_configs)) {
fprintf(stderr, "eglChooseConfig failed\n");
exit(1);
}
EGLint vid;
if (!eglGetConfigAttrib(e_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) {
fprintf(stderr, "eglGetConfigAttrib failed\n");
exit(1);
}
XVisualInfo visTemplate;
visTemplate.visualid = vid;
int num_visuals;
XVisualInfo *visInfo = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals);
if (!visInfo) {
fprintf(stderr, "XGetVisualInfo failed\n");
exit(1);
}
Window root = RootWindow(x_dpy, DefaultScreen(x_dpy));
XSetWindowAttributes attr;
attr.colormap = XCreateColormap(x_dpy, root, visInfo->visual, AllocNone);
if (!attr.colormap) {
fprintf(stderr, "XCreateColormap failed\n");
exit(1);
}
attr.event_mask = StructureNotifyMask | ExposureMask |
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
Window win = XCreateWindow(
x_dpy, root, 0, 0, w, h, 0, visInfo->depth, InputOutput,
visInfo->visual, CWColormap | CWEventMask, &attr);
XFree(visInfo);
XSizeHints sizehints;
sizehints.width = w;
sizehints.height = h;
sizehints.flags = USSize;
XSetNormalHints(x_dpy, win, &sizehints);
XSetStandardProperties(x_dpy, win, "App", "App", None, (char **)NULL, 0, &sizehints);
static const EGLint ctx_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
*ctx = eglCreateContext(e_dpy, config, EGL_NO_CONTEXT, ctx_attribs);
if (!*ctx) {
fprintf(stderr, "eglCreateContext failed\n");
exit(1);
}
*surf = eglCreateWindowSurface(e_dpy, config, win, NULL);
if (!*surf) {
fprintf(stderr, "eglCreateWindowSurface failed\n");
exit(1);
}
return win;
}
Display *x_dpy;
EGLDisplay e_dpy;
EGLContext e_ctx;
EGLSurface e_surf;
Window win;
void
createWindow(void) {
x_dpy = XOpenDisplay(NULL);
if (!x_dpy) {
fprintf(stderr, "XOpenDisplay failed\n");
exit(1);
}
e_dpy = eglGetDisplay(x_dpy);
if (!e_dpy) {
fprintf(stderr, "eglGetDisplay failed\n");
exit(1);
}
EGLint e_major, e_minor;
if (!eglInitialize(e_dpy, &e_major, &e_minor)) {
fprintf(stderr, "eglInitialize failed\n");
exit(1);
}
eglBindAPI(EGL_OPENGL_ES_API);
win = new_window(x_dpy, e_dpy, 600, 800, &e_ctx, &e_surf);
wm_delete_window = XInternAtom(x_dpy, "WM_DELETE_WINDOW", True);
if (wm_delete_window != None) {
XSetWMProtocols(x_dpy, win, &wm_delete_window, 1);
}
XMapWindow(x_dpy, win);
if (!eglMakeCurrent(e_dpy, e_surf, e_surf, e_ctx)) {
fprintf(stderr, "eglMakeCurrent failed\n");
exit(1);
}
// Window size and DPI should be initialized before starting app.
XEvent ev;
while (1) {
if (XCheckMaskEvent(x_dpy, StructureNotifyMask, &ev) == False) {
continue;
}
if (ev.type == ConfigureNotify) {
onResize(ev.xconfigure.width, ev.xconfigure.height);
break;
}
}
}
void
processEvents(void) {
while (XPending(x_dpy)) {
XEvent ev;
XNextEvent(x_dpy, &ev);
switch (ev.type) {
case ButtonPress:
onTouchBegin((float)ev.xbutton.x, (float)ev.xbutton.y);
break;
case ButtonRelease:
onTouchEnd((float)ev.xbutton.x, (float)ev.xbutton.y);
break;
case MotionNotify:
onTouchMove((float)ev.xmotion.x, (float)ev.xmotion.y);
break;
case ConfigureNotify:
onResize(ev.xconfigure.width, ev.xconfigure.height);
break;
case ClientMessage:
if (wm_delete_window != None && (Atom)ev.xclient.data.l[0] == wm_delete_window) {
onStop();
return;
}
break;
}
}
}
void
swapBuffers(void) {
if (eglSwapBuffers(e_dpy, e_surf) == EGL_FALSE) {
fprintf(stderr, "eglSwapBuffer failed\n");
exit(1);
}
}

126
app/x11.go Normal file
View file

@ -0,0 +1,126 @@
// Copyright 2014 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.
//go:build linux && !android
package app
/*
Simple on-screen app debugging for X11. Not an officially supported
development target for apps, as screens with mice are very different
than screens with touch panels.
*/
/*
#cgo LDFLAGS: -lEGL -lGLESv2 -lX11
void createWindow(void);
void processEvents(void);
void swapBuffers(void);
*/
import "C"
import (
"runtime"
"time"
"golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/geom"
)
func init() {
theApp.registerGLViewportFilter()
}
func main(f func(App)) {
runtime.LockOSThread()
workAvailable := theApp.worker.WorkAvailable()
C.createWindow()
// TODO: send lifecycle events when e.g. the X11 window is iconified or moved off-screen.
theApp.sendLifecycle(lifecycle.StageFocused)
// TODO: translate X11 expose events to shiny paint events, instead of
// sending this synthetic paint event as a hack.
theApp.eventsIn <- paint.Event{}
donec := make(chan struct{})
go func() {
// close the donec channel in a defer statement
// so that we could still be able to return even
// if f panics.
defer close(donec)
f(theApp)
}()
// TODO: can we get the actual vsync signal?
ticker := time.NewTicker(time.Second / 60)
defer ticker.Stop()
var tc <-chan time.Time
for {
select {
case <-donec:
return
case <-workAvailable:
theApp.worker.DoWork()
case <-theApp.publish:
C.swapBuffers()
tc = ticker.C
case <-tc:
tc = nil
theApp.publishResult <- PublishResult{}
}
C.processEvents()
}
}
//export onResize
func onResize(w, h int) {
// TODO(nigeltao): don't assume 72 DPI. DisplayWidth and DisplayWidthMM
// is probably the best place to start looking.
pixelsPerPt := float32(1)
theApp.eventsIn <- size.Event{
WidthPx: w,
HeightPx: h,
WidthPt: geom.Pt(w),
HeightPt: geom.Pt(h),
PixelsPerPt: pixelsPerPt,
}
}
func sendTouch(t touch.Type, x, y float32) {
theApp.eventsIn <- touch.Event{
X: x,
Y: y,
Sequence: 0, // TODO: button??
Type: t,
}
}
//export onTouchBegin
func onTouchBegin(x, y float32) { sendTouch(touch.TypeBegin, x, y) }
//export onTouchMove
func onTouchMove(x, y float32) { sendTouch(touch.TypeMove, x, y) }
//export onTouchEnd
func onTouchEnd(x, y float32) { sendTouch(touch.TypeEnd, x, y) }
var stopped bool
//export onStop
func onStop() {
if stopped {
return
}
stopped = true
theApp.sendLifecycle(lifecycle.StageDead)
theApp.eventsIn <- stopPumping{}
}

24
asset/asset.go Normal file
View file

@ -0,0 +1,24 @@
// 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.
//go:build darwin || linux || windows
package asset
import "io"
// Open opens a named asset.
//
// Errors are of type *os.PathError.
//
// This must not be called from init when used in android apps.
func Open(name string) (File, error) {
return openAsset(name)
}
// File is an open asset.
type File interface {
io.ReadSeeker
io.Closer
}

109
asset/asset_android.go Normal file
View file

@ -0,0 +1,109 @@
// 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 asset
/*
#cgo LDFLAGS: -landroid
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <jni.h>
#include <stdlib.h>
static AAssetManager* asset_manager_init(uintptr_t java_vm, uintptr_t jni_env, jobject ctx) {
JavaVM* vm = (JavaVM*)java_vm;
JNIEnv* env = (JNIEnv*)jni_env;
// Equivalent to:
// assetManager = ctx.getResources().getAssets();
jclass ctx_clazz = (*env)->FindClass(env, "android/content/Context");
jmethodID getres_id = (*env)->GetMethodID(env, ctx_clazz, "getResources", "()Landroid/content/res/Resources;");
jobject res = (*env)->CallObjectMethod(env, ctx, getres_id);
jclass res_clazz = (*env)->FindClass(env, "android/content/res/Resources");
jmethodID getam_id = (*env)->GetMethodID(env, res_clazz, "getAssets", "()Landroid/content/res/AssetManager;");
jobject am = (*env)->CallObjectMethod(env, res, getam_id);
// Pin the AssetManager and load an AAssetManager from it.
am = (*env)->NewGlobalRef(env, am);
return AAssetManager_fromJava(env, am);
}
*/
import "C"
import (
"fmt"
"io"
"log"
"os"
"sync"
"unsafe"
"golang.org/x/mobile/internal/mobileinit"
)
var assetOnce sync.Once
// asset_manager is the asset manager of the app.
var assetManager *C.AAssetManager
func assetInit() {
err := mobileinit.RunOnJVM(func(vm, env, ctx uintptr) error {
assetManager = C.asset_manager_init(C.uintptr_t(vm), C.uintptr_t(env), C.jobject(ctx))
return nil
})
if err != nil {
log.Fatalf("asset: %v", err)
}
}
func openAsset(name string) (File, error) {
assetOnce.Do(assetInit)
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
a := &asset{
ptr: C.AAssetManager_open(assetManager, cname, C.AASSET_MODE_UNKNOWN),
name: name,
}
if a.ptr == nil {
return nil, a.errorf("open", "bad asset")
}
return a, nil
}
type asset struct {
ptr *C.AAsset
name string
}
func (a *asset) errorf(op string, format string, v ...interface{}) error {
return &os.PathError{
Op: op,
Path: a.name,
Err: fmt.Errorf(format, v...),
}
}
func (a *asset) Read(p []byte) (n int, err error) {
n = int(C.AAsset_read(a.ptr, unsafe.Pointer(&p[0]), C.size_t(len(p))))
if n == 0 && len(p) > 0 {
return 0, io.EOF
}
if n < 0 {
return 0, a.errorf("read", "negative bytes: %d", n)
}
return n, nil
}
func (a *asset) Seek(offset int64, whence int) (int64, error) {
// TODO(crawshaw): use AAsset_seek64 if it is available.
off := C.AAsset_seek(a.ptr, C.off_t(offset), C.int(whence))
if off == -1 {
return 0, a.errorf("seek", "bad result for offset=%d, whence=%d", offset, whence)
}
return int64(off), nil
}
func (a *asset) Close() error {
C.AAsset_close(a.ptr)
return nil
}

23
asset/asset_desktop.go Normal file
View file

@ -0,0 +1,23 @@
// Copyright 2014 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.
//go:build (linux && !android) || (darwin && !ios) || windows
package asset
import (
"os"
"path/filepath"
)
func openAsset(name string) (File, error) {
if !filepath.IsAbs(name) {
name = filepath.Join("assets", name)
}
f, err := os.Open(name)
if err != nil {
return nil, err
}
return f, nil
}

13
asset/asset_ios.go Normal file
View file

@ -0,0 +1,13 @@
// Copyright 2024 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 asset
import (
"os"
)
func openAsset(name string) (File, error) {
return os.Open(name)
}

17
asset/doc.go Normal file
View file

@ -0,0 +1,17 @@
// 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 asset provides access to application-bundled assets.
//
// On Android, assets are accessed via android.content.res.AssetManager.
// These files are stored in the assets/ directory of the app. Any raw asset
// can be accessed by its direct relative name. For example assets/img.png
// can be opened with Open("img.png").
//
// On iOS an asset is a resource stored in the application bundle.
// Resources can be loaded using the same relative paths.
//
// For consistency when debugging on a desktop, assets are read from a
// directory named assets under the current working directory.
package asset

59
bind/bind.go Normal file
View file

@ -0,0 +1,59 @@
// Copyright 2014 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 implements a code generator for gobind.
//
// See the documentation on the gobind command for usage details
// and the list of currently supported types.
// (http://godoc.org/golang.org/x/mobile/cmd/gobind)
package bind
// TODO(crawshaw): slice support
// TODO(crawshaw): channel support
import (
"bytes"
"go/format"
"go/token"
"go/types"
"io"
)
const gobindPreamble = "// Code generated by gobind. DO NOT EDIT.\n\n"
type (
GeneratorConfig struct {
Writer io.Writer
Fset *token.FileSet
Pkg *types.Package
AllPkg []*types.Package
}
fileType int
)
// GenGo generates a Go stub to support foreign language APIs.
func GenGo(conf *GeneratorConfig) error {
buf := new(bytes.Buffer)
g := &goGen{
Generator: &Generator{
Printer: &Printer{Buf: buf, IndentEach: []byte("\t")},
Fset: conf.Fset,
AllPkg: conf.AllPkg,
Pkg: conf.Pkg,
},
}
g.Init()
if err := g.gen(); err != nil {
return err
}
src := buf.Bytes()
srcf, err := format.Source(src)
if err != nil {
conf.Writer.Write(src) // for debugging
return err
}
_, err = conf.Writer.Write(srcf)
return err
}

703
bind/bind_test.go Normal file
View file

@ -0,0 +1,703 @@
// Copyright 2014 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 (
"bytes"
"flag"
"go/ast"
"go/build"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"testing"
"golang.org/x/mobile/internal/importers"
"golang.org/x/mobile/internal/importers/java"
"golang.org/x/mobile/internal/importers/objc"
)
func init() {
log.SetFlags(log.Lshortfile)
}
var updateFlag = flag.Bool("update", false, "Update the golden files.")
var tests = []string{
"", // The universe package with the error type.
"testdata/basictypes.go",
"testdata/structs.go",
"testdata/interfaces.go",
"testdata/issue10788.go",
"testdata/issue12328.go",
"testdata/issue12403.go",
"testdata/issue29559.go",
"testdata/keywords.go",
"testdata/try.go",
"testdata/vars.go",
"testdata/ignore.go",
"testdata/doc.go",
"testdata/underscores.go",
}
var javaTests = []string{
"testdata/java.go",
"testdata/classes.go",
}
var objcTests = []string{
"testdata/objc.go",
"testdata/objcw.go",
}
var fset = token.NewFileSet()
func fileRefs(t *testing.T, filename string, pkgPrefix string) *importers.References {
f, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
if err != nil {
t.Fatalf("%s: %v", filename, err)
}
refs, err := importers.AnalyzeFile(f, pkgPrefix)
if err != nil {
t.Fatalf("%s: %v", filename, err)
}
fakePath := path.Dir(filename)
for i := range refs.Embedders {
refs.Embedders[i].PkgPath = fakePath
}
return refs
}
func typeCheck(t *testing.T, filename string, gopath string) (*types.Package, *ast.File) {
f, err := parser.ParseFile(fset, filename, nil, parser.AllErrors|parser.ParseComments)
if err != nil {
t.Fatalf("%s: %v", filename, err)
}
pkgName := filepath.Base(filename)
pkgName = strings.TrimSuffix(pkgName, ".go")
// typecheck and collect typechecker errors
var conf types.Config
conf.Error = func(err error) {
t.Error(err)
}
if gopath != "" {
conf.Importer = importer.Default()
oldDefault := build.Default
defer func() { build.Default = oldDefault }()
build.Default.GOPATH = gopath
}
pkg, err := conf.Check(pkgName, fset, []*ast.File{f}, nil)
if err != nil {
t.Fatal(err)
}
return pkg, f
}
// diff runs the command "diff a b" and returns its output
func diff(a, b string) string {
var buf bytes.Buffer
var cmd *exec.Cmd
switch runtime.GOOS {
case "plan9":
cmd = exec.Command("/bin/diff", "-c", a, b)
default:
cmd = exec.Command("/usr/bin/diff", "-u", a, b)
}
cmd.Stdout = &buf
cmd.Stderr = &buf
cmd.Run()
return buf.String()
}
func writeTempFile(t *testing.T, name string, contents []byte) string {
f, err := os.CreateTemp("", name)
if err != nil {
t.Fatal(err)
}
if _, err := f.Write(contents); err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
return f.Name()
}
func TestGenObjc(t *testing.T) {
for _, filename := range tests {
var pkg *types.Package
var file *ast.File
if filename != "" {
pkg, file = typeCheck(t, filename, "")
}
var buf bytes.Buffer
g := &ObjcGen{
Generator: &Generator{
Printer: &Printer{Buf: &buf, IndentEach: []byte("\t")},
Fset: fset,
Files: []*ast.File{file},
Pkg: pkg,
},
}
if pkg != nil {
g.AllPkg = []*types.Package{pkg}
}
g.Init(nil)
testcases := []struct {
suffix string
gen func() error
}{
{
".objc.h.golden",
g.GenH,
},
{
".objc.m.golden",
g.GenM,
},
{
".objc.go.h.golden",
g.GenGoH,
},
}
for _, tc := range testcases {
buf.Reset()
if err := tc.gen(); err != nil {
t.Errorf("%s: %v", filename, err)
continue
}
out := writeTempFile(t, "generated"+tc.suffix, buf.Bytes())
defer os.Remove(out)
var golden string
if filename != "" {
golden = filename[:len(filename)-len(".go")]
} else {
golden = "testdata/universe"
}
golden += tc.suffix
if diffstr := diff(golden, out); diffstr != "" {
t.Errorf("%s: does not match Objective-C golden:\n%s", filename, diffstr)
if *updateFlag {
t.Logf("Updating %s...", golden)
err := exec.Command("/bin/cp", out, golden).Run()
if err != nil {
t.Errorf("Update failed: %s", err)
}
}
}
}
}
}
func genObjcPackages(t *testing.T, dir string, cg *ObjcWrapper) {
pkgBase := filepath.Join(dir, "src", "ObjC")
if err := os.MkdirAll(pkgBase, 0700); err != nil {
t.Fatal(err)
}
for i, jpkg := range cg.Packages() {
pkgDir := filepath.Join(pkgBase, jpkg)
if err := os.MkdirAll(pkgDir, 0700); err != nil {
t.Fatal(err)
}
pkgFile := filepath.Join(pkgDir, "package.go")
cg.Buf.Reset()
cg.GenPackage(i)
if err := os.WriteFile(pkgFile, cg.Buf.Bytes(), 0600); err != nil {
t.Fatal(err)
}
}
cg.Buf.Reset()
cg.GenInterfaces()
clsFile := filepath.Join(pkgBase, "interfaces.go")
if err := os.WriteFile(clsFile, cg.Buf.Bytes(), 0600); err != nil {
t.Fatal(err)
}
gocmd := filepath.Join(runtime.GOROOT(), "bin", "go")
cmd := exec.Command(
gocmd,
"install",
"-pkgdir="+filepath.Join(dir, "pkg", build.Default.GOOS+"_"+build.Default.GOARCH),
"ObjC/...",
)
cmd.Env = append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("failed to go install the generated ObjC wrappers: %v: %s", err, string(out))
}
}
func genJavaPackages(t *testing.T, dir string, cg *ClassGen) {
buf := cg.Buf
cg.Buf = new(bytes.Buffer)
pkgBase := filepath.Join(dir, "src", "Java")
if err := os.MkdirAll(pkgBase, 0700); err != nil {
t.Fatal(err)
}
for i, jpkg := range cg.Packages() {
pkgDir := filepath.Join(pkgBase, jpkg)
if err := os.MkdirAll(pkgDir, 0700); err != nil {
t.Fatal(err)
}
pkgFile := filepath.Join(pkgDir, "package.go")
cg.Buf.Reset()
cg.GenPackage(i)
if err := os.WriteFile(pkgFile, cg.Buf.Bytes(), 0600); err != nil {
t.Fatal(err)
}
io.Copy(buf, cg.Buf)
}
cg.Buf.Reset()
cg.GenInterfaces()
clsFile := filepath.Join(pkgBase, "interfaces.go")
if err := os.WriteFile(clsFile, cg.Buf.Bytes(), 0600); err != nil {
t.Fatal(err)
}
io.Copy(buf, cg.Buf)
cg.Buf = buf
gocmd := filepath.Join(runtime.GOROOT(), "bin", "go")
cmd := exec.Command(
gocmd,
"install",
"-pkgdir="+filepath.Join(dir, "pkg", build.Default.GOOS+"_"+build.Default.GOARCH),
"Java/...",
)
cmd.Env = append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("failed to go install the generated Java wrappers: %v: %s", err, string(out))
}
}
func TestGenJava(t *testing.T) {
allTests := tests
if java.IsAvailable() {
allTests = append(append([]string{}, allTests...), javaTests...)
}
for _, filename := range allTests {
var pkg *types.Package
var file *ast.File
var buf bytes.Buffer
var cg *ClassGen
var classes []*java.Class
if filename != "" {
refs := fileRefs(t, filename, "Java/")
imp := &java.Importer{}
var err error
classes, err = imp.Import(refs)
if err != nil {
t.Fatal(err)
}
tmpGopath := ""
if len(classes) > 0 {
tmpGopath, err = os.MkdirTemp(os.TempDir(), "gomobile-bind-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpGopath)
cg = &ClassGen{
Printer: &Printer{
IndentEach: []byte("\t"),
Buf: new(bytes.Buffer),
},
}
cg.Init(classes, refs.Embedders)
genJavaPackages(t, tmpGopath, cg)
cg.Buf = &buf
}
pkg, file = typeCheck(t, filename, tmpGopath)
}
g := &JavaGen{
Generator: &Generator{
Printer: &Printer{Buf: &buf, IndentEach: []byte(" ")},
Fset: fset,
Files: []*ast.File{file},
Pkg: pkg,
},
}
if pkg != nil {
g.AllPkg = []*types.Package{pkg}
}
g.Init(classes)
testCases := []struct {
suffix string
gen func() error
}{
{
".java.golden",
func() error {
for i := range g.ClassNames() {
if err := g.GenClass(i); err != nil {
return err
}
}
return g.GenJava()
},
},
{
".java.c.golden",
func() error {
if cg != nil {
cg.GenC()
}
return g.GenC()
},
},
{
".java.h.golden",
func() error {
if cg != nil {
cg.GenH()
}
return g.GenH()
},
},
}
for _, tc := range testCases {
buf.Reset()
if err := tc.gen(); err != nil {
t.Errorf("%s: %v", filename, err)
continue
}
out := writeTempFile(t, "generated"+tc.suffix, buf.Bytes())
defer os.Remove(out)
var golden string
if filename != "" {
golden = filename[:len(filename)-len(".go")]
} else {
golden = "testdata/universe"
}
golden += tc.suffix
if diffstr := diff(golden, out); diffstr != "" {
t.Errorf("%s: does not match Java golden:\n%s", filename, diffstr)
if *updateFlag {
t.Logf("Updating %s...", golden)
if err := exec.Command("/bin/cp", out, golden).Run(); err != nil {
t.Errorf("Update failed: %s", err)
}
}
}
}
}
}
func TestGenGo(t *testing.T) {
for _, filename := range tests {
var buf bytes.Buffer
var pkg *types.Package
if filename != "" {
pkg, _ = typeCheck(t, filename, "")
}
testGenGo(t, filename, &buf, pkg)
}
}
func TestGenGoJavaWrappers(t *testing.T) {
if !java.IsAvailable() {
t.Skipf("java is not available")
}
for _, filename := range javaTests {
var buf bytes.Buffer
refs := fileRefs(t, filename, "Java/")
imp := &java.Importer{}
classes, err := imp.Import(refs)
if err != nil {
t.Fatal(err)
}
tmpGopath, err := os.MkdirTemp(os.TempDir(), "gomobile-bind-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpGopath)
cg := &ClassGen{
Printer: &Printer{
IndentEach: []byte("\t"),
Buf: &buf,
},
}
cg.Init(classes, refs.Embedders)
genJavaPackages(t, tmpGopath, cg)
pkg, _ := typeCheck(t, filename, tmpGopath)
cg.GenGo()
testGenGo(t, filename, &buf, pkg)
}
}
func TestGenGoObjcWrappers(t *testing.T) {
if runtime.GOOS != "darwin" {
t.Skipf("can only generate objc wrappers on darwin")
}
for _, filename := range objcTests {
var buf bytes.Buffer
refs := fileRefs(t, filename, "ObjC/")
types, err := objc.Import(refs)
if err != nil {
t.Fatal(err)
}
tmpGopath, err := os.MkdirTemp(os.TempDir(), "gomobile-bind-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpGopath)
cg := &ObjcWrapper{
Printer: &Printer{
IndentEach: []byte("\t"),
Buf: &buf,
},
}
var genNames []string
for _, emb := range refs.Embedders {
genNames = append(genNames, emb.Name)
}
cg.Init(types, genNames)
genObjcPackages(t, tmpGopath, cg)
pkg, _ := typeCheck(t, filename, tmpGopath)
cg.GenGo()
testGenGo(t, filename, &buf, pkg)
}
}
func testGenGo(t *testing.T, filename string, buf *bytes.Buffer, pkg *types.Package) {
conf := &GeneratorConfig{
Writer: buf,
Fset: fset,
Pkg: pkg,
}
if pkg != nil {
conf.AllPkg = []*types.Package{pkg}
}
if err := GenGo(conf); err != nil {
t.Errorf("%s: %v", filename, err)
return
}
// TODO(hyangah): let GenGo format the generated go files.
out := writeTempFile(t, "go", gofmt(t, buf.Bytes()))
defer os.Remove(out)
golden := filename
if golden == "" {
golden = "testdata/universe"
}
golden += ".golden"
goldenContents, err := os.ReadFile(golden)
if err != nil {
t.Fatalf("failed to read golden file: %v", err)
}
// format golden file using the current go version's formatting rule.
formattedGolden := writeTempFile(t, "go", gofmt(t, goldenContents))
defer os.Remove(formattedGolden)
if diffstr := diff(formattedGolden, out); diffstr != "" {
t.Errorf("%s: does not match Go golden:\n%s", filename, diffstr)
if *updateFlag {
t.Logf("Updating %s...", golden)
if err := exec.Command("/bin/cp", out, golden).Run(); err != nil {
t.Errorf("Update failed: %s", err)
}
}
}
}
// gofmt formats the collection of Go source files auto-generated by gobind.
func gofmt(t *testing.T, src []byte) []byte {
t.Helper()
buf := &bytes.Buffer{}
mark := []byte(gobindPreamble)
for i, c := range bytes.Split(src, mark) {
if i == 0 {
buf.Write(c)
continue
}
tmp := append(mark, c...)
out, err := format.Source(tmp)
if err != nil {
t.Fatalf("failed to format Go file: error=%v\n----\n%s\n----", err, tmp)
}
if _, err := buf.Write(out); err != nil {
t.Fatalf("failed to write formatted file to buffer: %v", err)
}
}
return buf.Bytes()
}
func TestCustomPrefix(t *testing.T) {
const datafile = "testdata/customprefix.go"
pkg, file := typeCheck(t, datafile, "")
type testCase struct {
golden string
gen func(w io.Writer) error
}
var buf bytes.Buffer
jg := &JavaGen{
JavaPkg: "com.example",
Generator: &Generator{
Printer: &Printer{Buf: &buf, IndentEach: []byte(" ")},
Fset: fset,
AllPkg: []*types.Package{pkg},
Files: []*ast.File{file},
Pkg: pkg,
},
}
jg.Init(nil)
testCases := []testCase{
{
"testdata/customprefix.java.golden",
func(w io.Writer) error {
buf.Reset()
for i := range jg.ClassNames() {
if err := jg.GenClass(i); err != nil {
return err
}
}
if err := jg.GenJava(); err != nil {
return err
}
_, err := io.Copy(w, &buf)
return err
},
},
{
"testdata/customprefix.java.h.golden",
func(w io.Writer) error {
buf.Reset()
if err := jg.GenH(); err != nil {
return err
}
_, err := io.Copy(w, &buf)
return err
},
},
{
"testdata/customprefix.java.c.golden",
func(w io.Writer) error {
buf.Reset()
if err := jg.GenC(); err != nil {
return err
}
_, err := io.Copy(w, &buf)
return err
},
},
}
for _, pref := range []string{"EX", ""} {
og := &ObjcGen{
Prefix: pref,
Generator: &Generator{
Printer: &Printer{Buf: &buf, IndentEach: []byte(" ")},
Fset: fset,
AllPkg: []*types.Package{pkg},
Pkg: pkg,
},
}
og.Init(nil)
testCases = append(testCases, []testCase{
{
"testdata/customprefix" + pref + ".objc.go.h.golden",
func(w io.Writer) error {
buf.Reset()
if err := og.GenGoH(); err != nil {
return err
}
_, err := io.Copy(w, &buf)
return err
},
},
{
"testdata/customprefix" + pref + ".objc.h.golden",
func(w io.Writer) error {
buf.Reset()
if err := og.GenH(); err != nil {
return err
}
_, err := io.Copy(w, &buf)
return err
},
},
{
"testdata/customprefix" + pref + ".objc.m.golden",
func(w io.Writer) error {
buf.Reset()
if err := og.GenM(); err != nil {
return err
}
_, err := io.Copy(w, &buf)
return err
},
},
}...)
}
for _, tc := range testCases {
var buf bytes.Buffer
if err := tc.gen(&buf); err != nil {
t.Errorf("generating %s: %v", tc.golden, err)
continue
}
out := writeTempFile(t, "generated", buf.Bytes())
defer os.Remove(out)
if diffstr := diff(tc.golden, out); diffstr != "" {
t.Errorf("%s: generated file does not match:\b%s", tc.golden, diffstr)
if *updateFlag {
t.Logf("Updating %s...", tc.golden)
err := exec.Command("/bin/cp", out, tc.golden).Run()
if err != nil {
t.Errorf("Update failed: %s", err)
}
}
}
}
}
func TestLowerFirst(t *testing.T) {
testCases := []struct {
in, want string
}{
{"", ""},
{"Hello", "hello"},
{"HelloGopher", "helloGopher"},
{"hello", "hello"},
{"ID", "id"},
{"IDOrName", "idOrName"},
{"ΓειαΣας", "γειαΣας"},
}
for _, tc := range testCases {
if got := lowerFirst(tc.in); got != tc.want {
t.Errorf("lowerFirst(%q) = %q; want %q", tc.in, got, tc.want)
}
}
}
// Test that typeName work for anonymous qualified fields.
func TestSelectorExprTypeName(t *testing.T) {
e, err := parser.ParseExprFrom(fset, "", "struct { bytes.Buffer }", 0)
if err != nil {
t.Fatal(err)
}
ft := e.(*ast.StructType).Fields.List[0].Type
if got, want := typeName(ft), "Buffer"; got != want {
t.Errorf("got: %q; want %q", got, want)
}
}

575
bind/gen.go Normal file
View file

@ -0,0 +1,575 @@
// 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 (
"bytes"
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"regexp"
"strings"
"unicode"
"unicode/utf8"
)
type (
ErrorList []error
// varMode describes the lifetime of an argument or
// return value. Modes are used to guide the conversion
// of string and byte slice values across the language
// barrier. The same conversion mode must be used for
// both the conversion before a foreign call and the
// corresponding conversion after the call.
// See the mode* constants for a description of
// each mode.
varMode int
)
const (
// modeTransient are for function arguments that
// are not used after the function returns.
// Transient byte slices don't need copying
// when passed across the language barrier.
modeTransient varMode = iota
// modeRetained are for returned values and for function
// arguments that are used after the function returns.
// Retained byte slices need an intermediate copy.
modeRetained
)
func (list ErrorList) Error() string {
buf := new(bytes.Buffer)
for i, err := range list {
if i > 0 {
buf.WriteRune('\n')
}
io.WriteString(buf, err.Error())
}
return buf.String()
}
// interfaceInfo comes from Init and collects the auxiliary information
// needed to generate bindings for an exported Go interface in a bound
// package.
type interfaceInfo struct {
obj *types.TypeName
t *types.Interface
summary ifaceSummary
}
// structInfo comes from Init and collects the auxiliary information
// needed to generate bindings for an exported Go struct in a bound
// package.
type structInfo struct {
obj *types.TypeName
t *types.Struct
}
// Generator contains the common Go package information
// needed for the specific Go, Java, ObjC generators.
//
// After setting Printer, Fset, AllPkg, Pkg, the Init
// method is used to initialize the auxiliary information
// about the package to be generated, Pkg.
type Generator struct {
*Printer
Fset *token.FileSet
AllPkg []*types.Package
Files []*ast.File
Pkg *types.Package
err ErrorList
// fields set by init.
pkgName string
pkgPrefix string
funcs []*types.Func
constants []*types.Const
vars []*types.Var
interfaces []interfaceInfo
structs []structInfo
otherNames []*types.TypeName
// allIntf contains interfaces from all bound packages.
allIntf []interfaceInfo
docs pkgDocs
}
// A pkgDocs maps the name of each exported package-level declaration to its extracted documentation.
type pkgDocs map[string]*pkgDoc
type pkgDoc struct {
doc string
// Struct or interface fields and methods.
members map[string]string
}
// pkgPrefix returns a prefix that disambiguates symbol names for binding
// multiple packages.
//
// TODO(elias.naur): Avoid (and test) name clashes from multiple packages
// with the same name. Perhaps use the index from the order the package is
// generated.
func pkgPrefix(pkg *types.Package) string {
// The error type has no package
if pkg == nil {
return ""
}
return pkg.Name()
}
func (g *Generator) Init() {
if g.Pkg != nil {
g.pkgName = g.Pkg.Name()
}
g.pkgPrefix = pkgPrefix(g.Pkg)
if g.Pkg != nil {
g.parseDocs()
scope := g.Pkg.Scope()
hasExported := false
for _, name := range scope.Names() {
obj := scope.Lookup(name)
if !obj.Exported() {
continue
}
hasExported = true
switch obj := obj.(type) {
case *types.Func:
if isCallable(obj) {
g.funcs = append(g.funcs, obj)
}
case *types.TypeName:
named, ok := obj.Type().(*types.Named)
if !ok {
continue
}
switch t := named.Underlying().(type) {
case *types.Struct:
g.structs = append(g.structs, structInfo{obj, t})
case *types.Interface:
g.interfaces = append(g.interfaces, interfaceInfo{obj, t, makeIfaceSummary(t)})
default:
g.otherNames = append(g.otherNames, obj)
}
case *types.Const:
g.constants = append(g.constants, obj)
case *types.Var:
g.vars = append(g.vars, obj)
default:
g.errorf("unsupported exported type for %s: %T", obj.Name(), obj)
}
}
if !hasExported {
g.errorf("no exported names in the package %q", g.Pkg.Path())
}
} else {
// Bind the single supported type from the universe scope, error.
errType := types.Universe.Lookup("error").(*types.TypeName)
t := errType.Type().Underlying().(*types.Interface)
g.interfaces = append(g.interfaces, interfaceInfo{errType, t, makeIfaceSummary(t)})
}
for _, p := range g.AllPkg {
scope := p.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
if !obj.Exported() {
continue
}
if obj, ok := obj.(*types.TypeName); ok {
named, ok := obj.Type().(*types.Named)
if !ok {
continue
}
if t, ok := named.Underlying().(*types.Interface); ok {
g.allIntf = append(g.allIntf, interfaceInfo{obj, t, makeIfaceSummary(t)})
}
}
}
}
}
// parseDocs extracts documentation from a package in a form useful for lookups.
func (g *Generator) parseDocs() {
d := make(pkgDocs)
for _, f := range g.Files {
for _, decl := range f.Decls {
switch decl := decl.(type) {
case *ast.GenDecl:
for _, spec := range decl.Specs {
switch spec := spec.(type) {
case *ast.TypeSpec:
d.addType(spec, decl.Doc)
case *ast.ValueSpec:
d.addValue(spec, decl.Doc)
}
}
case *ast.FuncDecl:
d.addFunc(decl)
}
}
}
g.docs = d
}
func (d pkgDocs) addValue(t *ast.ValueSpec, outerDoc *ast.CommentGroup) {
for _, n := range t.Names {
if !ast.IsExported(n.Name) {
continue
}
doc := t.Doc
if doc == nil {
doc = outerDoc
}
if doc != nil {
d[n.Name] = &pkgDoc{doc: doc.Text()}
}
}
}
func (d pkgDocs) addFunc(f *ast.FuncDecl) {
doc := f.Doc
if doc == nil {
return
}
fn := f.Name.Name
if !ast.IsExported(fn) {
return
}
if r := f.Recv; r != nil {
// f is a method.
n := typeName(r.List[0].Type)
pd, exists := d[n]
if !exists {
pd = &pkgDoc{members: make(map[string]string)}
d[n] = pd
}
pd.members[fn] = doc.Text()
} else {
// f is a function.
d[fn] = &pkgDoc{doc: doc.Text()}
}
}
func (d pkgDocs) addType(t *ast.TypeSpec, outerDoc *ast.CommentGroup) {
if !ast.IsExported(t.Name.Name) {
return
}
doc := t.Doc
if doc == nil {
doc = outerDoc
}
pd := d[t.Name.Name]
pd = &pkgDoc{members: make(map[string]string)}
d[t.Name.Name] = pd
if doc != nil {
pd.doc = doc.Text()
}
var fields *ast.FieldList
switch t := t.Type.(type) {
case *ast.StructType:
fields = t.Fields
case *ast.InterfaceType:
fields = t.Methods
}
if fields != nil {
for _, field := range fields.List {
if field.Doc != nil {
if field.Names == nil {
// Anonymous field. Extract name from its type.
if n := typeName(field.Type); ast.IsExported(n) {
pd.members[n] = field.Doc.Text()
}
}
for _, n := range field.Names {
if ast.IsExported(n.Name) {
pd.members[n.Name] = field.Doc.Text()
}
}
}
}
}
}
// typeName returns the type name T for expressions on the
// T, *T, **T (etc.) form.
func typeName(t ast.Expr) string {
switch t := t.(type) {
case *ast.StarExpr:
return typeName(t.X)
case *ast.Ident:
return t.Name
case *ast.SelectorExpr:
return t.Sel.Name
default:
return ""
}
}
func (d *pkgDoc) Doc() string {
if d == nil {
return ""
}
return d.doc
}
func (d *pkgDoc) Member(n string) string {
if d == nil {
return ""
}
return d.members[n]
}
// constructorType returns the type T for a function of the forms:
//
// func NewT...(...) *T
// func NewT...(...) (*T, error)
func (g *Generator) constructorType(f *types.Func) *types.TypeName {
sig := f.Type().(*types.Signature)
res := sig.Results()
if res.Len() != 1 && !(res.Len() == 2 && isErrorType(res.At(1).Type())) {
return nil
}
rt := res.At(0).Type()
pt, ok := rt.(*types.Pointer)
if !ok {
return nil
}
nt, ok := pt.Elem().(*types.Named)
if !ok {
return nil
}
obj := nt.Obj()
if !strings.HasPrefix(f.Name(), "New"+obj.Name()) {
return nil
}
return obj
}
func toCFlag(v bool) int {
if v {
return 1
}
return 0
}
func (g *Generator) errorf(format string, args ...interface{}) {
g.err = append(g.err, fmt.Errorf(format, args...))
}
// cgoType returns the name of a Cgo type suitable for converting a value of
// the given type.
func (g *Generator) cgoType(t types.Type) string {
switch t := t.(type) {
case *types.Basic:
switch t.Kind() {
case types.Bool, types.UntypedBool:
return "char"
case types.Int:
return "nint"
case types.Int8:
return "int8_t"
case types.Int16:
return "int16_t"
case types.Int32, types.UntypedRune: // types.Rune
return "int32_t"
case types.Int64, types.UntypedInt:
return "int64_t"
case types.Uint8: // types.Byte
return "uint8_t"
// TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64:
case types.Float32:
return "float"
case types.Float64, types.UntypedFloat:
return "double"
case types.String:
return "nstring"
default:
g.errorf("unsupported basic type: %s", t)
}
case *types.Slice:
switch e := t.Elem().(type) {
case *types.Basic:
switch e.Kind() {
case types.Uint8: // Byte.
return "nbyteslice"
default:
g.errorf("unsupported slice type: %s", t)
}
default:
g.errorf("unsupported slice type: %s", t)
}
case *types.Pointer:
if _, ok := t.Elem().(*types.Named); ok {
return g.cgoType(t.Elem())
}
g.errorf("unsupported pointer to type: %s", t)
case *types.Named:
return "int32_t"
default:
g.errorf("unsupported type: %s", t)
}
return "TODO"
}
func (g *Generator) genInterfaceMethodSignature(m *types.Func, iName string, header bool, g_paramName func(*types.Tuple, int) string) {
sig := m.Type().(*types.Signature)
params := sig.Params()
res := sig.Results()
if res.Len() == 0 {
g.Printf("void ")
} else {
if res.Len() == 1 {
g.Printf("%s ", g.cgoType(res.At(0).Type()))
} else {
if header {
g.Printf("typedef struct cproxy%s_%s_%s_return {\n", g.pkgPrefix, iName, m.Name())
g.Indent()
for i := 0; i < res.Len(); i++ {
t := res.At(i).Type()
g.Printf("%s r%d;\n", g.cgoType(t), i)
}
g.Outdent()
g.Printf("} cproxy%s_%s_%s_return;\n", g.pkgPrefix, iName, m.Name())
}
g.Printf("struct cproxy%s_%s_%s_return ", g.pkgPrefix, iName, m.Name())
}
}
g.Printf("cproxy%s_%s_%s(int32_t refnum", g.pkgPrefix, iName, m.Name())
for i := 0; i < params.Len(); i++ {
t := params.At(i).Type()
g.Printf(", %s %s", g.cgoType(t), g_paramName(params, i))
}
g.Printf(")")
if header {
g.Printf(";\n")
} else {
g.Printf(" {\n")
}
}
func (g *Generator) validPkg(pkg *types.Package) bool {
for _, p := range g.AllPkg {
if p == pkg {
return true
}
}
return false
}
// isSigSupported reports whether the generators can handle a given
// function signature.
func (g *Generator) isSigSupported(t types.Type) bool {
sig := t.(*types.Signature)
params := sig.Params()
for i := 0; i < params.Len(); i++ {
if !g.isSupported(params.At(i).Type()) {
return false
}
}
res := sig.Results()
for i := 0; i < res.Len(); i++ {
if !g.isSupported(res.At(i).Type()) {
return false
}
}
return true
}
// isSupported reports whether the generators can handle the type.
func (g *Generator) isSupported(t types.Type) bool {
if isErrorType(t) || isWrapperType(t) {
return true
}
switch t := t.(type) {
case *types.Basic:
switch t.Kind() {
case types.Bool, types.UntypedBool,
types.Int,
types.Int8, types.Uint8, // types.Byte
types.Int16,
types.Int32, types.UntypedRune, // types.Rune
types.Int64, types.UntypedInt,
types.Float32,
types.Float64, types.UntypedFloat,
types.String, types.UntypedString:
return true
}
return false
case *types.Slice:
switch e := t.Elem().(type) {
case *types.Basic:
return e.Kind() == types.Uint8
}
case *types.Pointer:
switch t := t.Elem().(type) {
case *types.Named:
return g.validPkg(t.Obj().Pkg())
}
case *types.Named:
switch t.Underlying().(type) {
case *types.Interface, *types.Pointer:
return g.validPkg(t.Obj().Pkg())
}
}
return false
}
var paramRE = regexp.MustCompile(`^p[0-9]*$`)
// basicParamName replaces incompatible name with a p0-pN name.
// Missing names, or existing names of the form p[0-9] are incompatible.
func basicParamName(params *types.Tuple, pos int) string {
name := params.At(pos).Name()
if name == "" || name[0] == '_' || paramRE.MatchString(name) {
name = fmt.Sprintf("p%d", pos)
}
return name
}
func lowerFirst(s string) string {
if s == "" {
return ""
}
var conv []rune
for len(s) > 0 {
r, n := utf8.DecodeRuneInString(s)
if !unicode.IsUpper(r) {
if l := len(conv); l > 1 {
conv[l-1] = unicode.ToUpper(conv[l-1])
}
return string(conv) + s
}
conv = append(conv, unicode.ToLower(r))
s = s[n:]
}
return string(conv)
}
// newNameSanitizer returns a functions that replaces all dashes and dots
// with underscores, as well as avoiding reserved words by suffixing such
// identifiers with underscores.
func newNameSanitizer(res []string) func(s string) string {
reserved := make(map[string]bool)
for _, word := range res {
reserved[word] = true
}
symbols := strings.NewReplacer(
"-", "_",
".", "_",
)
return func(s string) string {
if reserved[s] {
return s + "_"
}
return symbols.Replace(s)
}
}

948
bind/genclasses.go Normal file
View file

@ -0,0 +1,948 @@
// Copyright 2016 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"
"path"
"reflect"
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/mobile/internal/importers"
"golang.org/x/mobile/internal/importers/java"
)
type (
// ClassGen generates Go and C stubs for Java classes so import statements
// on the form
//
//
// import "Java/classpath/to/Class"
//
// will work.
ClassGen struct {
*Printer
// JavaPkg is the Java package prefix for the generated classes. The prefix is prepended to the Go
// package name to create the full Java package name.
JavaPkg string
imported map[string]struct{}
// The list of imported Java classes
classes []*java.Class
// The list of Go package paths with Java interfaces inside
jpkgs []string
// For each Go package path, the list of Java classes.
typePkgs map[string][]*java.Class
// For each Go package path, the Java class with static functions
// or constants.
clsPkgs map[string]*java.Class
// goClsMap is the map of Java class names to Go type names, qualified with package name. Go types
// that implement Java classes need Super methods and Unwrap methods.
goClsMap map[string]string
// goClsImports is the list of imports of user packages that contains the Go types implementing Java
// classes.
goClsImports []string
}
)
func (g *ClassGen) isSupported(t *java.Type) bool {
switch t.Kind {
case java.Array:
// TODO: Support all array types
return t.Elem.Kind == java.Byte
default:
return true
}
}
func (g *ClassGen) isFuncSetSupported(fs *java.FuncSet) bool {
for _, f := range fs.Funcs {
if g.isFuncSupported(f) {
return true
}
}
return false
}
func (g *ClassGen) isFuncSupported(f *java.Func) bool {
for _, a := range f.Params {
if !g.isSupported(a) {
return false
}
}
if f.Ret != nil {
return g.isSupported(f.Ret)
}
return true
}
func (g *ClassGen) goType(t *java.Type, local bool) string {
if t == nil {
// interface{} is used for parameters types for overloaded methods
// where no common ancestor type exists.
return "interface{}"
}
switch t.Kind {
case java.Int:
return "int32"
case java.Boolean:
return "bool"
case java.Short:
return "int16"
case java.Char:
return "uint16"
case java.Byte:
return "byte"
case java.Long:
return "int64"
case java.Float:
return "float32"
case java.Double:
return "float64"
case java.String:
return "string"
case java.Array:
return "[]" + g.goType(t.Elem, local)
case java.Object:
name := goClsName(t.Class)
if !local {
name = "Java." + name
}
return name
default:
panic("invalid kind")
}
}
// Init initializes the class wrapper generator. Classes is the
// list of classes to wrap, goClasses is the list of Java classes
// implemented in Go.
func (g *ClassGen) Init(classes []*java.Class, goClasses []importers.Struct) {
g.goClsMap = make(map[string]string)
impMap := make(map[string]struct{})
for _, s := range goClasses {
n := s.Pkg + "." + s.Name
jn := n
if g.JavaPkg != "" {
jn = g.JavaPkg + "." + jn
}
g.goClsMap[jn] = n
if _, exists := impMap[s.PkgPath]; !exists {
impMap[s.PkgPath] = struct{}{}
g.goClsImports = append(g.goClsImports, s.PkgPath)
}
}
g.classes = classes
g.imported = make(map[string]struct{})
g.typePkgs = make(map[string][]*java.Class)
g.clsPkgs = make(map[string]*java.Class)
pkgSet := make(map[string]struct{})
for _, cls := range classes {
g.imported[cls.Name] = struct{}{}
clsPkg := strings.Replace(cls.Name, ".", "/", -1)
g.clsPkgs[clsPkg] = cls
typePkg := path.Dir(clsPkg)
g.typePkgs[typePkg] = append(g.typePkgs[typePkg], cls)
if _, exists := pkgSet[clsPkg]; !exists {
pkgSet[clsPkg] = struct{}{}
g.jpkgs = append(g.jpkgs, clsPkg)
}
if _, exists := pkgSet[typePkg]; !exists {
pkgSet[typePkg] = struct{}{}
g.jpkgs = append(g.jpkgs, typePkg)
}
}
}
// Packages return the list of Go packages to be generated.
func (g *ClassGen) Packages() []string {
return g.jpkgs
}
func (g *ClassGen) GenPackage(idx int) {
jpkg := g.jpkgs[idx]
g.Printf(gobindPreamble)
g.Printf("package %s\n\n", path.Base(jpkg))
g.Printf("import \"Java\"\n\n")
g.Printf("const _ = Java.Dummy\n\n")
for _, cls := range g.typePkgs[jpkg] {
g.Printf("type %s Java.%s\n", cls.PkgName, goClsName(cls.Name))
}
if cls, ok := g.clsPkgs[jpkg]; ok {
g.Printf("const (\n")
g.Indent()
// Constants
for _, v := range cls.Vars {
if g.isSupported(v.Type) && v.Constant() {
g.Printf("%s = %s\n", initialUpper(v.Name), v.Val)
}
}
g.Outdent()
g.Printf(")\n\n")
g.Printf("var (\n")
g.Indent()
// Functions
loop:
for _, fs := range cls.Funcs {
for _, f := range fs.Funcs {
if f.Public && g.isFuncSupported(f) {
g.Printf("%s func", fs.GoName)
g.genFuncDecl(false, fs)
g.Printf("\n")
continue loop
}
}
}
g.Printf("// Cast takes a proxy for a Java object and converts it to a %s proxy.\n", cls.Name)
g.Printf("// Cast panics if the argument is not a proxy or if the underlying object does\n")
g.Printf("// not extend or implement %s.\n", cls.Name)
g.Printf("Cast func(v interface{}) Java.%s\n", goClsName(cls.Name))
g.Outdent()
g.Printf(")\n\n")
}
}
func (g *ClassGen) GenGo() {
g.Printf(classesGoHeader)
for _, cls := range g.classes {
pkgName := strings.Replace(cls.Name, ".", "/", -1)
g.Printf("import %q\n", "Java/"+pkgName)
}
for _, imp := range g.goClsImports {
g.Printf("import %q\n", imp)
}
if len(g.classes) > 0 {
g.Printf("import \"unsafe\"\n\n")
g.Printf("import \"reflect\"\n\n")
g.Printf("import \"fmt\"\n\n")
}
g.Printf("type proxy interface { Bind_proxy_refnum__() int32 }\n\n")
g.Printf("// Suppress unused package error\n\n")
g.Printf("var _ = _seq.FromRefNum\n")
g.Printf("const _ = Java.Dummy\n\n")
g.Printf("//export initClasses\n")
g.Printf("func initClasses() {\n")
g.Indent()
g.Printf("C.init_proxies()\n")
for _, cls := range g.classes {
g.Printf("init_%s()\n", cls.JNIName)
}
g.Outdent()
g.Printf("}\n\n")
for _, cls := range g.classes {
g.genGo(cls)
}
}
func (g *ClassGen) GenH() {
g.Printf(classesHHeader)
for _, tn := range []string{"jint", "jboolean", "jshort", "jchar", "jbyte", "jlong", "jfloat", "jdouble", "nstring", "nbyteslice"} {
g.Printf("typedef struct ret_%s {\n", tn)
g.Printf(" %s res;\n", tn)
g.Printf(" jint exc;\n")
g.Printf("} ret_%s;\n", tn)
}
g.Printf("\n")
for _, cls := range g.classes {
for _, fs := range cls.AllMethods {
for _, f := range fs.Funcs {
if !g.isFuncSupported(f) {
continue
}
g.Printf("extern ")
g.genCMethodDecl("cproxy", cls.JNIName, f)
g.Printf(";\n")
if _, ok := g.goClsMap[cls.Name]; ok {
g.Printf("extern ")
g.genCMethodDecl("csuper", cls.JNIName, f)
g.Printf(";\n")
}
}
}
}
for _, cls := range g.classes {
g.genH(cls)
}
}
func (g *ClassGen) GenC() {
g.Printf(classesCHeader)
for _, cls := range g.classes {
g.Printf("static jclass class_%s;\n", cls.JNIName)
if _, ok := g.goClsMap[cls.Name]; ok {
g.Printf("static jclass sclass_%s;\n", cls.JNIName)
}
for _, fs := range cls.Funcs {
for _, f := range fs.Funcs {
if !f.Public || !g.isFuncSupported(f) {
continue
}
g.Printf("static jmethodID m_s_%s_%s;\n", cls.JNIName, f.JNIName)
}
}
for _, fs := range cls.AllMethods {
for _, f := range fs.Funcs {
if g.isFuncSupported(f) {
g.Printf("static jmethodID m_%s_%s;\n", cls.JNIName, f.JNIName)
if _, ok := g.goClsMap[cls.Name]; ok {
g.Printf("static jmethodID sm_%s_%s;\n", cls.JNIName, f.JNIName)
}
}
}
}
g.genC(cls)
}
g.Printf("\n")
g.Printf("void init_proxies() {\n")
g.Indent()
g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", len(g.classes))
g.Printf("jclass clazz;\n")
for _, cls := range g.classes {
g.Printf("clazz = go_seq_find_class(%q);\n", strings.Replace(cls.FindName, ".", "/", -1))
g.Printf("if (clazz != NULL) {\n")
g.Indent()
g.Printf("class_%s = (*env)->NewGlobalRef(env, clazz);\n", cls.JNIName)
if _, ok := g.goClsMap[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 _, fs := range cls.Funcs {
for _, f := range fs.Funcs {
if !f.Public || !g.isFuncSupported(f) {
continue
}
g.Printf("m_s_%s_%s = ", cls.JNIName, f.JNIName)
if f.Constructor {
g.Printf("go_seq_get_method_id(clazz, \"<init>\", %q);\n", f.Desc)
} else {
g.Printf("go_seq_get_static_method_id(clazz, %q, %q);\n", f.Name, f.Desc)
}
}
}
for _, fs := range cls.AllMethods {
for _, f := range fs.Funcs {
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.goClsMap[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)
}
}
}
}
g.Outdent()
g.Printf("}\n")
}
g.Printf("go_seq_pop_local_frame(env);\n")
g.Outdent()
g.Printf("}\n\n")
for _, cls := range g.classes {
for _, fs := range cls.AllMethods {
for _, f := range fs.Funcs {
if !g.isFuncSupported(f) {
continue
}
g.genCMethodDecl("cproxy", cls.JNIName, f)
g.genCMethodBody(cls, f, false)
if _, ok := g.goClsMap[cls.Name]; ok {
g.genCMethodDecl("csuper", cls.JNIName, f)
g.genCMethodBody(cls, f, true)
}
}
}
}
}
func (g *ClassGen) GenInterfaces() {
g.Printf(classesPkgHeader)
for _, cls := range g.classes {
g.genInterface(cls)
}
}
func (g *ClassGen) genCMethodBody(cls *java.Class, f *java.Func, virtual bool) {
g.Printf(" {\n")
g.Indent()
// Add 1 for the 'this' argument
g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", len(f.Params)+1)
g.Printf("// Must be a Java object\n")
g.Printf("jobject _this = go_seq_from_refnum(env, this, NULL, NULL);\n")
for i, a := range f.Params {
g.genCToJava(fmt.Sprintf("a%d", i), a)
}
if f.Ret != nil {
g.Printf("%s res = ", f.Ret.JNIType())
}
g.Printf("(*env)->Call")
if virtual {
g.Printf("Nonvirtual")
}
if f.Ret != nil {
g.Printf("%s", f.Ret.JNICallType())
} else {
g.Printf("Void")
}
g.Printf("Method(env, _this, ")
if virtual {
g.Printf("sclass_%s, sm_%s_%s", cls.JNIName, cls.JNIName, f.JNIName)
} else {
g.Printf("m_%s_%s", cls.JNIName, f.JNIName)
}
for i := range f.Params {
g.Printf(", _a%d", i)
}
g.Printf(");\n")
g.Printf("jobject _exc = go_seq_get_exception(env);\n")
g.Printf("int32_t _exc_ref = go_seq_to_refnum(env, _exc);\n")
if f.Ret != nil {
g.genCRetClear("res", f.Ret, "_exc")
g.genJavaToC("res", f.Ret)
}
g.Printf("go_seq_pop_local_frame(env);\n")
if f.Ret != nil {
g.Printf("ret_%s __res = {_res, _exc_ref};\n", f.Ret.CType())
g.Printf("return __res;\n")
} else {
g.Printf("return _exc_ref;\n")
}
g.Outdent()
g.Printf("}\n\n")
}
func initialUpper(s string) string {
if s == "" {
return ""
}
r, n := utf8.DecodeRuneInString(s)
return string(unicode.ToUpper(r)) + s[n:]
}
func (g *ClassGen) genFuncDecl(local bool, fs *java.FuncSet) {
g.Printf("(")
for i, a := range fs.Params {
if i > 0 {
g.Printf(", ")
}
g.Printf("a%d ", i)
if i == len(fs.Params)-1 && fs.Variadic {
g.Printf("...")
}
g.Printf("%s", g.goType(a, local))
}
g.Printf(")")
if fs.Throws {
if fs.HasRet {
g.Printf(" (%s, error)", g.goType(fs.Ret, local))
} else {
g.Printf(" error")
}
} else if fs.HasRet {
g.Printf(" %s", g.goType(fs.Ret, local))
}
}
func (g *ClassGen) genC(cls *java.Class) {
for _, fs := range cls.Funcs {
for _, f := range fs.Funcs {
if !f.Public || !g.isFuncSupported(f) {
continue
}
g.genCFuncDecl(cls.JNIName, f)
g.Printf(" {\n")
g.Indent()
g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", len(f.Params))
for i, a := range f.Params {
g.genCToJava(fmt.Sprintf("a%d", i), a)
}
if f.Constructor {
g.Printf("jobject res = (*env)->NewObject(env")
} else if f.Ret != nil {
g.Printf("%s res = (*env)->CallStatic%sMethod(env", f.Ret.JNIType(), f.Ret.JNICallType())
} else {
g.Printf("(*env)->CallStaticVoidMethod(env")
}
g.Printf(", class_%s, m_s_%s_%s", cls.JNIName, cls.JNIName, f.JNIName)
for i := range f.Params {
g.Printf(", _a%d", i)
}
g.Printf(");\n")
g.Printf("jobject _exc = go_seq_get_exception(env);\n")
g.Printf("int32_t _exc_ref = go_seq_to_refnum(env, _exc);\n")
if f.Ret != nil {
g.genCRetClear("res", f.Ret, "_exc")
g.genJavaToC("res", f.Ret)
}
g.Printf("go_seq_pop_local_frame(env);\n")
if f.Ret != nil {
g.Printf("ret_%s __res = {_res, _exc_ref};\n", f.Ret.CType())
g.Printf("return __res;\n")
} else {
g.Printf("return _exc_ref;\n")
}
g.Outdent()
g.Printf("}\n\n")
}
}
}
func (g *ClassGen) genH(cls *java.Class) {
for _, fs := range cls.Funcs {
for _, f := range fs.Funcs {
if !f.Public || !g.isFuncSupported(f) {
continue
}
g.Printf("extern ")
g.genCFuncDecl(cls.JNIName, f)
g.Printf(";\n")
}
}
}
func (g *ClassGen) genCMethodDecl(prefix, jniName string, f *java.Func) {
if f.Ret != nil {
g.Printf("ret_%s", f.Ret.CType())
} else {
// Return only the exception, if any
g.Printf("jint")
}
g.Printf(" %s_%s_%s(jint this", prefix, jniName, f.JNIName)
for i, a := range f.Params {
g.Printf(", %s a%d", a.CType(), i)
}
g.Printf(")")
}
func (g *ClassGen) genCFuncDecl(jniName string, f *java.Func) {
if f.Ret != nil {
g.Printf("ret_%s", f.Ret.CType())
} else {
// Return only the exception, if any
g.Printf("jint")
}
g.Printf(" cproxy_s_%s_%s(", jniName, f.JNIName)
for i, a := range f.Params {
if i > 0 {
g.Printf(", ")
}
g.Printf("%s a%d", a.CType(), i)
}
g.Printf(")")
}
func (g *ClassGen) genGo(cls *java.Class) {
g.Printf("var class_%s C.jclass\n\n", cls.JNIName)
g.Printf("func init_%s() {\n", cls.JNIName)
g.Indent()
g.Printf("cls := C.CString(%q)\n", strings.Replace(cls.FindName, ".", "/", -1))
g.Printf("clazz := C.go_seq_find_class(cls)\n")
g.Printf("C.free(unsafe.Pointer(cls))\n")
// Before Go 1.11 clazz was a pointer value, an uintptr after.
g.Printf("if uintptr(clazz) == 0 {\n")
g.Printf(" return\n")
g.Printf("}\n")
g.Printf("class_%s = clazz\n", cls.JNIName)
for _, fs := range cls.Funcs {
var supported bool
for _, f := range fs.Funcs {
if f.Public && g.isFuncSupported(f) {
supported = true
break
}
}
if !supported {
continue
}
g.Printf("%s.%s = func", cls.PkgName, fs.GoName)
g.genFuncDecl(false, fs)
g.genFuncBody(cls, fs, "cproxy_s", true)
}
g.Printf("%s.Cast = func(v interface{}) Java.%s {\n", cls.PkgName, goClsName(cls.Name))
g.Indent()
g.Printf("t := reflect.TypeOf((*proxy_class_%s)(nil))\n", cls.JNIName)
g.Printf("cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_%s)\n", cls.JNIName)
g.Printf("ref := C.jint(_seq.ToRefNum(cv))\n")
g.Printf("if C.go_seq_isinstanceof(ref, class_%s) != 1 {\n", cls.JNIName)
g.Printf(" panic(fmt.Errorf(\"%%T is not an instance of %%s\", v, %q))\n", cls.Name)
g.Printf("}\n")
g.Printf("return cv\n")
g.Outdent()
g.Printf("}\n")
g.Outdent()
g.Printf("}\n\n")
g.Printf("type proxy_class_%s _seq.Ref\n\n", cls.JNIName)
g.Printf("func (p *proxy_class_%s) Bind_proxy_refnum__() int32 {\n", cls.JNIName)
g.Indent()
g.Printf("return (*_seq.Ref)(p).Bind_IncNum()\n")
g.Outdent()
g.Printf("}\n\n")
for _, fs := range cls.AllMethods {
if !g.isFuncSetSupported(fs) {
continue
}
g.Printf("func (p *proxy_class_%s) %s", cls.JNIName, fs.GoName)
g.genFuncDecl(false, fs)
g.genFuncBody(cls, fs, "cproxy", false)
}
if cls.Throwable {
g.Printf("func (p *proxy_class_%s) Error() string {\n", cls.JNIName)
g.Printf(" return p.ToString()\n")
g.Printf("}\n")
}
if goName, ok := g.goClsMap[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")
g.Printf("type super_%s struct {*proxy_class_%[1]s}\n\n", cls.JNIName)
g.Printf("func (p *proxy_class_%s) Unwrap() interface{} {\n", cls.JNIName)
g.Indent()
g.Printf("goRefnum := C.go_seq_unwrap(C.jint(p.Bind_proxy_refnum__()))\n")
g.Printf("return _seq.FromRefNum(int32(goRefnum)).Get().(*%s)\n", goName)
g.Outdent()
g.Printf("}\n\n")
for _, fs := range cls.AllMethods {
if !g.isFuncSetSupported(fs) {
continue
}
g.Printf("func (p *super_%s) %s", cls.JNIName, fs.GoName)
g.genFuncDecl(false, fs)
g.genFuncBody(cls, fs, "csuper", false)
}
}
}
// genFuncBody generated a Go function body for a FuncSet. It resolves overloading dynamically,
// by inspecting the number of arguments (if the FuncSet contains varying parameter counts),
// and their types.
func (g *ClassGen) genFuncBody(cls *java.Class, fs *java.FuncSet, prefix string, static bool) {
maxp := len(fs.Funcs[0].Params)
minp := maxp
// sort the function variants into argument sizes.
buckets := make(map[int][]*java.Func)
numF := 0
for _, f := range fs.Funcs {
if !g.isFuncSupported(f) {
continue
}
numF++
n := len(f.Params)
if n < minp {
minp = n
} else if n > maxp {
maxp = n
}
buckets[n] = append(buckets[n], f)
}
g.Printf(" {\n")
g.Indent()
if len(buckets) != 1 {
// Switch over the number of arguments.
g.Printf("switch %d + len(a%d) {\n", minp, minp)
}
for i := minp; i <= maxp; i++ {
funcs := buckets[i]
if len(funcs) == 0 {
continue
}
if len(buckets) != 1 {
g.Printf("case %d:\n", i)
g.Indent()
}
for _, f := range funcs {
if len(funcs) > 1 {
g.Printf("{\n")
g.Indent()
}
var argNames []string
var preds []string
for i, a := range f.Params {
var ct *java.Type
var argName string
if i >= minp {
argName = fmt.Sprintf("a%d[%d]", minp, i-minp)
ct = fs.Params[minp]
} else {
argName = fmt.Sprintf("a%d", i)
ct = fs.Params[i]
}
if !reflect.DeepEqual(ct, a) {
g.Printf("_a%d, ok%d := %s.(%s)\n", i, i, argName, g.goType(a, false))
argName = fmt.Sprintf("_a%d", i)
preds = append(preds, fmt.Sprintf("ok%d", i))
}
argNames = append(argNames, argName)
}
if len(preds) > 0 {
g.Printf("if %s {\n", strings.Join(preds, " && "))
g.Indent()
}
for i, a := range f.Params {
g.genWrite(fmt.Sprintf("__a%d", i), argNames[i], a, modeTransient)
}
g.Printf("res := C.%s_%s_%s(", prefix, cls.JNIName, f.JNIName)
if !static {
g.Printf("C.jint(p.Bind_proxy_refnum__())")
}
for i := range f.Params {
if !static || i > 0 {
g.Printf(", ")
}
g.Printf("__a%d", i)
}
g.Printf(")\n")
g.genFuncRet(fs, f, numF > 1)
if len(preds) > 0 {
g.Outdent()
g.Printf("}\n")
}
if len(funcs) > 1 {
g.Outdent()
g.Printf("}\n")
}
}
if len(buckets) != 1 {
g.Outdent()
}
}
if len(buckets) != 1 {
g.Printf("}\n")
}
if numF > 1 {
g.Printf("panic(\"no overloaded method found for %s.%s that matched the arguments\")\n", cls.Name, fs.Name)
}
g.Outdent()
g.Printf("}\n\n")
}
func (g *ClassGen) genFuncRet(fs *java.FuncSet, f *java.Func, mustReturn bool) {
if f.Ret != nil {
g.genRead("_res", "res.res", f.Ret, modeRetained)
g.genRefRead("_exc", "res.exc", "error", "proxy_error", true)
} else {
g.genRefRead("_exc", "res", "error", "proxy_error", true)
}
if !fs.Throws {
g.Printf("if (_exc != nil) { panic(_exc) }\n")
if fs.HasRet {
if f.Ret != nil {
g.Printf("return _res\n")
} else {
// The variant doesn't return a value, but the common
// signature does. Use nil as a placeholder return value.
g.Printf("return nil\n")
}
} else if mustReturn {
// If there are overloaded variants, return here to avoid the fallback
// panic generated in genFuncBody.
g.Printf("return\n")
}
} else {
if fs.HasRet {
if f.Ret != nil {
g.Printf("return _res, _exc\n")
} else {
// As above, use a nil placeholder return value.
g.Printf("return nil, _exc\n")
}
} else {
g.Printf("return _exc\n")
}
}
}
func (g *ClassGen) genRead(to, from string, t *java.Type, mode varMode) {
switch t.Kind {
case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double:
g.Printf("%s := %s(%s)\n", to, g.goType(t, false), from)
case java.Boolean:
g.Printf("%s := %s != C.JNI_FALSE\n", to, from)
case java.String:
g.Printf("%s := decodeString(%s)\n", to, from)
case java.Array:
if t.Elem.Kind != java.Byte {
panic("unsupported array type")
}
g.Printf("%s := toSlice(%s, %v)\n", to, from, mode == modeRetained)
case java.Object:
_, hasProxy := g.imported[t.Class]
g.genRefRead(to, from, g.goType(t, false), "proxy_class_"+flattenName(t.Class), hasProxy)
default:
panic("invalid kind")
}
}
func (g *ClassGen) genRefRead(to, from string, intfName, proxyName string, hasProxy bool) {
g.Printf("var %s %s\n", to, intfName)
g.Printf("%s_ref := _seq.FromRefNum(int32(%s))\n", to, from)
g.Printf("if %s_ref != nil {\n", to)
g.Printf(" if %s < 0 { // go object\n", from)
g.Printf(" %s = %s_ref.Get().(%s)\n", to, to, intfName)
g.Printf(" } else { // foreign object\n")
if hasProxy {
g.Printf(" %s = (*%s)(%s_ref)\n", to, proxyName, to)
} else {
g.Printf(" %s = %s_ref\n", to, to)
}
g.Printf(" }\n")
g.Printf("}\n")
}
func (g *ClassGen) genWrite(dst, v string, t *java.Type, mode varMode) {
switch t.Kind {
case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double:
g.Printf("%s := C.%s(%s)\n", dst, t.CType(), v)
case java.Boolean:
g.Printf("%s := C.jboolean(C.JNI_FALSE)\n", dst)
g.Printf("if %s {\n", v)
g.Printf(" %s = C.jboolean(C.JNI_TRUE)\n", dst)
g.Printf("}\n")
case java.String:
g.Printf("%s := encodeString(%s)\n", dst, v)
case java.Array:
if t.Elem.Kind != java.Byte {
panic("unsupported array type")
}
g.Printf("%s := fromSlice(%s, %v)\n", dst, v, mode == modeRetained)
case java.Object:
g.Printf("var %s C.jint = _seq.NullRefNum\n", dst)
g.Printf("if %s != nil {\n", v)
g.Printf(" %s = C.jint(_seq.ToRefNum(%s))\n", dst, v)
g.Printf("}\n")
default:
panic("invalid kind")
}
}
// genCRetClear clears the result value from a JNI call if an exception was
// raised.
func (g *ClassGen) genCRetClear(v string, t *java.Type, exc string) {
g.Printf("if (%s != NULL) {\n", exc)
g.Indent()
switch t.Kind {
case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double, java.Boolean:
g.Printf("%s = 0;\n", v)
default:
// Assume a nullable type. It will break if we missed a type.
g.Printf("%s = NULL;\n", v)
}
g.Outdent()
g.Printf("}\n")
}
func (g *ClassGen) genJavaToC(v string, t *java.Type) {
switch t.Kind {
case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double, java.Boolean:
g.Printf("%s _%s = %s;\n", t.JNIType(), v, v)
case java.String:
g.Printf("nstring _%s = go_seq_from_java_string(env, %s);\n", v, v)
case java.Array:
if t.Elem.Kind != java.Byte {
panic("unsupported array type")
}
g.Printf("nbyteslice _%s = go_seq_from_java_bytearray(env, %s, 1);\n", v, v)
case java.Object:
g.Printf("jint _%s = go_seq_to_refnum(env, %s);\n", v, v)
default:
panic("invalid kind")
}
}
func (g *ClassGen) genCToJava(v string, t *java.Type) {
switch t.Kind {
case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double, java.Boolean:
g.Printf("%s _%s = %s;\n", t.JNIType(), v, v)
case java.String:
g.Printf("jstring _%s = go_seq_to_java_string(env, %s);\n", v, v)
case java.Array:
if t.Elem.Kind != java.Byte {
panic("unsupported array type")
}
g.Printf("jbyteArray _%s = go_seq_to_java_bytearray(env, %s, 0);\n", v, v)
case java.Object:
g.Printf("jobject _%s = go_seq_from_refnum(env, %s, NULL, NULL);\n", v, v)
default:
panic("invalid kind")
}
}
func goClsName(n string) string {
return initialUpper(strings.Replace(n, ".", "_", -1))
}
func (g *ClassGen) genInterface(cls *java.Class) {
g.Printf("type %s interface {\n", goClsName(cls.Name))
g.Indent()
// Methods
for _, fs := range cls.AllMethods {
if !g.isFuncSetSupported(fs) {
continue
}
g.Printf("%s", fs.GoName)
g.genFuncDecl(true, fs)
g.Printf("\n")
}
if goName, ok := g.goClsMap[cls.Name]; ok {
g.Printf("Super() %s\n", goClsName(cls.Name))
g.Printf("// Unwrap returns the Go object this Java instance\n")
g.Printf("// is wrapping.\n")
g.Printf("// The return value is a %s, but the delclared type is\n", goName)
g.Printf("// interface{} to avoid import cycles.\n")
g.Printf("Unwrap() interface{}\n")
}
if cls.Throwable {
g.Printf("Error() string\n")
}
g.Outdent()
g.Printf("}\n\n")
}
// Flatten java class names. "java.package.Class$Inner" is converted to
// "java_package_Class_Inner"
func flattenName(n string) string {
return strings.Replace(strings.Replace(n, ".", "_", -1), "$", "_", -1)
}
const (
classesPkgHeader = gobindPreamble + `
package Java
// Used to silence this package not used errors
const Dummy = 0
`
classesCHeader = gobindPreamble + `
#include <jni.h>
#include "seq.h"
#include "classes.h"
`
classesHHeader = gobindPreamble + `
#include <jni.h>
#include "seq.h"
extern void init_proxies();
`
javaImplHeader = gobindPreamble
classesGoHeader = gobindPreamble + `
package main
/*
#include <stdlib.h> // for free()
#include <jni.h>
#include "seq.h"
#include "classes.h"
*/
import "C"
import (
"Java"
_seq "golang.org/x/mobile/bind/seq"
)
`
)

593
bind/gengo.go Normal file
View file

@ -0,0 +1,593 @@
// Copyright 2014 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 (
"bytes"
"fmt"
"go/types"
"strings"
)
type goGen struct {
*Generator
// imports is the list of imports, in the form
// "the/package/path"
//
// or
//
// name "the/package/path"
//
// in case of duplicates.
imports []string
// The set of taken import names.
importNames map[string]struct{}
// importMap is a map from packages to their names. The name of a package is the last
// segment of its path, with duplicates resolved by appending a underscore and a unique
// number.
importMap map[*types.Package]string
}
const (
goPreamble = gobindPreamble + `// Package main is an autogenerated binder stub for package %[1]s.
//
// autogenerated by gobind -lang=go %[2]s
package main
/*
#include <stdlib.h>
#include <stdint.h>
#include "seq.h"
#include "%[1]s.h"
*/
import "C"
`
)
func (g *goGen) genFuncBody(o *types.Func, selectorLHS string) {
sig := o.Type().(*types.Signature)
params := sig.Params()
for i := 0; i < params.Len(); i++ {
p := params.At(i)
pn := "param_" + g.paramName(params, i)
g.genRead("_"+pn, pn, p.Type(), modeTransient)
}
res := sig.Results()
if res.Len() > 2 || res.Len() == 2 && !isErrorType(res.At(1).Type()) {
g.errorf("functions and methods must return either zero or one values, and optionally an error")
return
}
if res.Len() > 0 {
for i := 0; i < res.Len(); i++ {
if i > 0 {
g.Printf(", ")
}
g.Printf("res_%d", i)
}
g.Printf(" := ")
}
g.Printf("%s%s(", selectorLHS, o.Name())
for i := 0; i < params.Len(); i++ {
if i > 0 {
g.Printf(", ")
}
g.Printf("_param_%s", g.paramName(params, i))
}
g.Printf(")\n")
for i := 0; i < res.Len(); i++ {
pn := fmt.Sprintf("res_%d", i)
g.genWrite("_"+pn, pn, res.At(i).Type(), modeRetained)
}
if res.Len() > 0 {
g.Printf("return ")
for i := 0; i < res.Len(); i++ {
if i > 0 {
g.Printf(", ")
}
g.Printf("_res_%d", i)
}
g.Printf("\n")
}
}
func (g *goGen) genWrite(toVar, fromVar string, t types.Type, mode varMode) {
switch t := t.(type) {
case *types.Basic:
switch t.Kind() {
case types.String:
g.Printf("%s := encodeString(%s)\n", toVar, fromVar)
case types.Bool:
g.Printf("var %s C.%s = 0\n", toVar, g.cgoType(t))
g.Printf("if %s { %s = 1 }\n", fromVar, toVar)
default:
g.Printf("%s := C.%s(%s)\n", toVar, g.cgoType(t), fromVar)
}
case *types.Slice:
switch e := t.Elem().(type) {
case *types.Basic:
switch e.Kind() {
case types.Uint8: // Byte.
g.Printf("%s := fromSlice(%s, %v)\n", toVar, fromVar, mode == modeRetained)
default:
g.errorf("unsupported type: %s", t)
}
default:
g.errorf("unsupported type: %s", t)
}
case *types.Pointer:
// TODO(crawshaw): test *int
// TODO(crawshaw): test **Generator
switch t := t.Elem().(type) {
case *types.Named:
g.genToRefNum(toVar, fromVar)
default:
g.errorf("unsupported type %s", t)
}
case *types.Named:
switch u := t.Underlying().(type) {
case *types.Interface, *types.Pointer:
g.genToRefNum(toVar, fromVar)
default:
g.errorf("unsupported, direct named type %s: %s", t, u)
}
default:
g.errorf("unsupported type %s", t)
}
}
// genToRefNum generates Go code for converting a variable to its refnum.
// Note that the nil-check cannot be lifted into seq.ToRefNum, because a nil
// struct pointer does not convert to a nil interface.
func (g *goGen) genToRefNum(toVar, fromVar string) {
g.Printf("var %s C.int32_t = _seq.NullRefNum\n", toVar)
g.Printf("if %s != nil {\n", fromVar)
g.Printf(" %s = C.int32_t(_seq.ToRefNum(%s))\n", toVar, fromVar)
g.Printf("}\n")
}
func (g *goGen) genFuncSignature(o *types.Func, objName string) {
g.Printf("//export proxy%s_%s_%s\n", g.pkgPrefix, objName, o.Name())
g.Printf("func proxy%s_%s_%s(", g.pkgPrefix, objName, o.Name())
if objName != "" {
g.Printf("refnum C.int32_t")
}
sig := o.Type().(*types.Signature)
params := sig.Params()
for i := 0; i < params.Len(); i++ {
if objName != "" || i > 0 {
g.Printf(", ")
}
p := params.At(i)
g.Printf("param_%s C.%s", g.paramName(params, i), g.cgoType(p.Type()))
}
g.Printf(") ")
res := sig.Results()
if res.Len() > 0 {
g.Printf("(")
for i := 0; i < res.Len(); i++ {
if i > 0 {
g.Printf(", ")
}
g.Printf("C.%s", g.cgoType(res.At(i).Type()))
}
g.Printf(") ")
}
g.Printf("{\n")
}
func (g *goGen) paramName(params *types.Tuple, pos int) string {
return basicParamName(params, pos)
}
func (g *goGen) genFunc(o *types.Func) {
if !g.isSigSupported(o.Type()) {
g.Printf("// skipped function %s with unsupported parameter or result types\n", o.Name())
return
}
g.genFuncSignature(o, "")
g.Indent()
g.genFuncBody(o, g.pkgName(g.Pkg))
g.Outdent()
g.Printf("}\n\n")
}
func (g *goGen) genStruct(obj *types.TypeName, T *types.Struct) {
fields := exportedFields(T)
methods := exportedMethodSet(types.NewPointer(obj.Type()))
for _, f := range fields {
if t := f.Type(); !g.isSupported(t) {
g.Printf("// skipped field %s.%s with unsupported type: %s\n\n", obj.Name(), f.Name(), t)
continue
}
g.Printf("//export proxy%s_%s_%s_Set\n", g.pkgPrefix, obj.Name(), f.Name())
g.Printf("func proxy%s_%s_%s_Set(refnum C.int32_t, v C.%s) {\n", g.pkgPrefix, obj.Name(), f.Name(), g.cgoType(f.Type()))
g.Indent()
g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
g.genRead("_v", "v", f.Type(), modeRetained)
g.Printf("ref.Get().(*%s%s).%s = _v\n", g.pkgName(g.Pkg), obj.Name(), f.Name())
g.Outdent()
g.Printf("}\n\n")
g.Printf("//export proxy%s_%s_%s_Get\n", g.pkgPrefix, obj.Name(), f.Name())
g.Printf("func proxy%s_%s_%s_Get(refnum C.int32_t) C.%s {\n", g.pkgPrefix, obj.Name(), f.Name(), g.cgoType(f.Type()))
g.Indent()
g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
g.Printf("v := ref.Get().(*%s%s).%s\n", g.pkgName(g.Pkg), obj.Name(), f.Name())
g.genWrite("_v", "v", f.Type(), modeRetained)
g.Printf("return _v\n")
g.Outdent()
g.Printf("}\n\n")
}
for _, m := range methods {
if !g.isSigSupported(m.Type()) {
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
continue
}
g.genFuncSignature(m, obj.Name())
g.Indent()
g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
g.Printf("v := ref.Get().(*%s%s)\n", g.pkgName(g.Pkg), obj.Name())
g.genFuncBody(m, "v.")
g.Outdent()
g.Printf("}\n\n")
}
// Export constructor for ObjC and Java default no-arg constructors
g.Printf("//export new_%s_%s\n", g.Pkg.Name(), obj.Name())
g.Printf("func new_%s_%s() C.int32_t {\n", g.Pkg.Name(), obj.Name())
g.Indent()
g.Printf("return C.int32_t(_seq.ToRefNum(new(%s%s)))\n", g.pkgName(g.Pkg), obj.Name())
g.Outdent()
g.Printf("}\n")
}
func (g *goGen) genVar(o *types.Var) {
if t := o.Type(); !g.isSupported(t) {
g.Printf("// skipped variable %s with unsupported type %s\n\n", o.Name(), t)
return
}
// TODO(hyangah): non-struct pointer types (*int), struct type.
v := fmt.Sprintf("%s%s", g.pkgName(g.Pkg), o.Name())
// var I int
//
// func var_setI(v int)
g.Printf("//export var_set%s_%s\n", g.pkgPrefix, o.Name())
g.Printf("func var_set%s_%s(v C.%s) {\n", g.pkgPrefix, o.Name(), g.cgoType(o.Type()))
g.Indent()
g.genRead("_v", "v", o.Type(), modeRetained)
g.Printf("%s = _v\n", v)
g.Outdent()
g.Printf("}\n")
// func var_getI() int
g.Printf("//export var_get%s_%s\n", g.pkgPrefix, o.Name())
g.Printf("func var_get%s_%s() C.%s {\n", g.pkgPrefix, o.Name(), g.cgoType(o.Type()))
g.Indent()
g.Printf("v := %s\n", v)
g.genWrite("_v", "v", o.Type(), modeRetained)
g.Printf("return _v\n")
g.Outdent()
g.Printf("}\n")
}
func (g *goGen) genInterface(obj *types.TypeName) {
iface := obj.Type().(*types.Named).Underlying().(*types.Interface)
summary := makeIfaceSummary(iface)
// Define the entry points.
for _, m := range summary.callable {
if !g.isSigSupported(m.Type()) {
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
continue
}
g.genFuncSignature(m, obj.Name())
g.Indent()
g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
g.Printf("v := ref.Get().(%s%s)\n", g.pkgName(g.Pkg), obj.Name())
g.genFuncBody(m, "v.")
g.Outdent()
g.Printf("}\n\n")
}
// Define a proxy interface.
if !summary.implementable {
// The interface defines an unexported method or a method that
// uses an unexported type. We cannot generate a proxy object
// for such a type.
return
}
g.Printf("type proxy%s_%s _seq.Ref\n\n", g.pkgPrefix, obj.Name())
g.Printf("func (p *proxy%s_%s) Bind_proxy_refnum__() int32 {\n", g.pkgPrefix, obj.Name())
g.Indent()
g.Printf("return (*_seq.Ref)(p).Bind_IncNum()\n")
g.Outdent()
g.Printf("}\n\n")
for _, m := range summary.callable {
if !g.isSigSupported(m.Type()) {
g.Printf("// skipped method %s.%s with unsupported parameter or result types\n", obj.Name(), m.Name())
continue
}
sig := m.Type().(*types.Signature)
params := sig.Params()
res := sig.Results()
if res.Len() > 2 ||
(res.Len() == 2 && !isErrorType(res.At(1).Type())) {
g.errorf("functions and methods must return either zero or one value, and optionally an error: %s.%s", obj.Name(), m.Name())
continue
}
g.Printf("func (p *proxy%s_%s) %s(", g.pkgPrefix, obj.Name(), m.Name())
for i := 0; i < params.Len(); i++ {
if i > 0 {
g.Printf(", ")
}
g.Printf("param_%s %s", g.paramName(params, i), g.typeString(params.At(i).Type()))
}
g.Printf(") ")
if res.Len() == 1 {
g.Printf("%s", g.typeString(res.At(0).Type()))
} else if res.Len() == 2 {
g.Printf("(%s, error)", g.typeString(res.At(0).Type()))
}
g.Printf(" {\n")
g.Indent()
for i := 0; i < params.Len(); i++ {
pn := "param_" + g.paramName(params, i)
g.genWrite("_"+pn, pn, params.At(i).Type(), modeTransient)
}
if res.Len() > 0 {
g.Printf("res := ")
}
g.Printf("C.cproxy%s_%s_%s(C.int32_t(p.Bind_proxy_refnum__())", g.pkgPrefix, obj.Name(), m.Name())
for i := 0; i < params.Len(); i++ {
g.Printf(", _param_%s", g.paramName(params, i))
}
g.Printf(")\n")
var retName string
if res.Len() > 0 {
if res.Len() == 1 {
T := res.At(0).Type()
g.genRead("_res", "res", T, modeRetained)
retName = "_res"
} else {
var rvs []string
for i := 0; i < res.Len(); i++ {
rv := fmt.Sprintf("res_%d", i)
g.genRead(rv, fmt.Sprintf("res.r%d", i), res.At(i).Type(), modeRetained)
rvs = append(rvs, rv)
}
retName = strings.Join(rvs, ", ")
}
g.Printf("return %s\n", retName)
}
g.Outdent()
g.Printf("}\n\n")
}
}
func (g *goGen) genRead(toVar, fromVar string, typ types.Type, mode varMode) {
switch t := typ.(type) {
case *types.Basic:
switch t.Kind() {
case types.String:
g.Printf("%s := decodeString(%s)\n", toVar, fromVar)
case types.Bool:
g.Printf("%s := %s != 0\n", toVar, fromVar)
default:
g.Printf("%s := %s(%s)\n", toVar, t.Underlying().String(), fromVar)
}
case *types.Slice:
switch e := t.Elem().(type) {
case *types.Basic:
switch e.Kind() {
case types.Uint8: // Byte.
g.Printf("%s := toSlice(%s, %v)\n", toVar, fromVar, mode == modeRetained)
default:
g.errorf("unsupported type: %s", t)
}
default:
g.errorf("unsupported type: %s", t)
}
case *types.Pointer:
switch u := t.Elem().(type) {
case *types.Named:
o := u.Obj()
oPkg := o.Pkg()
if !g.validPkg(oPkg) {
g.errorf("type %s is defined in %s, which is not bound", u, oPkg)
return
}
g.Printf("// Must be a Go object\n")
g.Printf("var %s *%s%s\n", toVar, g.pkgName(oPkg), o.Name())
g.Printf("if %s_ref := _seq.FromRefNum(int32(%s)); %s_ref != nil {\n", toVar, fromVar, toVar)
g.Printf(" %s = %s_ref.Get().(*%s%s)\n", toVar, toVar, g.pkgName(oPkg), o.Name())
g.Printf("}\n")
default:
g.errorf("unsupported pointer type %s", t)
}
case *types.Named:
switch t.Underlying().(type) {
case *types.Interface, *types.Pointer:
hasProxy := true
if iface, ok := t.Underlying().(*types.Interface); ok {
hasProxy = makeIfaceSummary(iface).implementable
}
pkgFirst := typePkgFirstElem(t)
isWrapper := pkgFirst == "Java" || pkgFirst == "ObjC"
o := t.Obj()
oPkg := o.Pkg()
if !isErrorType(t) && !g.validPkg(oPkg) && !isWrapper {
g.errorf("type %s is defined in %s, which is not bound", t, oPkg)
return
}
g.Printf("var %s %s\n", toVar, g.typeString(t))
g.Printf("%s_ref := _seq.FromRefNum(int32(%s))\n", toVar, fromVar)
g.Printf("if %s_ref != nil {\n", toVar)
g.Printf(" if %s < 0 { // go object \n", fromVar)
g.Printf(" %s = %s_ref.Get().(%s%s)\n", toVar, toVar, g.pkgName(oPkg), o.Name())
if hasProxy {
g.Printf(" } else { // foreign object \n")
if isWrapper {
var clsName string
switch pkgFirst {
case "Java":
clsName = flattenName(classNameFor(t))
case "ObjC":
clsName = t.Obj().Name()
}
g.Printf(" %s = (*proxy_class_%s)(%s_ref)\n", toVar, clsName, toVar)
} else {
g.Printf(" %s = (*proxy%s_%s)(%s_ref)\n", toVar, pkgPrefix(oPkg), o.Name(), toVar)
}
}
g.Printf(" }\n")
g.Printf("}\n")
default:
g.errorf("unsupported named type %s", t)
}
default:
g.errorf("unsupported type: %s", typ)
}
}
func (g *goGen) typeString(typ types.Type) string {
pkg := g.Pkg
switch t := typ.(type) {
case *types.Named:
obj := t.Obj()
if obj.Pkg() == nil { // e.g. error type is *types.Named.
return types.TypeString(typ, types.RelativeTo(pkg))
}
oPkg := obj.Pkg()
if !g.validPkg(oPkg) && !isWrapperType(t) {
g.errorf("type %s is defined in %s, which is not bound", t, oPkg)
return "TODO"
}
switch t.Underlying().(type) {
case *types.Interface, *types.Struct:
return fmt.Sprintf("%s%s", g.pkgName(oPkg), types.TypeString(typ, types.RelativeTo(oPkg)))
default:
g.errorf("unsupported named type %s / %T", t, t)
}
case *types.Pointer:
switch t := t.Elem().(type) {
case *types.Named:
return fmt.Sprintf("*%s", g.typeString(t))
default:
g.errorf("not yet supported, pointer type %s / %T", t, t)
}
default:
return types.TypeString(typ, types.RelativeTo(pkg))
}
return ""
}
// genPreamble generates the preamble. It is generated after everything
// else, where we know which bound packages to import.
func (g *goGen) genPreamble() {
pkgName := ""
pkgPath := ""
if g.Pkg != nil {
pkgName = g.Pkg.Name()
pkgPath = g.Pkg.Path()
} else {
pkgName = "universe"
}
g.Printf(goPreamble, pkgName, pkgPath)
g.Printf("import (\n")
g.Indent()
g.Printf("_seq \"golang.org/x/mobile/bind/seq\"\n")
for _, imp := range g.imports {
g.Printf("%s\n", imp)
}
g.Outdent()
g.Printf(")\n\n")
}
func (g *goGen) gen() error {
g.importNames = make(map[string]struct{})
g.importMap = make(map[*types.Package]string)
// Switch to a temporary buffer so the preamble can be
// written last.
oldBuf := g.Printer.Buf
newBuf := new(bytes.Buffer)
g.Printer.Buf = newBuf
g.Printf("// suppress the error if seq ends up unused\n")
g.Printf("var _ = _seq.FromRefNum\n")
for _, s := range g.structs {
g.genStruct(s.obj, s.t)
}
for _, intf := range g.interfaces {
g.genInterface(intf.obj)
}
for _, v := range g.vars {
g.genVar(v)
}
for _, f := range g.funcs {
g.genFunc(f)
}
// Switch to the original buffer, write the preamble
// and append the rest of the file.
g.Printer.Buf = oldBuf
g.genPreamble()
g.Printer.Buf.Write(newBuf.Bytes())
if len(g.err) > 0 {
return g.err
}
return nil
}
// pkgName returns the package name and adds the package to the list of
// imports.
func (g *goGen) pkgName(pkg *types.Package) string {
// The error type has no package
if pkg == nil {
return ""
}
if name, exists := g.importMap[pkg]; exists {
return name + "."
}
i := 0
pname := pkg.Name()
name := pkg.Name()
for {
if _, exists := g.importNames[name]; !exists {
g.importNames[name] = struct{}{}
g.importMap[pkg] = name
var imp string
if pname != name {
imp = fmt.Sprintf("%s %q", name, pkg.Path())
} else {
imp = fmt.Sprintf("%q", pkg.Path())
}
g.imports = append(g.imports, imp)
break
}
i++
name = fmt.Sprintf("%s_%d", pname, i)
}
g.importMap[pkg] = name
return name + "."
}

1732
bind/genjava.go Normal file

File diff suppressed because it is too large Load diff

1428
bind/genobjc.go Normal file

File diff suppressed because it is too large Load diff

754
bind/genobjcw.go Normal file
View file

@ -0,0 +1,754 @@
// Copyright 2016 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 (
"path"
"strings"
"golang.org/x/mobile/internal/importers/objc"
)
type (
// ObjCWrapper generates Go and C stubs for ObjC interfaces and protocols.
ObjcWrapper struct {
*Printer
imported map[string]*objc.Named
// The list of ObjC types.
types []*objc.Named
// The list of Go package paths with ObjC wrappers.
pkgNames []string
modules []string
// For each ObjC module, the list of ObjC types within.
modMap map[string][]*objc.Named
// For each module/name Go package path, the ObjC type
// with static functions or constants.
typePkgs map[string]*objc.Named
// supers is the map of types that need Super methods.
supers map[string]struct{}
}
)
// Init initializes the ObjC types wrapper generator. Types is the
// list of types to wrap, genNames the list of generated type names.
func (g *ObjcWrapper) Init(types []*objc.Named, genNames []string) {
g.supers = make(map[string]struct{})
for _, s := range genNames {
g.supers[s] = struct{}{}
}
g.types = types
g.imported = make(map[string]*objc.Named)
g.modMap = make(map[string][]*objc.Named)
g.typePkgs = make(map[string]*objc.Named)
pkgSet := make(map[string]struct{})
for _, n := range types {
g.imported[n.GoName] = n
typePkg := n.Module + "/" + n.GoName
g.typePkgs[typePkg] = n
if !n.Generated {
if _, exists := g.modMap[n.Module]; !exists {
g.modules = append(g.modules, n.Module)
}
}
g.modMap[n.Module] = append(g.modMap[n.Module], n)
if _, exists := pkgSet[n.Module]; !exists {
pkgSet[n.Module] = struct{}{}
g.pkgNames = append(g.pkgNames, n.Module)
}
g.pkgNames = append(g.pkgNames, typePkg)
}
}
func (g *ObjcWrapper) GenM() {
g.Printf(gobindPreamble)
// For objc_msgSend* functions.
g.Printf("@import ObjectiveC.message;\n")
g.Printf("#include \"seq.h\"\n")
g.Printf("#include \"interfaces.h\"\n\n")
for _, n := range g.types {
g.genM(n)
}
g.Printf("\n")
for _, n := range g.types {
for _, f := range n.AllMethods {
if !g.isFuncSupported(f) {
continue
}
g.genCFuncDecl("cproxy", n.GoName, f)
g.genCFuncBody(n, f, false)
if _, exists := g.supers[n.GoName]; exists {
g.genCFuncDecl("csuper", n.GoName, f)
g.genCFuncBody(n, f, true)
}
}
}
}
func (g *ObjcWrapper) genCFuncBody(n *objc.Named, f *objc.Func, super bool) {
g.Printf(" {\n")
g.Indent()
if !f.Static {
g.Printf("%s _this = go_seq_from_refnum(this).obj;\n", n.ObjcType())
}
var errParam *objc.Param
for i, a := range f.Params {
if i == len(f.Params)-1 && g.isErrorType(a.Type) {
errParam = a
break
}
g.genCToObjC(a.Name, a.Type, modeTransient)
}
if errParam != nil {
g.Printf("NSError *%s = nil;\n", errParam.Name)
}
if f.Constructor {
g.Printf("%s _this = [%s alloc];\n", n.ObjcType(), n.Name)
}
if super {
g.Printf("struct objc_super _super = {\n")
g.Printf(" .receiver = _this,\n")
g.Printf(" .super_class = class_getSuperclass([%s class]),\n", n.Name)
g.Printf("};\n")
}
retType := "void"
if f.Ret != nil {
retType = g.objcType(f.Ret)
g.Printf("%s res = ", retType)
}
// There is no direct way to send a message to a class' super
// class from outside the class itself. Use objc_msgSendSuper instead
// which is what the compiler uses itself. To keep us honest and to exercise
// the code paths more use objc_msgSend for regular calls as well.
//
// A regular call looks like this:
//
// res = ((<return type> (*)(id, SEL, <argument_types>))objc_msgSend)(_this, @selector(...), <arguments>)
//
// a call to super looks like this:
//
// ret = ((<return type> (*)(id, SEL, <argument_types>))objc_msgSendSuper)(<struct objc_super>, <arguments>)
if f.Ret != nil {
switch f.Ret.Kind {
case objc.String, objc.Bool, objc.Data, objc.Int, objc.Uint, objc.Short, objc.Ushort, objc.Char, objc.Uchar, objc.Float, objc.Double, objc.Class, objc.Protocol:
default:
// If support for struct results is added, objc_msgSend_stret must be used
panic("unsupported type kind - use objc_msgSend_stret?")
}
}
g.Printf("((%s (*)(", retType)
if super {
g.Printf("struct objc_super *")
} else {
g.Printf("id")
}
g.Printf(", SEL")
for _, a := range f.Params {
g.Printf(", %s", g.objcType(a.Type))
}
g.Printf("))")
if super {
g.Printf("objc_msgSendSuper")
} else {
g.Printf("objc_msgSend")
}
g.Printf(")(")
if f.Static && !f.Constructor {
g.Printf("[%s class]", n.Name)
} else {
if super {
g.Printf("&_super")
} else {
g.Printf("_this")
}
}
g.Printf(", @selector(%s)", f.Sig)
for _, a := range f.Params {
arg := "_" + a.Name
if a == errParam {
arg = "&" + a.Name
}
g.Printf(", %s", arg)
}
g.Printf(");\n")
if errParam != nil {
g.Printf("NSError *_%s = nil;\n", errParam.Name)
if f.Ret != nil {
g.Printf("if (!res && %s != nil) {\n", errParam.Name)
} else {
g.Printf("if (%s != nil) {\n", errParam.Name)
}
g.Printf(" _%[1]s = %[1]s;\n", errParam.Name)
g.Printf("}\n")
g.genObjCToC("_"+errParam.Name, g.errType(), modeRetained)
}
ret := f.Ret
if ret != nil && ret.Kind == objc.Bool && errParam != nil {
ret = nil
}
if ret != nil {
g.genObjCToC("res", ret, modeRetained)
}
switch {
case ret != nil && errParam != nil:
stype := strings.Replace(g.cType(ret), " ", "_", -1)
g.Printf("ret_%s _sres = {_res, __%s};\n", stype, errParam.Name)
g.Printf("return _sres;\n")
case ret != nil:
g.Printf("return _res;\n")
case errParam != nil:
g.Printf("return __%s;\n", errParam.Name)
}
g.Outdent()
g.Printf("}\n\n")
}
func (_ *ObjcWrapper) errType() *objc.Type {
return &objc.Type{Kind: objc.Class, Name: "NSError"}
}
func (g *ObjcWrapper) genM(n *objc.Named) {
for _, f := range n.Funcs {
if !g.isFuncSupported(f) {
continue
}
g.genCFuncDecl("cproxy", n.GoName, f)
g.genCFuncBody(n, f, false)
}
}
func (g *ObjcWrapper) GenH() {
g.Printf(gobindPreamble)
g.Printf("#include \"seq.h\"\n\n")
for _, m := range g.modules {
g.Printf("@import %s;\n", m)
}
// Include header files for generated types
for _, n := range g.pkgNames {
hasGen := false
for _, t := range g.modMap[n] {
if t.Generated {
hasGen = true
break
}
}
if hasGen {
g.Printf("#import %q\n", n+".objc.h")
}
}
for _, tn := range []string{"int", "nstring", "nbyteslice", "long", "unsigned long", "short", "unsigned short", "bool", "char", "unsigned char", "float", "double"} {
sn := strings.Replace(tn, " ", "_", -1)
g.Printf("typedef struct ret_%s {\n", sn)
g.Printf(" %s res;\n", tn)
g.Printf(" int err;\n")
g.Printf("} ret_%s;\n", sn)
}
g.Printf("\n")
for _, n := range g.types {
for _, f := range n.AllMethods {
if !g.isFuncSupported(f) {
continue
}
g.Printf("extern ")
g.genCFuncDecl("cproxy", n.GoName, f)
g.Printf(";\n")
if _, exists := g.supers[n.GoName]; exists {
g.Printf("extern ")
g.genCFuncDecl("csuper", n.GoName, f)
g.Printf(";\n")
}
}
}
for _, cls := range g.types {
g.genH(cls)
}
}
func (g *ObjcWrapper) genH(n *objc.Named) {
for _, f := range n.Funcs {
if !g.isFuncSupported(f) {
continue
}
g.Printf("extern ")
g.genCFuncDecl("cproxy", n.GoName, f)
g.Printf(";\n")
}
}
func (g *ObjcWrapper) genCFuncDecl(prefix, name string, f *objc.Func) {
returnsErr := len(f.Params) > 0 && g.isErrorType(f.Params[len(f.Params)-1].Type)
ret := f.Ret
if ret != nil && returnsErr && ret.Kind == objc.Bool {
ret = nil
}
switch {
case ret != nil && returnsErr:
g.Printf("ret_%s", strings.Replace(g.cType(ret), " ", "_", -1))
case ret != nil:
g.Printf("%s", g.cType(ret))
case returnsErr:
g.Printf("int")
default:
g.Printf("void")
}
g.Printf(" %s", prefix)
if f.Static {
g.Printf("_s")
}
g.Printf("_%s_%s(", name, f.GoName)
if !f.Static {
g.Printf("int this")
}
for i, p := range f.Params {
if i == len(f.Params)-1 && returnsErr {
break
}
if !f.Static || i > 0 {
g.Printf(", ")
}
g.Printf("%s %s", g.cType(p.Type), p.Name)
}
g.Printf(")")
}
func (g *ObjcWrapper) GenGo() {
g.Printf(gobindPreamble)
g.Printf("package main\n\n")
g.Printf("// #include \"interfaces.h\"\n")
g.Printf("import \"C\"\n\n")
g.Printf("import \"ObjC\"\n")
g.Printf("import _seq \"golang.org/x/mobile/bind/seq\"\n")
for _, n := range g.types {
for _, f := range n.Funcs {
if g.isFuncSupported(f) {
pkgName := n.Module + "/" + n.GoName
g.Printf("import %q\n", "ObjC/"+pkgName)
break
}
}
}
g.Printf("\n")
g.Printf("type proxy interface { Bind_proxy_refnum__() int32 }\n\n")
g.Printf("// Suppress unused package error\n\n")
g.Printf("var _ = _seq.FromRefNum\n")
g.Printf("const _ = ObjC.Dummy\n\n")
for _, n := range g.types {
g.genGo(n)
}
}
func (g *ObjcWrapper) genGo(n *objc.Named) {
g.Printf("func init() {\n")
g.Indent()
for _, f := range n.Funcs {
if !g.isFuncSupported(f) {
continue
}
g.Printf("%s.%s = func", n.GoName, f.GoName)
g.genFuncDecl(false, f)
g.genFuncBody(n, f, "cproxy")
}
g.Outdent()
g.Printf("}\n\n")
g.Printf("type proxy_class_%s _seq.Ref\n\n", n.GoName)
g.Printf("func (p *proxy_class_%s) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }\n\n", n.GoName)
for _, f := range n.AllMethods {
if !g.isFuncSupported(f) {
continue
}
g.Printf("func (p *proxy_class_%s) %s", n.GoName, f.GoName)
g.genFuncDecl(false, f)
g.genFuncBody(n, f, "cproxy")
}
if _, exists := g.supers[n.GoName]; exists {
g.Printf("func (p *proxy_class_%s) Super() ObjC.%s {\n", n.GoName, n.Module+"_"+n.GoName)
g.Printf(" return &super_%s{p}\n", n.GoName)
g.Printf("}\n\n")
g.Printf("type super_%s struct {*proxy_class_%[1]s}\n\n", n.GoName)
for _, f := range n.AllMethods {
if !g.isFuncSupported(f) {
continue
}
g.Printf("func (p *super_%s) %s", n.GoName, f.GoName)
g.genFuncDecl(false, f)
g.genFuncBody(n, f, "csuper")
}
}
}
func (g *ObjcWrapper) genFuncBody(n *objc.Named, f *objc.Func, prefix string) {
g.Printf(" {\n")
g.Indent()
var errParam *objc.Param
for i, a := range f.Params {
if i == len(f.Params)-1 && g.isErrorType(a.Type) {
errParam = a
break
}
g.genWrite(a)
}
ret := f.Ret
if ret != nil && errParam != nil && ret.Kind == objc.Bool {
ret = nil
}
if ret != nil || errParam != nil {
g.Printf("res := ")
}
g.Printf("C.%s", prefix)
if f.Static {
g.Printf("_s")
}
g.Printf("_%s_%s(", n.GoName, f.GoName)
if !f.Static {
g.Printf("C.int(p.Bind_proxy_refnum__())")
}
for i, a := range f.Params {
if a == errParam {
break
}
if !f.Static || i > 0 {
g.Printf(", ")
}
g.Printf("_%s", a.Name)
}
g.Printf(")\n")
switch {
case ret != nil && errParam != nil:
g.genRead("_res", "res.res", ret)
g.genRefRead("_"+errParam.Name, "res.err", "error", "proxy_error")
g.Printf("return _res, _%s\n", errParam.Name)
case ret != nil:
g.genRead("_res", "res", ret)
g.Printf("return _res\n")
case errParam != nil:
g.genRefRead("_"+errParam.Name, "res", "error", "proxy_error")
g.Printf("return _%s\n", errParam.Name)
}
g.Outdent()
g.Printf("}\n\n")
}
func (g *ObjcWrapper) genCToObjC(name string, t *objc.Type, mode varMode) {
switch t.Kind {
case objc.String:
g.Printf("NSString *_%s = go_seq_to_objc_string(%s);\n", name, name)
case objc.Bool:
g.Printf("BOOL _%s = %s ? YES : NO;\n", name, name)
case objc.Data:
g.Printf("NSData *_%s = go_seq_to_objc_bytearray(%s, %d);\n", name, name, toCFlag(mode == modeRetained))
case objc.Int, objc.Uint, objc.Short, objc.Ushort, objc.Char, objc.Uchar, objc.Float, objc.Double:
g.Printf("%s _%s = (%s)%s;\n", g.objcType(t), name, g.objcType(t), name)
case objc.Class, objc.Protocol:
g.Printf("GoSeqRef* %s_ref = go_seq_from_refnum(%s);\n", name, name)
g.Printf("%s _%s;\n", g.objcType(t), name)
g.Printf("if (%s_ref != NULL) {\n", name)
g.Printf(" _%s = %s_ref.obj;\n", name, name)
g.Printf("}\n")
default:
panic("invalid kind")
}
}
func (g *ObjcWrapper) genObjCToC(name string, t *objc.Type, mode varMode) {
switch t.Kind {
case objc.String:
g.Printf("nstring _%s = go_seq_from_objc_string(%s);\n", name, name)
case objc.Data:
g.Printf("nbyteslice _%s = go_seq_from_objc_bytearray(%s, %d);\n", name, name, toCFlag(mode == modeRetained))
case objc.Bool, objc.Int, objc.Uint, objc.Short, objc.Ushort, objc.Char, objc.Uchar, objc.Float, objc.Double:
g.Printf("%s _%s = (%s)%s;\n", g.cType(t), name, g.cType(t), name)
case objc.Protocol, objc.Class:
g.Printf("int _%s = go_seq_to_refnum(%s);\n", name, name)
default:
panic("invalid kind")
}
}
func (g *ObjcWrapper) genWrite(a *objc.Param) {
switch a.Type.Kind {
case objc.String:
g.Printf("_%s := encodeString(%s)\n", a.Name, a.Name)
case objc.Data:
g.Printf("_%s := fromSlice(%s, false)\n", a.Name, a.Name)
case objc.Bool:
g.Printf("_%s := %s(0)\n", a.Name, g.cgoType(a.Type))
g.Printf("if %s {\n", a.Name)
g.Printf(" _%s = %s(1)\n", a.Name, g.cgoType(a.Type))
g.Printf("}\n")
case objc.Int, objc.Uint, objc.Short, objc.Ushort, objc.Char, objc.Uchar, objc.Float, objc.Double:
g.Printf("_%s := %s(%s)\n", a.Name, g.cgoType(a.Type), a.Name)
case objc.Protocol, objc.Class:
g.Printf("var _%s %s = _seq.NullRefNum\n", a.Name, g.cgoType(a.Type))
g.Printf("if %s != nil {\n", a.Name)
g.Printf(" _%s = %s(_seq.ToRefNum(%s))\n", a.Name, g.cgoType(a.Type), a.Name)
g.Printf("}\n")
default:
panic("invalid kind")
}
}
func (g *ObjcWrapper) genRead(to, from string, t *objc.Type) {
switch t.Kind {
case objc.Int, objc.Uint, objc.Uchar, objc.Short, objc.Ushort, objc.Char, objc.Float, objc.Double:
g.Printf("%s := %s(%s)\n", to, g.goType(t, false), from)
case objc.Bool:
g.Printf("%s := %s != 0\n", to, from)
case objc.String:
g.Printf("%s := decodeString(%s)\n", to, from)
case objc.Data:
g.Printf("%s := toSlice(%s, true)\n", to, from)
case objc.Protocol, objc.Class:
var proxyName string
if n := g.lookupImported(t); n != nil {
proxyName = "proxy_class_" + n.GoName
}
g.genRefRead(to, from, g.goType(t, false), proxyName)
default:
panic("invalid kind")
}
}
func (g *ObjcWrapper) genRefRead(to, from string, intfName, proxyName string) {
g.Printf("var %s %s\n", to, intfName)
g.Printf("%s_ref := _seq.FromRefNum(int32(%s))\n", to, from)
g.Printf("if %s_ref != nil {\n", to)
g.Printf(" if %s < 0 { // go object\n", from)
g.Printf(" %s = %s_ref.Get().(%s)\n", to, to, intfName)
if proxyName != "" {
g.Printf(" } else { // foreign object\n")
g.Printf(" %s = (*%s)(%s_ref)\n", to, proxyName, to)
}
g.Printf(" }\n")
g.Printf("}\n")
}
// Packages return the list of Go packages to be generated.
func (g *ObjcWrapper) Packages() []string {
return g.pkgNames
}
func (g *ObjcWrapper) GenPackage(idx int) {
pkg := g.pkgNames[idx]
g.Printf(gobindPreamble)
g.Printf("package %s\n\n", path.Base(pkg))
g.Printf("import \"ObjC\"\n\n")
g.Printf("const _ = ObjC.Dummy\n\n")
for _, n := range g.modMap[pkg] {
g.Printf("type %s ObjC.%s\n", n.GoName, n.Module+"_"+n.GoName)
}
if n, ok := g.typePkgs[pkg]; ok {
g.Printf("var (\n")
g.Indent()
// Functions
for _, f := range n.Funcs {
if !g.isFuncSupported(f) {
continue
}
g.Printf("%s func", f.GoName)
g.genFuncDecl(false, f)
g.Printf("\n")
}
g.Outdent()
g.Printf(")\n\n")
}
}
func (g *ObjcWrapper) GenInterfaces() {
g.Printf(gobindPreamble)
g.Printf("package ObjC\n\n")
g.Printf("// Used to silence this package not used errors\n")
g.Printf("const Dummy = 0\n\n")
for _, n := range g.types {
g.genInterface(n)
}
}
func (g *ObjcWrapper) genInterface(n *objc.Named) {
g.Printf("type %s interface {\n", n.Module+"_"+n.GoName)
g.Indent()
// Methods
for _, f := range n.AllMethods {
if !g.isFuncSupported(f) {
continue
}
g.Printf("%s", f.GoName)
g.genFuncDecl(true, f)
g.Printf("\n")
}
if _, exists := g.supers[n.GoName]; exists {
g.Printf("Super() %s\n", n.Module+"_"+n.GoName)
}
g.Outdent()
g.Printf("}\n\n")
}
func (g *ObjcWrapper) genFuncDecl(local bool, f *objc.Func) {
var returnsErr bool
g.Printf("(")
for i, p := range f.Params {
if i == len(f.Params)-1 && g.isErrorType(p.Type) {
returnsErr = true
break
}
if i > 0 {
g.Printf(", ")
}
g.Printf("%s %s", p.Name, g.goType(p.Type, local))
}
g.Printf(")")
if f.Ret != nil || returnsErr {
ret := f.Ret
if ret.Kind == objc.Bool && returnsErr {
// Skip the bool result and use the error results.
ret = nil
}
if ret != nil {
g.Printf(" (%s", g.goType(f.Ret, local))
if returnsErr {
g.Printf(", error")
}
g.Printf(")")
} else {
g.Printf(" error")
}
}
}
func (g *ObjcWrapper) isFuncSupported(f *objc.Func) bool {
for i, p := range f.Params {
if !g.isSupported(p.Type) {
if i < len(f.Params)-1 || !g.isErrorType(p.Type) {
return false
}
}
}
if f.Ret != nil {
return g.isSupported(f.Ret)
}
return true
}
func (g *ObjcWrapper) isErrorType(t *objc.Type) bool {
// Must be a NSError ** type
return t.Kind == objc.Class && t.Indirect && t.Name == "NSError"
}
func (g *ObjcWrapper) isSupported(t *objc.Type) bool {
if t.Indirect {
return false
}
switch t.Kind {
case objc.Unknown:
return false
case objc.Protocol:
// TODO: support inout parameters.
return !strings.HasSuffix(t.Decl, " *")
case objc.Class:
return t.Name != "SEL" && t.Name != "void"
default:
return true
}
}
func (g *ObjcWrapper) cgoType(t *objc.Type) string {
switch t.Kind {
case objc.Uint:
return "C.ulong"
case objc.Ushort:
return "C.ushort"
case objc.Uchar:
return "C.uchar"
default:
return "C." + g.cType(t)
}
}
func (g *ObjcWrapper) cType(t *objc.Type) string {
switch t.Kind {
case objc.Protocol, objc.Class:
return "int"
case objc.String:
return "nstring"
case objc.Data:
return "nbyteslice"
case objc.Int:
return "long"
case objc.Uint:
return "unsigned long"
case objc.Short:
return "short"
case objc.Ushort:
return "unsigned short"
case objc.Bool:
return "char"
case objc.Char:
return "char"
case objc.Uchar:
return "unsigned char"
case objc.Float:
return "float"
case objc.Double:
return "double"
default:
panic("invalid kind")
}
}
func (g *ObjcWrapper) objcType(t *objc.Type) string {
return t.Decl
}
func (g *ObjcWrapper) lookupImported(t *objc.Type) *objc.Named {
var mangled string
switch t.Kind {
case objc.Class:
mangled = t.Name + "C"
case objc.Protocol:
mangled = t.Name + "P"
default:
panic("invalid type kind")
}
if n, exists := g.imported[mangled]; exists {
return n
}
return g.imported[t.Name]
}
func (g *ObjcWrapper) goType(t *objc.Type, local bool) string {
switch t.Kind {
case objc.String:
return "string"
case objc.Data:
return "[]byte"
case objc.Int:
return "int"
case objc.Uint:
return "uint"
case objc.Short:
return "int16"
case objc.Ushort:
return "uint16"
case objc.Bool:
return "bool"
case objc.Char:
return "byte"
case objc.Uchar:
return "uint8"
case objc.Float:
return "float32"
case objc.Double:
return "float64"
case objc.Protocol, objc.Class:
var n *objc.Named
n = g.lookupImported(t)
name := n.Module + "_" + n.GoName
if !local {
name = "ObjC." + name
}
return name
default:
panic("invalid kind")
}
}

9
bind/implicit.go Normal file
View file

@ -0,0 +1,9 @@
// This file imports implicit dependencies required by generated code.
//go:build mobile_implicit
package bind
import (
_ "golang.org/x/mobile/bind/seq"
)

157
bind/java/ClassesTest.java Normal file
View file

@ -0,0 +1,157 @@
// Copyright 2016 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 go;
import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
import java.io.InputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
import javapkg.Javapkg;
import javapkg.I;
import javapkg.GoObject;
import javapkg.GoRunnable;
import javapkg.GoSubset;
import javapkg.GoInputStream;
import javapkg.GoArrayList;
public class ClassesTest extends InstrumentationTestCase {
public void testConst() {
assertEquals("const Float", Float.MIN_VALUE, Javapkg.floatMin());
assertEquals("const String", java.util.jar.JarFile.MANIFEST_NAME, Javapkg.manifestName());
assertEquals("const Int", 7, Integer.SIZE, Javapkg.integerBytes());
}
public void testFunction() {
Javapkg.systemCurrentTimeMillis();
}
public void testMethod() {
try {
assertEquals("Integer.decode", 0xff, Javapkg.integerDecode("0xff"));
} catch (Exception e) {
throw new RuntimeException(e);
}
Exception exc = null;
try {
Javapkg.integerDecode("obviously wrong");
} catch (Exception e) {
exc = e;
}
assertNotNull("IntegerDecode Exception", exc);
}
public void testOverloadedMethod() {
try {
assertEquals("Integer.parseInt", 0xc4, Javapkg.integerParseInt("c4", 16));
} catch (Exception e) {
throw new RuntimeException(e);
}
Exception exc = null;
try {
Javapkg.integerParseInt("wrong", 16);
} catch (Exception e) {
exc = e;
}
assertNotNull("integerParseInt Exception", exc);
assertEquals("Integer.valueOf", 42, Javapkg.integerValueOf(42));
}
public void testException() {
Exception exc = null;
try {
Javapkg.provokeRuntimeException();
} catch (Exception e) {
exc = e;
}
assertNotNull("RuntimeException", exc);
}
public void testField() {
GoRunnable r = new GoRunnable();
String f = r.getField();
}
public void testGoObject() {
Runnable r = new GoRunnable();
r.run();
assertEquals("GoRunnable.toString", r.toString(), Javapkg.ToStringPrefix);
Runnable r2 = ((GoRunnable)r).getThis();
assertTrue("GoObject.this", r == r2);
Object o = new GoObject();
assertEquals("GoObject hashCode", 42, o.hashCode());
Object o2 = Javapkg.constructGoObject();
assertEquals("GoObject hashCode", 42, o2.hashCode());
assertTrue("GoObject.toString", o.toString().startsWith(Javapkg.ToStringPrefix));
Javapkg.runRunnable(r);
final boolean[] ran = new boolean[1];
Runnable r3 = new Runnable(){
@Override public void run() {
ran[0] = true;
}
};
Javapkg.runRunnable(r3);
assertTrue("RunRunnable", ran[0]);
assertTrue("RunnableRoundtrip Java", r3 == Javapkg.runnableRoundtrip(r3));
assertTrue("RunnableRoundtrip Go", r == Javapkg.runnableRoundtrip(r));
Runnable r5 = Javapkg.constructGoRunnable();
r5.run();
}
public void testTypedException() {
InputStream is = new GoInputStream();
Exception exc = null;
try {
is.read();
} catch (IOException e) {
exc = e;
}
assertNotNull("IOException", exc);
assertEquals("IOException message", Javapkg.IOExceptionMessage, exc.getMessage());
}
public void testInnerClass() {
Character.Subset s = new Character.Subset(""){};
Character.Subset s2 = new GoSubset("");
Javapkg.callSubset(s);
Javapkg.callSubset(s2);
}
public void testNew() {
Object o = Javapkg.newJavaObject();
assertTrue("new Object()", o != null);
Integer i = Javapkg.newJavaInteger();
assertEquals("new Integer(42)", 42, i.intValue());
}
private static class InterfaceRunnable implements I, Runnable {
@Override public void run() {
}
}
public void testCast() {
Runnable r1 = new GoRunnable();
Runnable r1c = Javapkg.castRunnable((Object)r1);
assertTrue("Casting Go object", r1c != null);
Runnable r2 = new Runnable() {
@Override public void run() {
}
};
Runnable r2c = Javapkg.castRunnable((Object)r2);
assertTrue("Casting Java object", r2c != null);
Runnable r3c = Javapkg.castInterface(new InterfaceRunnable());
assertTrue("Casting Go interface implementation", r3c != null);
Runnable r4c = Javapkg.castRunnable(new Object());
assertTrue("Invalid cast", r4c == null);
}
public void testUnwrap() {
GoArrayList l = new GoArrayList();
Javapkg.unwrapGoArrayList(l);
}
}

View file

@ -0,0 +1,15 @@
// Copyright 2016 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 go;
import android.test.InstrumentationTestCase;
import org.golang.custompkg.testpkg.Testpkg;
public class CustomPkgTest extends InstrumentationTestCase {
public void testHi() {
Testpkg.hi();
}
}

400
bind/java/Seq.java Normal file
View file

@ -0,0 +1,400 @@
// Copyright 2014 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 go;
import android.content.Context;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
import go.Universe;
// Seq is a sequence of machine-dependent encoded values.
// Used by automatically generated language bindings to talk to Go.
public class Seq {
private static Logger log = Logger.getLogger("GoSeq");
// also known to bind/seq/ref.go and bind/objc/seq_darwin.m
private static final int NULL_REFNUM = 41;
// use single Ref for null Object
public static final Ref nullRef = new Ref(NULL_REFNUM, null);
// The singleton GoRefQueue
private static final GoRefQueue goRefQueue = new GoRefQueue();
static {
System.loadLibrary("gojni");
init();
Universe.touch();
}
// setContext sets the context in the go-library to be used in RunOnJvm.
public static void setContext(Context context) {
setContext((java.lang.Object)context);
}
private static native void init();
// Empty method to run class initializer
public static void touch() {}
private Seq() {
}
// ctx is an android.context.Context.
static native void setContext(java.lang.Object ctx);
public static void incRefnum(int refnum) {
tracker.incRefnum(refnum);
}
// incRef increments the reference count of Java objects.
// For proxies for Go objects, it calls into the Proxy method
// incRefnum() to make sure the Go reference count is positive
// even if the Proxy is garbage collected and its Ref is finalized.
public static int incRef(Object o) {
return tracker.inc(o);
}
public static int incGoObjectRef(GoObject o) {
return o.incRefnum();
}
// trackGoRef tracks a Go reference and decrements its refcount
// when the given GoObject wrapper is garbage collected.
//
// TODO(crawshaw): We could cut down allocations for frequently
// sent Go objects by maintaining a map to weak references. This
// however, would require allocating two objects per reference
// instead of one. It also introduces weak references, the bane
// of any Java debugging session.
//
// When we have real code, examine the tradeoffs.
public static void trackGoRef(int refnum, GoObject obj) {
if (refnum > 0) {
throw new RuntimeException("trackGoRef called with Java refnum " + refnum);
}
goRefQueue.track(refnum, obj);
}
public static Ref getRef(int refnum) {
return tracker.get(refnum);
}
// Increment the Go reference count before sending over a refnum.
// The ref parameter is only used to make sure the referenced
// object is not garbage collected before Go increments the
// count. It's the equivalent of Go's runtime.KeepAlive.
public static native void incGoRef(int refnum, GoObject ref);
// Informs the Go ref tracker that Java is done with this refnum.
static native void destroyRef(int refnum);
// decRef is called from seq.FinalizeRef
static void decRef(int refnum) {
tracker.dec(refnum);
}
// A GoObject is a Java class implemented in Go. When a GoObject
// is passed to Go, it is wrapped in a Go proxy, to make it behave
// the same as passing a regular Java class.
public interface GoObject {
// Increment refcount and return the refnum of the proxy.
//
// The Go reference count need to be bumped while the
// refnum is passed to Go, to avoid finalizing and
// invalidating it before being translated on the Go side.
int incRefnum();
}
// A Proxy is a Java object that proxies a Go object. Proxies, unlike
// GoObjects, are unwrapped to their Go counterpart when deserialized
// in Go.
public interface Proxy extends GoObject {}
// A Ref represents an instance of a Java object passed back and forth
// across the language boundary.
public static final class Ref {
public final int refnum;
private int refcnt; // Track how many times sent to Go.
public final Object obj; // The referenced Java obj.
Ref(int refnum, Object o) {
if (refnum < 0) {
throw new RuntimeException("Ref instantiated with a Go refnum " + refnum);
}
this.refnum = refnum;
this.refcnt = 0;
this.obj = o;
}
void inc() {
// Count how many times this ref's Java object is passed to Go.
if (refcnt == Integer.MAX_VALUE) {
throw new RuntimeException("refnum " + refnum + " overflow");
}
refcnt++;
}
}
static final RefTracker tracker = new RefTracker();
static final class RefTracker {
private static final int REF_OFFSET = 42;
// Next Java object reference number.
//
// Reference numbers are positive for Java objects,
// and start, arbitrarily at a different offset to Go
// to make debugging by reading Seq hex a little easier.
private int next = REF_OFFSET; // next Java object ref
// Java objects that have been passed to Go. refnum -> Ref
// The Ref obj field is non-null.
// This map pins Java objects so they don't get GCed while the
// only reference to them is held by Go code.
private final RefMap javaObjs = new RefMap();
// Java objects to refnum
private final IdentityHashMap<Object, Integer> javaRefs = new IdentityHashMap<>();
// inc increments the reference count of a Java object when it
// is sent to Go. inc returns the refnum for the object.
synchronized int inc(Object o) {
if (o == null) {
return NULL_REFNUM;
}
if (o instanceof Proxy) {
return ((Proxy)o).incRefnum();
}
Integer refnumObj = javaRefs.get(o);
if (refnumObj == null) {
if (next == Integer.MAX_VALUE) {
throw new RuntimeException("createRef overflow for " + o);
}
refnumObj = next++;
javaRefs.put(o, refnumObj);
}
int refnum = refnumObj;
Ref ref = javaObjs.get(refnum);
if (ref == null) {
ref = new Ref(refnum, o);
javaObjs.put(refnum, ref);
}
ref.inc();
return refnum;
}
synchronized void incRefnum(int refnum) {
Ref ref = javaObjs.get(refnum);
if (ref == null) {
throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
}
ref.inc();
}
// dec decrements the reference count of a Java object when
// Go signals a corresponding proxy object is finalized.
// If the count reaches zero, the Java object is removed
// from the javaObjs map.
synchronized void dec(int refnum) {
if (refnum <= 0) {
// We don't keep track of the Go object.
// This must not happen.
log.severe("dec request for Go object "+ refnum);
return;
}
if (refnum == Seq.nullRef.refnum) {
return;
}
// Java objects are removed on request of Go.
Ref obj = javaObjs.get(refnum);
if (obj == null) {
throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
}
obj.refcnt--;
if (obj.refcnt <= 0) {
javaObjs.remove(refnum);
javaRefs.remove(obj.obj);
}
}
// get returns an existing Ref to a Java object.
synchronized Ref get(int refnum) {
if (refnum < 0) {
throw new RuntimeException("ref called with Go refnum " + refnum);
}
if (refnum == NULL_REFNUM) {
return nullRef;
}
Ref ref = javaObjs.get(refnum);
if (ref == null) {
throw new RuntimeException("unknown java Ref: "+refnum);
}
return ref;
}
}
// GoRefQueue is a queue of GoRefs that are no longer live. An internal thread
// processes the queue and decrement the reference count on the Go side.
static class GoRefQueue extends ReferenceQueue<GoObject> {
// The set of tracked GoRefs. If we don't hold on to the GoRef instances, the Java GC
// will not add them to the queue when their referents are reclaimed.
private final Collection<GoRef> refs = Collections.synchronizedCollection(new HashSet<GoRef>());
void track(int refnum, GoObject obj) {
refs.add(new GoRef(refnum, obj, this));
}
GoRefQueue() {
Thread daemon = new Thread(new Runnable() {
@Override public void run() {
while (true) {
try {
GoRef ref = (GoRef)remove();
refs.remove(ref);
destroyRef(ref.refnum);
ref.clear();
} catch (InterruptedException e) {
// Ignore
}
}
}
});
daemon.setDaemon(true);
daemon.setName("GoRefQueue Finalizer Thread");
daemon.start();
}
}
// A GoRef is a PhantomReference to a Java proxy for a Go object.
// GoRefs are enqueued to the singleton GoRefQueue when no longer live,
// so the corresponding reference count can be decremented.
static class GoRef extends PhantomReference<GoObject> {
final int refnum;
GoRef(int refnum, GoObject obj, GoRefQueue q) {
super(obj, q);
if (refnum > 0) {
throw new RuntimeException("GoRef instantiated with a Java refnum " + refnum);
}
this.refnum = refnum;
}
}
// RefMap is a mapping of integers to Ref objects.
//
// The integers can be sparse. In Go this would be a map[int]*Ref.
static final class RefMap {
private int next = 0;
private int live = 0;
private int[] keys = new int[16];
private Ref[] objs = new Ref[16];
RefMap() {}
Ref get(int key) {
int i = Arrays.binarySearch(keys, 0, next, key);
if (i >= 0) {
return objs[i];
}
return null;
}
void remove(int key) {
int i = Arrays.binarySearch(keys, 0, next, key);
if (i >= 0) {
if (objs[i] != null) {
objs[i] = null;
live--;
}
}
}
void put(int key, Ref obj) {
if (obj == null) {
throw new RuntimeException("put a null ref (with key "+key+")");
}
int i = Arrays.binarySearch(keys, 0, next, key);
if (i >= 0) {
if (objs[i] == null) {
objs[i] = obj;
live++;
}
if (objs[i] != obj) {
throw new RuntimeException("replacing an existing ref (with key "+key+")");
}
return;
}
if (next >= keys.length) {
grow();
i = Arrays.binarySearch(keys, 0, next, key);
}
i = ~i;
if (i < next) {
// Insert, shift everything afterwards down.
System.arraycopy(keys, i, keys, i+1, next-i);
System.arraycopy(objs, i, objs, i+1, next-i);
}
keys[i] = key;
objs[i] = obj;
live++;
next++;
}
private void grow() {
// Compact and (if necessary) grow backing store.
int[] newKeys;
Ref[] newObjs;
int len = 2*roundPow2(live);
if (len > keys.length) {
newKeys = new int[keys.length*2];
newObjs = new Ref[objs.length*2];
} else {
newKeys = keys;
newObjs = objs;
}
int j = 0;
for (int i = 0; i < keys.length; i++) {
if (objs[i] != null) {
newKeys[j] = keys[i];
newObjs[j] = objs[i];
j++;
}
}
for (int i = j; i < newKeys.length; i++) {
newKeys[i] = 0;
newObjs[i] = null;
}
keys = newKeys;
objs = newObjs;
next = j;
if (live != next) {
throw new RuntimeException("bad state: live="+live+", next="+next);
}
}
private static int roundPow2(int x) {
int p = 1;
while (p < x) {
p *= 2;
}
return p;
}
}
}

162
bind/java/SeqBench.java Normal file
View file

@ -0,0 +1,162 @@
// Copyright 2016 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 go;
import android.test.InstrumentationTestCase;
import android.util.Log;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import benchmark.*;
public class SeqBench extends InstrumentationTestCase {
public static class AnI implements I {
@Override public void f() {
}
}
private static class Benchmarks implements benchmark.Benchmarks {
private static Map<String, Runnable> benchmarks;
private static ExecutorService executor = Executors.newSingleThreadExecutor();
static {
benchmarks = new HashMap<String, Runnable>();
benchmarks.put("Empty", new Runnable() {
@Override public void run() {
}
});
benchmarks.put("Noargs", new Runnable() {
@Override public void run() {
Benchmark.noargs();
}
});
benchmarks.put("Onearg", new Runnable() {
@Override public void run() {
Benchmark.onearg(0);
}
});
benchmarks.put("Manyargs", new Runnable() {
@Override public void run() {
Benchmark.manyargs(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
});
benchmarks.put("Oneret", new Runnable() {
@Override public void run() {
Benchmark.oneret();
}
});
final I javaRef = new AnI();
benchmarks.put("Refforeign", new Runnable() {
@Override public void run() {
Benchmark.ref(javaRef);
}
});
final I goRef = Benchmark.newI();
benchmarks.put("Refgo", new Runnable() {
@Override public void run() {
Benchmark.ref(goRef);
}
});
benchmarks.put("StringShort", new Runnable() {
@Override public void run() {
Benchmark.string(Benchmark.ShortString);
}
});
benchmarks.put("StringLong", new Runnable() {
@Override public void run() {
Benchmark.string(Benchmark.LongString);
}
});
benchmarks.put("StringShortUnicode", new Runnable() {
@Override public void run() {
Benchmark.string(Benchmark.ShortStringUnicode);
}
});
benchmarks.put("StringLongUnicode", new Runnable() {
@Override public void run() {
Benchmark.string(Benchmark.LongStringUnicode);
}
});
benchmarks.put("StringRetShort", new Runnable() {
@Override public void run() {
Benchmark.stringRetShort();
}
});
benchmarks.put("StringRetLong", new Runnable() {
@Override public void run() {
Benchmark.stringRetLong();
}
});
final byte[] shortSlice = Benchmark.getShortSlice();
benchmarks.put("SliceShort", new Runnable() {
@Override public void run() {
Benchmark.slice(shortSlice);
}
});
final byte[] longSlice = Benchmark.getLongSlice();
benchmarks.put("SliceLong", new Runnable() {
@Override public void run() {
Benchmark.slice(longSlice);
}
});
}
public void runDirect(String name, final long n) {
final Runnable r = benchmarks.get(name);
try {
executor.submit(new Runnable() {
@Override public void run() {
for (int i = 0; i < n; i++) {
r.run();
}
}
}).get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void run(String name, long n) {
final Runnable r = benchmarks.get(name);
for (int i = 0; i < n; i++) {
r.run();
}
}
@Override public I newI() {
return new AnI();
}
@Override public void ref(I i) {
}
@Override public void noargs() {
}
@Override public void onearg(long i) {
}
@Override public long oneret() {
return 0;
}
@Override public void manyargs(long p0, long p1, long p2, long p3, long p4, long p5, long p6, long p7, long gp8, long p9) {
}
@Override public void string(String s) {
}
@Override public void slice(byte[] s) {
}
@Override public String stringRetShort() {
return Benchmark.ShortString;
}
@Override public String stringRetLong() {
return Benchmark.LongString;
}
}
public void testBenchmark() {
Benchmark.runBenchmarks(new Benchmarks());
}
}

602
bind/java/SeqTest.java Normal file
View file

@ -0,0 +1,602 @@
// Copyright 2014 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 go;
import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
import java.util.Arrays;
import java.util.Random;
import testpkg.*;
import secondpkg.Secondpkg;
public class SeqTest extends InstrumentationTestCase {
public SeqTest() {
}
public void testConst() {
assertEquals("const String", "a string", Testpkg.AString);
assertEquals("const Int", 7, Testpkg.AnInt);
assertEquals("const Bool", true, Testpkg.ABool);
assertEquals("const Float", 0.12345, Testpkg.AFloat, 0.0001);
assertEquals("const MinInt32", -1<<31, Testpkg.MinInt32);
assertEquals("const MaxInt32", (1<<31) - 1, Testpkg.MaxInt32);
assertEquals("const MinInt64", -1L<<63, Testpkg.MinInt64);
assertEquals("const MaxInt64", (1L<<63) - 1, Testpkg.MaxInt64);
assertEquals("const SmallestNonzeroFloat64", 4.940656458412465441765687928682213723651e-324, Testpkg.SmallestNonzeroFloat64, 1e-323);
assertEquals("const MaxFloat64", 1.797693134862315708145274237317043567981e+308, Testpkg.MaxFloat64, 0.0001);
assertEquals("const SmallestNonzeroFloat32", 1.401298464324817070923729583289916131280e-45, Testpkg.SmallestNonzeroFloat32, 1e-44);
assertEquals("const MaxFloat32", 3.40282346638528859811704183484516925440e+38, Testpkg.MaxFloat32, 0.0001);
assertEquals("const Log2E", 1/0.693147180559945309417232121458176568075500134360255254120680009, Testpkg.Log2E, 0.0001);
}
public void testRefMap() {
// Ensure that the RefMap.live count is kept in sync
// even a particular reference number is removed and
// added again
Seq.RefMap m = new Seq.RefMap();
Seq.Ref r = new Seq.Ref(1, null);
m.put(r.refnum, r);
m.remove(r.refnum);
m.put(r.refnum, r);
// Force the RefMap to grow, to activate the sanity
// checking of the live count in RefMap.grow.
for (int i = 2; i < 24; i++) {
m.put(i, new Seq.Ref(i, null));
}
}
public void testVar() {
assertEquals("var StringVar", "a string var", Testpkg.getStringVar());
String newStringVar = "a new string var";
Testpkg.setStringVar(newStringVar);
assertEquals("var StringVar", newStringVar, Testpkg.getStringVar());
assertEquals("var IntVar", 77, Testpkg.getIntVar());
long newIntVar = 777;
Testpkg.setIntVar(newIntVar);
assertEquals("var IntVar", newIntVar, Testpkg.getIntVar());
S s0 = Testpkg.getStructVar();
assertEquals("var StructVar", "a struct var", s0.string());
S s1 = Testpkg.new_();
Testpkg.setStructVar(s1);
assertEquals("var StructVar", s1.string(), Testpkg.getStructVar().string());
AnI obj = new AnI();
obj.name = "this is an I";
Testpkg.setInterfaceVar(obj);
assertEquals("var InterfaceVar", obj.string(), Testpkg.getInterfaceVar().string());
}
public void testAssets() {
// Make sure that a valid context is set before reading assets
Seq.setContext(getInstrumentation().getContext());
String want = "Hello, Assets.\n";
String got = Testpkg.readAsset();
assertEquals("Asset read", want, got);
}
public void testAdd() {
long res = Testpkg.add(3, 4);
assertEquals("Unexpected arithmetic failure", 7, res);
}
public void testBool() {
assertTrue(Testpkg.negate(false));
assertFalse(Testpkg.negate(true));
}
public void testShortString() {
String want = "a short string";
String got = Testpkg.strDup(want);
assertEquals("Strings should match", want, got);
want = "";
got = Testpkg.strDup(want);
assertEquals("Strings should match (empty string)", want, got);
got = Testpkg.strDup(null);
assertEquals("Strings should match (null string)", want, got);
}
public void testLongString() {
StringBuilder b = new StringBuilder();
for (int i = 0; i < 128*1024; i++) {
b.append("0123456789");
}
String want = b.toString();
String got = Testpkg.strDup(want);
assertEquals("Strings should match", want, got);
}
public void testUnicode() {
String[] tests = new String[]{
"abcxyz09{}",
"Hello, 世界",
"\uffff\uD800\uDC00\uD800\uDC01\uD808\uDF45\uDBFF\uDFFF",
// From Go std lib tests in unicode/utf16/utf16_test.go
"\u0001\u0002\u0003\u0004",
"\uffff\ud800\udc00\ud800\udc01\ud808\udf45\udbff\udfff",
"\ud800a",
"\udfff"
};
String[] wants = new String[]{
"abcxyz09{}",
"Hello, 世界",
"\uffff\uD800\uDC00\uD800\uDC01\uD808\uDF45\uDBFF\uDFFF",
"\u0001\u0002\u0003\u0004",
"\uffff\ud800\udc00\ud800\udc01\ud808\udf45\udbff\udfff",
"\ufffda",
"\ufffd"
};
for (int i = 0; i < tests.length; i++) {
String got = Testpkg.strDup(tests[i]);
String want = wants[i];
assertEquals("Strings should match", want, got);
}
}
public void testNilErr() throws Exception {
Testpkg.err(null); // returns nil, no exception
}
public void testErr() {
String msg = "Go errors are dropped into the confusing space of exceptions";
try {
Testpkg.err(msg);
fail("expected non-nil error to be turned into an exception");
} catch (Exception e) {
assertEquals("messages should match", msg, e.getMessage());
}
}
public void testByteArray() {
for (int i = 0; i < 2048; i++) {
if (i == 0) {
byte[] got = Testpkg.bytesAppend(null, null);
assertEquals("Bytes(null+null) should match", (byte[])null, got);
got = Testpkg.bytesAppend(new byte[0], new byte[0]);
assertEquals("Bytes(empty+empty) should match", (byte[])null, got);
continue;
}
byte[] want = new byte[i];
new Random().nextBytes(want);
byte[] s1 = null;
byte[] s2 = null;
if (i > 0) {
s1 = Arrays.copyOfRange(want, 0, 1);
}
if (i > 1) {
s2 = Arrays.copyOfRange(want, 1, i);
}
byte[] got = Testpkg.bytesAppend(s1, s2);
MoreAsserts.assertEquals("Bytes(len="+i+") should match", want, got);
}
}
// Test for golang.org/issue/9486.
public void testByteArrayAfterString() {
byte[] bytes = new byte[1024];
for (int i=0; i < bytes.length; i++) {
bytes[i] = 8;
}
String stuff = "stuff";
byte[] got = Testpkg.appendToString(stuff, bytes);
try {
byte[] s = stuff.getBytes("UTF-8");
byte[] want = new byte[s.length + bytes.length];
System.arraycopy(s, 0, want, 0, s.length);
System.arraycopy(bytes, 0, want, s.length, bytes.length);
MoreAsserts.assertEquals("Bytes should match", want, got);
} catch (Exception e) {
fail("Cannot perform the test: " + e.toString());
}
}
public void testGoRefGC() {
S s = Testpkg.new_();
runGC();
long collected = Testpkg.numSCollected();
assertEquals("Only S should be pinned", 0, collected);
s = null;
runGC();
collected = Testpkg.numSCollected();
assertEquals("S should be collected", 1, collected);
}
private class AnI implements I {
public void e() throws Exception {
throw new Exception("my exception from E");
}
boolean calledF;
public void f() {
calledF = true;
}
public I i() {
return this;
}
public S s() {
return Testpkg.new_();
}
public String stoString(S s) {
return s.string();
}
public long v() {
return 1234;
}
public long ve() throws Exception {
throw new Exception("my exception from VE");
}
public String name;
public String string() {
return name;
}
}
// TODO(hyangah): add tests for methods that take parameters.
public void testInterfaceMethodReturnsError() {
final AnI obj = new AnI();
try {
Testpkg.callE(obj);
fail("Expecting exception but none was thrown.");
} catch (Exception e) {
assertEquals("Error messages should match", "my exception from E", e.getMessage());
}
}
public void testInterfaceMethodVoid() {
final AnI obj = new AnI();
Testpkg.callF(obj);
assertTrue("Want AnI.F to be called", obj.calledF);
}
public void testInterfaceMethodReturnsInterface() {
AnI obj = new AnI();
obj.name = "testing AnI.I";
I i = Testpkg.callI(obj);
assertEquals("Want AnI.I to return itself", i.string(), obj.string());
runGC();
i = Testpkg.callI(obj);
assertEquals("Want AnI.I to return itself", i.string(), obj.string());
}
public void testInterfaceMethodReturnsStructPointer() {
final AnI obj = new AnI();
for (int i = 0; i < 5; i++) {
S s = Testpkg.callS(obj);
runGC();
}
}
public void testInterfaceMethodTakesStructPointer() {
final AnI obj = new AnI();
S s = Testpkg.callS(obj);
String got = obj.stoString(s);
String want = s.string();
assertEquals("Want AnI.StoString(s) to call s's String", want, got);
}
public void testInterfaceMethodReturnsInt() {
final AnI obj = new AnI();
assertEquals("Values must match", 1234, Testpkg.callV(obj));
}
public void testInterfaceMethodReturnsIntOrError() {
final AnI obj = new AnI();
try {
long v = Testpkg.callVE(obj);
fail("Expecting exception but none was thrown and got value " + v);
} catch (Exception e) {
assertEquals("Error messages should match", "my exception from VE", e.getMessage());
}
}
boolean finalizedAnI;
private class AnI_Traced extends AnI {
@Override
public void finalize() throws Throwable {
finalizedAnI = true;
super.finalize();
}
}
public void testJavaRefKeep() {
finalizedAnI = false;
AnI obj = new AnI_Traced();
Testpkg.callF(obj);
assertTrue("want F to be called", obj.calledF);
Testpkg.callF(obj);
obj = null;
int attempts = 0;
while (true) {
runGC();
if (finalizedAnI)
break;
attempts++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (attempts >= 10)
fail("want obj not to be kept by Go; tried " + attempts + " garbage collections.");
}
finalizedAnI = false;
obj = new AnI_Traced();
Testpkg.keep(obj);
obj = null;
runGC();
assertFalse("want obj to be kept live by Go", finalizedAnI);
}
private int countI = 0;
private class CountI implements I {
public void f() { countI++; }
public void e() throws Exception {}
public I i() { return null; }
public S s() { return null; }
public String stoString(S s) { return ""; }
public long v() { return 0; }
public long ve() throws Exception { return 0; }
public String string() { return ""; }
}
public void testGoRefMapGrow() {
CountI obj = new CountI();
Testpkg.keep(obj);
// Push active references beyond base map size.
for (int i = 0; i < 24; i++) {
CountI o = new CountI();
Testpkg.callF(o);
if (i%3==0) {
Testpkg.keep(o);
}
}
runGC();
for (int i = 0; i < 128; i++) {
Testpkg.callF(new CountI());
}
Testpkg.callF(obj); // original object needs to work.
assertEquals(countI, 1+24+128);
}
private void runGC() {
System.gc();
System.runFinalization();
Testpkg.gc();
System.gc();
System.runFinalization();
}
public void testUnnamedParams() {
final String msg = "1234567";
assertEquals("want the length of \"1234567\" passed after unnamed params",
7, Testpkg.unnamedParams(10, 20, msg));
}
public void testPointerToStructAsField() {
Node a = Testpkg.newNode("A");
Node b = Testpkg.newNode("B");
a.setNext(b);
String got = a.string();
assertEquals("want Node A points to Node B", "A:B:<end>", got);
}
public void testImplementsInterface() {
Interface intf = Testpkg.newConcrete();
}
public void testErrorField() {
Node n = Testpkg.newNode("ErrTest");
Exception want = new Exception("an error message");
n.setErr(want);
Exception got = n.getErr();
assertTrue("want back the error we set", want == got);
String msg = Testpkg.errorMessage(want);
assertEquals("the error message must match", want.getMessage(), msg);
}
public void testErrorDup() {
Exception err = Testpkg.getGlobalErr();
assertTrue("the Go error instance must preserve its identity", Testpkg.isGlobalErr(err));
assertEquals("the Go error message must be preserved", "global err", err.getMessage());
}
//test if we have JNI local reference table overflow error
public void testLocalReferenceOverflow() {
Testpkg.callWithCallback(new GoCallback() {
@Override
public void varUpdate() {
//do nothing
}
});
}
public void testNullReferences() {
assertTrue(Testpkg.callWithNull(null, new NullTest() {
public NullTest null_() {
return null;
}
}));
assertEquals("Go nil interface is null", null, Testpkg.newNullInterface());
assertEquals("Go nil struct pointer is null", null, Testpkg.newNullStruct());
Issue20330 nullArger = new Issue20330();
assertTrue(nullArger.callWithNull(null));
}
public void testPassByteArray() {
Testpkg.passByteArray(new B() {
@Override public void b(byte[] b) {
byte[] want = new byte[]{1, 2, 3, 4};
MoreAsserts.assertEquals("bytes should match", want, b);
}
});
}
public void testReader() {
byte[] b = new byte[8];
try {
long n = Testpkg.readIntoByteArray(b);
assertEquals("wrote to the entire byte array", b.length, n);
byte[] want = new byte[b.length];
for (int i = 0; i < want.length; i++)
want[i] = (byte)i;
MoreAsserts.assertEquals("bytes should match", want, b);
} catch (Exception e) {
fail("Failed to write: " + e.toString());
}
}
public void testGoroutineCallback() {
Testpkg.goroutineCallback(new Receiver() {
@Override public void hello(String msg) {
}
});
}
public void testImportedPkg() {
Testpkg.callImportedI(new secondpkg.I() {
@Override public long f(long i) {
return i;
}
});
assertEquals("imported string should match", Secondpkg.HelloString, Secondpkg.hello());
secondpkg.I i = Testpkg.newImportedI();
secondpkg.S s = Testpkg.newImportedS();
i = Testpkg.getImportedVarI();
s = Testpkg.getImportedVarS();
assertEquals("numbers should match", 8, i.f(8));
assertEquals("numbers should match", 8, s.f(8));
Testpkg.setImportedVarI(i);
Testpkg.setImportedVarS(s);
ImportedFields fields = Testpkg.newImportedFields();
i = fields.getI();
s = fields.getS();
fields.setI(i);
fields.setS(s);
Testpkg.withImportedI(i);
Testpkg.withImportedS(s);
secondpkg.IF f = new AnI();
f = Testpkg.new_();
secondpkg.Ser ser = Testpkg.newSer();
}
public void testRoundtripEquality() {
I want = new AnI();
assertTrue("java object passed through Go should not be wrapped", want == Testpkg.iDup(want));
InterfaceDupper idup = new InterfaceDupper(){
@Override public Interface iDup(Interface i) {
return i;
}
};
assertTrue("Go interface passed through Java should not be wrapped", Testpkg.callIDupper(idup));
ConcreteDupper cdup = new ConcreteDupper(){
@Override public Concrete cDup(Concrete c) {
return c;
}
};
assertTrue("Go struct passed through Java should not be wrapped", Testpkg.callCDupper(cdup));
}
public void testConstructor() {
Interface i = new Concrete();
i.f();
S2 s = new S2(1, 2);
assertEquals("new S2().sum", 3.0, s.sum());
assertEquals("new S2().tryTwoStrings", "gostring", s.tryTwoStrings("go", "string"));
new S3();
S4 s4 = new S4(123);
assertEquals("Constructor argument", 123, s4.getI());
s4 = new S4(123.456);
assertEquals("Overloaded constructor argument", 123, s4.getI());
s4 = new S4(false);
assertEquals("Exceptional constructor", 0, s4.getI());
try {
s4 = new S4(true);
fail("Constructor error wasn't caught");
} catch (Exception e) {
}
}
public void testEmptyError() {
try {
Testpkg.emptyError();
fail("Empty error wasn't caught");
} catch (Exception e) {
}
EmptyErrorer empty = new EmptyErrorer() {
@Override public void emptyError() throws Exception {
throw new Exception("");
}
};
try {
Testpkg.callEmptyError(empty);
fail("Empty exception wasn't caught");
} catch (Exception e) {
}
}
public void testInitCaller() {
Testpkg.init();
InitCaller initer = Testpkg.newInitCaller();
initer.init();
}
public void testSIGPIPE() {
Testpkg.testSIGPIPE();
}
public void testTags() {
assertEquals("Constant from a tagged file", 42, Testpkg.TaggedConst);
}
public void testClassNameWithPackageName() {
testpkg.Testpkg_ o = new secondpkg.Secondpkg_();
secondpkg.Secondpkg_ o2 = Secondpkg.newSecondpkg();
o2.m();
o2.setV("hi");
assertEquals(o2.getV(), "hi");
Testpkg.clashingParameterFromOtherPackage(o2);
}
}

View file

@ -0,0 +1,16 @@
// Copyright 2016 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.
#include <jni.h>
#include "seq_android.h"
#include "_cgo_export.h"
JNIEXPORT void JNICALL
Java_go_Seq_setContext(JNIEnv* env, jclass clazz, jobject ctx) {
JavaVM* vm;
if ((*env)->GetJavaVM(env, &vm) != 0) {
LOG_FATAL("failed to get JavaVM");
}
setContext(vm, (*env)->NewGlobalRef(env, ctx));
}

View file

@ -0,0 +1,21 @@
// Copyright 2016 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 java
// #cgo LDFLAGS: -llog
//
//#include <jni.h>
import "C"
import (
"unsafe"
"golang.org/x/mobile/internal/mobileinit"
)
//export setContext
func setContext(vm *C.JavaVM, ctx C.jobject) {
mobileinit.SetCurrentContext(unsafe.Pointer(vm), uintptr(ctx))
}

8
bind/java/doc.go Normal file
View file

@ -0,0 +1,8 @@
// 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 java implements the Java language bindings.
//
// See the design document (http://golang.org/s/gobind).
package java

View file

@ -0,0 +1,401 @@
// Copyright 2016 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.
// C support functions for bindings. This file is copied into the
// generated gomobile_bind package and compiled along with the
// generated binding files.
#include <android/log.h>
#include <errno.h>
#include <jni.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "seq.h"
#include "_cgo_export.h"
#define NULL_REFNUM 41
// initClasses are only exported from Go if reverse bindings are used.
// If they're not, weakly define a no-op function.
__attribute__((weak)) void initClasses(void) { }
static JavaVM *jvm;
// jnienvs holds the per-thread JNIEnv* for Go threads where we called AttachCurrentThread.
// A pthread key destructor is supplied to call DetachCurrentThread on exit. This trick is
// documented in http://developer.android.com/training/articles/perf-jni.html under "Threads".
static pthread_key_t jnienvs;
static jclass seq_class;
static jmethodID seq_getRef;
static jmethodID seq_decRef;
static jmethodID seq_incRef;
static jmethodID seq_incGoObjectRef;
static jmethodID seq_incRefnum;
static jfieldID ref_objField;
static jclass throwable_class;
// env_destructor is registered as a thread data key destructor to
// clean up a Go thread that is attached to the JVM.
static void env_destructor(void *env) {
if ((*jvm)->DetachCurrentThread(jvm) != JNI_OK) {
LOG_INFO("failed to detach current thread");
}
}
static JNIEnv *go_seq_get_thread_env(void) {
JNIEnv *env;
jint ret = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6);
if (ret != JNI_OK) {
if (ret != JNI_EDETACHED) {
LOG_FATAL("failed to get thread env");
}
if ((*jvm)->AttachCurrentThread(jvm, &env, NULL) != JNI_OK) {
LOG_FATAL("failed to attach current thread");
}
pthread_setspecific(jnienvs, env);
}
return env;
}
void go_seq_maybe_throw_exception(JNIEnv *env, jobject exc) {
if (exc != NULL) {
(*env)->Throw(env, exc);
}
}
jobject go_seq_get_exception(JNIEnv *env) {
jthrowable exc = (*env)->ExceptionOccurred(env);
if (!exc) {
return NULL;
}
(*env)->ExceptionClear(env);
return exc;
}
jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy) {
if (s.ptr == NULL) {
return NULL;
}
jbyteArray res = (*env)->NewByteArray(env, s.len);
if (res == NULL) {
LOG_FATAL("NewByteArray failed");
}
(*env)->SetByteArrayRegion(env, res, 0, s.len, s.ptr);
if (copy) {
free(s.ptr);
}
return res;
}
#define surr1 0xd800
#define surr2 0xdc00
#define surr3 0xe000
// Unicode replacement character
#define replacementChar 0xFFFD
#define rune1Max ((1<<7) - 1)
#define rune2Max ((1<<11) - 1)
#define rune3Max ((1<<16) - 1)
// Maximum valid Unicode code point.
#define MaxRune 0x0010FFFF
#define surrogateMin 0xD800
#define surrogateMax 0xDFFF
// 0011 1111
#define maskx 0x3F
// 1000 0000
#define tx 0x80
// 1100 0000
#define t2 0xC0
// 1110 0000
#define t3 0xE0
// 1111 0000
#define t4 0xF0
// encode_rune writes into p (which must be large enough) the UTF-8 encoding
// of the rune. It returns the number of bytes written.
static int encode_rune(uint8_t *p, uint32_t r) {
if (r <= rune1Max) {
p[0] = (uint8_t)r;
return 1;
} else if (r <= rune2Max) {
p[0] = t2 | (uint8_t)(r>>6);
p[1] = tx | (((uint8_t)(r))&maskx);
return 2;
} else {
if (r > MaxRune || (surrogateMin <= r && r <= surrogateMax)) {
r = replacementChar;
}
if (r <= rune3Max) {
p[0] = t3 | (uint8_t)(r>>12);
p[1] = tx | (((uint8_t)(r>>6))&maskx);
p[2] = tx | (((uint8_t)(r))&maskx);
return 3;
} else {
p[0] = t4 | (uint8_t)(r>>18);
p[1] = tx | (((uint8_t)(r>>12))&maskx);
p[2] = tx | (((uint8_t)(r>>6))&maskx);
p[3] = tx | (((uint8_t)(r))&maskx);
return 4;
}
}
}
// utf16_decode decodes an array of UTF16 characters to a UTF-8 encoded
// nstring copy. The support functions and utf16_decode itself are heavily
// based on the unicode/utf8 and unicode/utf16 Go packages.
static nstring utf16_decode(jchar *chars, jsize len) {
jsize worstCaseLen = 4*len;
uint8_t *buf = malloc(worstCaseLen);
if (buf == NULL) {
LOG_FATAL("utf16Decode: malloc failed");
}
jsize nsrc = 0;
jsize ndst = 0;
while (nsrc < len) {
uint32_t r = chars[nsrc];
nsrc++;
if (surr1 <= r && r < surr2 && nsrc < len) {
uint32_t r2 = chars[nsrc];
if (surr2 <= r2 && r2 < surr3) {
nsrc++;
r = (((r-surr1)<<10) | (r2 - surr2)) + 0x10000;
}
}
if (ndst + 4 > worstCaseLen) {
LOG_FATAL("utf16Decode: buffer overflow");
}
ndst += encode_rune(buf + ndst, r);
}
struct nstring res = {.chars = buf, .len = ndst};
return res;
}
nstring go_seq_from_java_string(JNIEnv *env, jstring str) {
struct nstring res = {NULL, 0};
if (str == NULL) {
return res;
}
jsize nchars = (*env)->GetStringLength(env, str);
if (nchars == 0) {
return res;
}
jchar *chars = (jchar *)(*env)->GetStringChars(env, str, NULL);
if (chars == NULL) {
LOG_FATAL("GetStringChars failed");
}
nstring nstr = utf16_decode(chars, nchars);
(*env)->ReleaseStringChars(env, str, chars);
return nstr;
}
nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray arr, int copy) {
struct nbyteslice res = {NULL, 0};
if (arr == NULL) {
return res;
}
jsize len = (*env)->GetArrayLength(env, arr);
if (len == 0) {
return res;
}
jbyte *ptr = (*env)->GetByteArrayElements(env, arr, NULL);
if (ptr == NULL) {
LOG_FATAL("GetByteArrayElements failed");
}
if (copy) {
void *ptr_copy = (void *)malloc(len);
if (ptr_copy == NULL) {
LOG_FATAL("malloc failed");
}
memcpy(ptr_copy, ptr, len);
(*env)->ReleaseByteArrayElements(env, arr, ptr, JNI_ABORT);
ptr = (jbyte *)ptr_copy;
}
res.ptr = ptr;
res.len = len;
return res;
}
int32_t go_seq_to_refnum_go(JNIEnv *env, jobject o) {
if (o == NULL) {
return NULL_REFNUM;
}
return (int32_t)(*env)->CallStaticIntMethod(env, seq_class, seq_incGoObjectRef, o);
}
int32_t go_seq_to_refnum(JNIEnv *env, jobject o) {
if (o == NULL) {
return NULL_REFNUM;
}
return (int32_t)(*env)->CallStaticIntMethod(env, seq_class, seq_incRef, o);
}
int32_t go_seq_unwrap(jint refnum) {
JNIEnv *env = go_seq_push_local_frame(0);
jobject jobj = go_seq_from_refnum(env, refnum, NULL, NULL);
int32_t goref = go_seq_to_refnum_go(env, jobj);
go_seq_pop_local_frame(env);
return goref;
}
jobject go_seq_from_refnum(JNIEnv *env, int32_t refnum, jclass proxy_class, jmethodID proxy_cons) {
if (refnum == NULL_REFNUM) {
return NULL;
}
if (refnum < 0) { // Go object
// return new <Proxy>(refnum)
return (*env)->NewObject(env, proxy_class, proxy_cons, refnum);
}
// Seq.Ref ref = Seq.getRef(refnum)
jobject ref = (*env)->CallStaticObjectMethod(env, seq_class, seq_getRef, (jint)refnum);
if (ref == NULL) {
LOG_FATAL("Unknown reference: %d", refnum);
}
// Go incremented the reference count just before passing the refnum. Decrement it here.
(*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)refnum);
// return ref.obj
return (*env)->GetObjectField(env, ref, ref_objField);
}
// go_seq_to_java_string converts a nstring to a jstring.
jstring go_seq_to_java_string(JNIEnv *env, nstring str) {
jstring s = (*env)->NewString(env, str.chars, str.len/2);
if (str.chars != NULL) {
free(str.chars);
}
return s;
}
// go_seq_push_local_frame retrieves or creates the JNIEnv* for the current thread
// and pushes a JNI reference frame. Must be matched with call to go_seq_pop_local_frame.
JNIEnv *go_seq_push_local_frame(jint nargs) {
JNIEnv *env = go_seq_get_thread_env();
// Given the number of function arguments, compute a conservative bound for the minimal frame size.
// Assume two slots for each per parameter (Seq.Ref and Seq.Object) and add extra
// extra space for the receiver, the return value, and exception (if any).
jint frameSize = 2*nargs + 10;
if ((*env)->PushLocalFrame(env, frameSize) < 0) {
LOG_FATAL("PushLocalFrame failed");
}
return env;
}
// Pop the current local frame, freeing all JNI local references in it
void go_seq_pop_local_frame(JNIEnv *env) {
(*env)->PopLocalFrame(env, NULL);
}
void go_seq_inc_ref(int32_t ref) {
JNIEnv *env = go_seq_get_thread_env();
(*env)->CallStaticVoidMethod(env, seq_class, seq_incRefnum, (jint)ref);
}
void go_seq_dec_ref(int32_t ref) {
JNIEnv *env = go_seq_get_thread_env();
(*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)ref);
}
JNIEXPORT void JNICALL
Java_go_Seq_init(JNIEnv *env, jclass clazz) {
if ((*env)->GetJavaVM(env, &jvm) != 0) {
LOG_FATAL("failed to get JVM");
}
if (pthread_key_create(&jnienvs, env_destructor) != 0) {
LOG_FATAL("failed to initialize jnienvs thread local storage");
}
seq_class = (*env)->NewGlobalRef(env, clazz);
seq_getRef = (*env)->GetStaticMethodID(env, seq_class, "getRef", "(I)Lgo/Seq$Ref;");
if (seq_getRef == NULL) {
LOG_FATAL("failed to find method Seq.getRef");
}
seq_decRef = (*env)->GetStaticMethodID(env, seq_class, "decRef", "(I)V");
if (seq_decRef == NULL) {
LOG_FATAL("failed to find method Seq.decRef");
}
seq_incRefnum = (*env)->GetStaticMethodID(env, seq_class, "incRefnum", "(I)V");
if (seq_incRefnum == NULL) {
LOG_FATAL("failed to find method Seq.incRefnum");
}
seq_incRef = (*env)->GetStaticMethodID(env, seq_class, "incRef", "(Ljava/lang/Object;)I");
if (seq_incRef == NULL) {
LOG_FATAL("failed to find method Seq.incRef");
}
seq_incGoObjectRef = (*env)->GetStaticMethodID(env, seq_class, "incGoObjectRef", "(Lgo/Seq$GoObject;)I");
if (seq_incGoObjectRef == NULL) {
LOG_FATAL("failed to find method Seq.incGoObjectRef");
}
jclass ref_class = (*env)->FindClass(env, "go/Seq$Ref");
if (ref_class == NULL) {
LOG_FATAL("failed to find the Seq.Ref class");
}
ref_objField = (*env)->GetFieldID(env, ref_class, "obj", "Ljava/lang/Object;");
if (ref_objField == NULL) {
LOG_FATAL("failed to find the Seq.Ref.obj field");
}
initClasses();
}
JNIEXPORT void JNICALL
Java_go_Seq_destroyRef(JNIEnv *env, jclass clazz, jint refnum) {
DestroyRef(refnum);
}
JNIEXPORT void JNICALL
Java_go_Seq_incGoRef(JNIEnv *env, jclass clazz, jint refnum, jobject ref) {
IncGoRef(refnum);
}
jclass go_seq_find_class(const char *name) {
JNIEnv *env = go_seq_push_local_frame(0);
jclass clazz = (*env)->FindClass(env, name);
if (clazz == NULL) {
(*env)->ExceptionClear(env);
} else {
clazz = (*env)->NewGlobalRef(env, clazz);
}
go_seq_pop_local_frame(env);
return clazz;
}
jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, const char *sig) {
JNIEnv *env = go_seq_push_local_frame(0);
jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
if (m == NULL) {
(*env)->ExceptionClear(env);
}
go_seq_pop_local_frame(env);
return m;
}
jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig) {
JNIEnv *env = go_seq_push_local_frame(0);
jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
if (m == NULL) {
(*env)->ExceptionClear(env);
}
go_seq_pop_local_frame(env);
return m;
}
void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr) {
if (ptr != NULL) {
(*env)->ReleaseByteArrayElements(env, arr, ptr, 0);
}
}
int go_seq_isinstanceof(jint refnum, jclass clazz) {
JNIEnv *env = go_seq_push_local_frame(0);
jobject obj = go_seq_from_refnum(env, refnum, NULL, NULL);
jboolean isinst = (*env)->IsInstanceOf(env, obj, clazz);
go_seq_pop_local_frame(env);
return isinst;
}

View file

@ -0,0 +1,98 @@
// Copyright 2016 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 main
// Go support functions for bindings. This file is copied into the
// generated main package and compiled along with the generated binding
// files.
//#cgo CFLAGS: -Werror
//#cgo LDFLAGS: -llog
//#include <jni.h>
//#include <stdint.h>
//#include <stdlib.h>
//#include "seq_android.h"
import "C"
import (
"unsafe"
"golang.org/x/mobile/bind/seq"
)
// DestroyRef is called by Java to inform Go it is done with a reference.
//export DestroyRef
func DestroyRef(refnum C.int32_t) {
seq.Delete(int32(refnum))
}
// encodeString returns a copy of a Go string as a UTF16 encoded nstring.
// The returned data is freed in go_seq_to_java_string.
//
// encodeString uses UTF16 as the intermediate format. Note that UTF8 is an obvious
// alternative, but JNI only supports a C-safe variant of UTF8 (modified UTF8).
func encodeString(s string) C.nstring {
n := C.int(len(s))
if n == 0 {
return C.nstring{}
}
// Allocate enough for the worst case estimate, every character is a surrogate pair
worstCaseLen := 4 * len(s)
utf16buf := C.malloc(C.size_t(worstCaseLen))
if utf16buf == nil {
panic("encodeString: malloc failed")
}
chars := (*[1<<30 - 1]uint16)(unsafe.Pointer(utf16buf))[:worstCaseLen/2 : worstCaseLen/2]
nchars := seq.UTF16Encode(s, chars)
return C.nstring{chars: unsafe.Pointer(utf16buf), len: C.jsize(nchars*2)}
}
// decodeString decodes a UTF8 encoded nstring to a Go string. The data
// in str is freed after use.
func decodeString(str C.nstring) string {
if str.chars == nil {
return ""
}
chars := (*[1<<31 - 1]byte)(str.chars)[:str.len]
s := string(chars)
C.free(str.chars)
return s
}
// fromSlice converts a slice to a jbyteArray cast as a nbyteslice. If cpy
// is set, the returned slice is a copy to be free by go_seq_to_java_bytearray.
func fromSlice(s []byte, cpy bool) C.nbyteslice {
if s == nil || len(s) == 0 {
return C.nbyteslice{}
}
var ptr *C.jbyte
n := C.jsize(len(s))
if cpy {
ptr = (*C.jbyte)(C.malloc(C.size_t(n)))
if ptr == nil {
panic("fromSlice: malloc failed")
}
copy((*[1<<31 - 1]byte)(unsafe.Pointer(ptr))[:n], s)
} else {
ptr = (*C.jbyte)(unsafe.Pointer(&s[0]))
}
return C.nbyteslice{ptr: unsafe.Pointer(ptr), len: n}
}
// toSlice takes a nbyteslice (jbyteArray) and returns a byte slice
// with the data. If cpy is set, the slice contains a copy of the data and is
// freed.
func toSlice(s C.nbyteslice, cpy bool) []byte {
if s.ptr == nil || s.len == 0 {
return nil
}
var b []byte
if cpy {
b = C.GoBytes(s.ptr, C.int(s.len))
C.free(s.ptr)
} else {
b = (*[1<<31 - 1]byte)(unsafe.Pointer(s.ptr))[:s.len:s.len]
}
return b
}

67
bind/java/seq_android.h Normal file
View file

@ -0,0 +1,67 @@
// Copyright 2016 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.
#ifndef __GO_SEQ_ANDROID_HDR__
#define __GO_SEQ_ANDROID_HDR__
#include <stdint.h>
#include <android/log.h>
// For abort()
#include <stdlib.h>
#include <jni.h>
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "go/Seq", __VA_ARGS__)
#define LOG_FATAL(...) \
{ \
__android_log_print(ANDROID_LOG_FATAL, "go/Seq", __VA_ARGS__); \
abort(); \
}
// Platform specific types
typedef struct nstring {
// UTF16 or UTF8 Encoded string. When converting from Java string to Go
// string, UTF16. When converting from Go to Java, UTF8.
void *chars;
// length in bytes, regardless of encoding
jsize len;
} nstring;
typedef struct nbyteslice {
void *ptr;
jsize len;
} nbyteslice;
typedef jlong nint;
extern void go_seq_dec_ref(int32_t ref);
extern void go_seq_inc_ref(int32_t ref);
// go_seq_unwrap takes a reference number to a Java wrapper and returns
// a reference number to its wrapped Go object.
extern int32_t go_seq_unwrap(jint refnum);
extern int32_t go_seq_to_refnum(JNIEnv *env, jobject o);
extern int32_t go_seq_to_refnum_go(JNIEnv *env, jobject o);
extern jobject go_seq_from_refnum(JNIEnv *env, int32_t refnum, jclass proxy_class, jmethodID proxy_cons);
extern void go_seq_maybe_throw_exception(JNIEnv *env, jobject msg);
// go_seq_get_exception returns any pending exception and clears the exception status.
extern jobject go_seq_get_exception(JNIEnv *env);
extern jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy);
extern nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray s, int copy);
extern void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr);
extern jstring go_seq_to_java_string(JNIEnv *env, nstring str);
extern nstring go_seq_from_java_string(JNIEnv *env, jstring s);
// push_local_frame retrieves or creates the JNIEnv* for the current thread
// and pushes a JNI reference frame. Must be matched with call to pop_local_frame.
extern JNIEnv *go_seq_push_local_frame(jint cap);
// Pop the current local frame, releasing all JNI local references in it
extern void go_seq_pop_local_frame(JNIEnv *env);
// Return a global reference to the given class. Return NULL and clear exception if not found.
extern jclass go_seq_find_class(const char *name);
extern jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, const char *sig);
extern jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig);
extern int go_seq_isinstanceof(jint refnum, jclass clazz);
#endif // __GO_SEQ_ANDROID_HDR__

256
bind/java/seq_test.go Normal file
View file

@ -0,0 +1,256 @@
// 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 java
import (
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"golang.org/x/mobile/internal/importers/java"
"golang.org/x/mobile/internal/sdkpath"
)
var gomobileBin string
func TestMain(m *testing.M) {
os.Exit(testMain(m))
}
func testMain(m *testing.M) int {
// Build gomobile and gobind and put them into PATH.
binDir, err := os.MkdirTemp("", "bind-java-test-")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(binDir)
exe := ""
if runtime.GOOS == "windows" {
exe = ".exe"
}
if runtime.GOOS != "android" {
gocmd := filepath.Join(runtime.GOROOT(), "bin", "go")
gomobileBin = filepath.Join(binDir, "gomobile"+exe)
gobindBin := filepath.Join(binDir, "gobind"+exe)
if out, err := exec.Command(gocmd, "build", "-o", gomobileBin, "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil {
log.Fatalf("gomobile build failed: %v: %s", err, out)
}
if out, err := exec.Command(gocmd, "build", "-o", gobindBin, "golang.org/x/mobile/cmd/gobind").CombinedOutput(); err != nil {
log.Fatalf("gobind build failed: %v: %s", err, out)
}
path := binDir
if oldPath := os.Getenv("PATH"); oldPath != "" {
path += string(filepath.ListSeparator) + oldPath
}
os.Setenv("PATH", path)
}
return m.Run()
}
func TestClasses(t *testing.T) {
if !java.IsAvailable() {
t.Skipf("java importer is not available")
}
runTest(t, []string{
"golang.org/x/mobile/bind/testdata/testpkg/javapkg",
}, "", "ClassesTest")
}
func TestCustomPkg(t *testing.T) {
runTest(t, []string{
"golang.org/x/mobile/bind/testdata/testpkg",
}, "org.golang.custompkg", "CustomPkgTest")
}
func TestJavaSeqTest(t *testing.T) {
runTest(t, []string{
"golang.org/x/mobile/bind/testdata/testpkg",
"golang.org/x/mobile/bind/testdata/testpkg/secondpkg",
"golang.org/x/mobile/bind/testdata/testpkg/simplepkg",
}, "", "SeqTest")
}
// TestJavaSeqBench runs java test SeqBench.java, with the same
// environment requirements as TestJavaSeqTest.
//
// The benchmarks runs on the phone, so the benchmarkpkg implements
// rudimentary timing logic and outputs benchcmp compatible runtimes
// to logcat. Use
//
// adb logcat -v raw GoLog:* *:S
//
// while running the benchmark to see the results.
func TestJavaSeqBench(t *testing.T) {
if testing.Short() {
t.Skip("skipping benchmark in short mode.")
}
runTest(t, []string{"golang.org/x/mobile/bind/testdata/benchmark"}, "", "SeqBench")
}
// runTest runs the Android java test class specified with javaCls. If javaPkg is
// set, it is passed with the -javapkg flag to gomobile. The pkgNames lists the Go
// packages to bind for the test.
// This requires the gradle command to be in PATH and the Android SDK to be
// installed.
func runTest(t *testing.T, pkgNames []string, javaPkg, javaCls string) {
if gomobileBin == "" {
t.Skipf("no gomobile on %s", runtime.GOOS)
}
gradle, err := exec.LookPath("gradle")
if err != nil {
t.Skip("command gradle not found, skipping")
}
if _, err := sdkpath.AndroidHome(); err != nil {
t.Skip("Android SDK not found, skipping")
}
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("failed pwd: %v", err)
}
tmpdir, err := os.MkdirTemp("", "bind-java-seq-test-")
if err != nil {
t.Fatalf("failed to prepare temp dir: %v", err)
}
defer os.RemoveAll(tmpdir)
t.Logf("tmpdir = %s", tmpdir)
if err := os.Chdir(tmpdir); err != nil {
t.Fatalf("failed chdir: %v", err)
}
defer os.Chdir(cwd)
for _, d := range []string{"src/main", "src/androidTest/java/go", "libs", "src/main/res/values"} {
err = os.MkdirAll(filepath.Join(tmpdir, d), 0700)
if err != nil {
t.Fatal(err)
}
}
args := []string{"bind", "-tags", "aaa bbb", "-o", "pkg.aar"}
if javaPkg != "" {
args = append(args, "-javapkg", javaPkg)
}
args = append(args, pkgNames...)
cmd := exec.Command(gomobileBin, args...)
// Reverse binding doesn't work with Go module since imports starting with Java or ObjC are not valid FQDNs.
// Disable Go module explicitly until this problem is solved. See golang/go#27234.
cmd.Env = append(os.Environ(), "GO111MODULE=off")
buf, err := cmd.CombinedOutput()
if err != nil {
t.Logf("%s", buf)
t.Fatalf("failed to run gomobile bind: %v", err)
}
fname := filepath.Join(tmpdir, "libs", "pkg.aar")
err = cp(fname, filepath.Join(tmpdir, "pkg.aar"))
if err != nil {
t.Fatalf("failed to copy pkg.aar: %v", err)
}
fname = filepath.Join(tmpdir, "src/androidTest/java/go/"+javaCls+".java")
err = cp(fname, filepath.Join(cwd, javaCls+".java"))
if err != nil {
t.Fatalf("failed to copy SeqTest.java: %v", err)
}
fname = filepath.Join(tmpdir, "src/main/AndroidManifest.xml")
err = os.WriteFile(fname, []byte(androidmanifest), 0700)
if err != nil {
t.Fatalf("failed to write android manifest file: %v", err)
}
// Add a dummy string resource to avoid errors from the Android build system.
fname = filepath.Join(tmpdir, "src/main/res/values/strings.xml")
err = os.WriteFile(fname, []byte(stringsxml), 0700)
if err != nil {
t.Fatalf("failed to write strings.xml file: %v", err)
}
fname = filepath.Join(tmpdir, "build.gradle")
err = os.WriteFile(fname, []byte(buildgradle), 0700)
if err != nil {
t.Fatalf("failed to write build.gradle file: %v", err)
}
if buf, err := run(gradle + " connectedAndroidTest"); err != nil {
t.Logf("%s", buf)
t.Errorf("failed to run gradle test: %v", err)
}
}
func run(cmd string) ([]byte, error) {
c := strings.Split(cmd, " ")
return exec.Command(c[0], c[1:]...).CombinedOutput()
}
func cp(dst, src string) error {
r, err := os.Open(src)
if err != nil {
return fmt.Errorf("failed to read source: %v", err)
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return fmt.Errorf("failed to open destination: %v", err)
}
_, err = io.Copy(w, r)
cerr := w.Close()
if err != nil {
return err
}
return cerr
}
const androidmanifest = `<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.BindTest"
android:versionCode="1"
android:versionName="1.0">
</manifest>`
const buildgradle = `buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 'android-19'
defaultConfig { minSdkVersion 16 }
}
repositories {
flatDir { dirs 'libs' }
}
dependencies {
implementation(name: "pkg", ext: "aar")
}
`
const stringsxml = `<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="dummy">dummy</string>
</resources>`

197
bind/objc/SeqBench.m Normal file
View file

@ -0,0 +1,197 @@
// 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.
//go:build ignore
// +build ignore
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#import "benchmark/Benchmark.h"
@interface AnI : NSObject <BenchmarkI> {
}
@end
@implementation AnI
- (void)f {
}
@end
@interface Benchmarks : NSObject <BenchmarkBenchmarks> {
}
@end
@implementation Benchmarks
- (void)manyargs:(long)p0 p1:(long)p1 p2:(long)p2 p3:(long)p3 p4:(long)p4 p5:(long)p5 p6:(long)p6 p7:(long)p7 p8:(long)p8 p9:(long)p9 {
}
- (id<BenchmarkI>)newI {
return [[AnI alloc] init];
}
- (void)noargs {
}
- (void)onearg:(long)p0 {
}
- (long)oneret {
return 0;
}
- (void)ref:(id<BenchmarkI>)p0 {
}
- (void)slice:(NSData*)p0 {
}
- (void)string:(NSString*)p0 {
}
- (NSString*)stringRetLong {
return BenchmarkLongString;
}
- (NSString*)stringRetShort {
return BenchmarkShortString;
}
- (void (^)(void))lookupBenchmark:(NSString *)name {
if ([name isEqualToString:@"Empty"]) {
return ^() {
};
} else if ([name isEqualToString:@"Noargs"]) {
return ^() {
BenchmarkNoargs();
};
} else if ([name isEqualToString:@"Onearg"]) {
return ^() {
BenchmarkOnearg(0);
};
} else if ([name isEqualToString:@"Manyargs"]) {
return ^() {
BenchmarkManyargs(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
};
} else if ([name isEqualToString:@"Oneret"]) {
return ^() {
BenchmarkOneret();
};
} else if ([name isEqualToString:@"Refforeign"]) {
id<BenchmarkI> objcRef = [[AnI alloc] init];
return ^() {
BenchmarkRef(objcRef);
};
} else if ([name isEqualToString:@"Refgo"]) {
id<BenchmarkI> goRef = BenchmarkNewI();
return ^() {
BenchmarkRef(goRef);
};
} else if ([name isEqualToString:@"StringShort"]) {
return ^() {
BenchmarkString(BenchmarkShortString);
};
} else if ([name isEqualToString:@"StringLong"]) {
return ^() {
BenchmarkString(BenchmarkLongString);
};
} else if ([name isEqualToString:@"StringShortUnicode"]) {
return ^() {
BenchmarkString(BenchmarkShortStringUnicode);
};
} else if ([name isEqualToString:@"StringLongUnicode"]) {
return ^() {
BenchmarkString(BenchmarkLongStringUnicode);
};
} else if ([name isEqualToString:@"StringRetShort"]) {
return ^() {
BenchmarkStringRetShort();
};
} else if ([name isEqualToString:@"StringRetLong"]) {
return ^() {
BenchmarkStringRetLong();
};
} else if ([name isEqualToString:@"SliceShort"]) {
NSData *s = [Benchmark shortSlice];
return ^() {
BenchmarkSlice(s);
};
} else if ([name isEqualToString:@"SliceLong"]) {
NSData *s = [Benchmark longSlice];
return ^() {
BenchmarkSlice(s);
};
} else {
return nil;
}
}
- (void)run:(NSString*)name n:(long)n {
void (^bench)(void) = [self lookupBenchmark:name];
if (bench == nil) {
NSLog(@"Error: no such benchmark: %@", name);
return;
}
for (int i = 0; i < n; i++) {
bench();
}
}
- (void)runDirect:(NSString*)name n:(long)n {
void (^bench)(void) = [self lookupBenchmark:name];
if (bench == nil) {
NSLog(@"Error: no such benchmark: %@", name);
return;
}
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < n; i++) {
bench();
}
});
}
@end
@interface benchmarks : XCTestCase
@end
@implementation benchmarks
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
self.continueAfterFailure = NO;
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
[[[XCUIApplication alloc] init] launch];
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testBenchmark {
// Long running unit tests seem to hang. Use an XCTestExpectation and run the Go
// benchmark suite on a GCD thread.
XCTestExpectation *expectation =
[self expectationWithDescription:@"Benchmark"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
Benchmarks *b = [[Benchmarks alloc] init];
BenchmarkRunBenchmarks(b);
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:5*60.0 handler:^(NSError *error) {
if (error) {
NSLog(@"Timeout Error: %@", error);
}
}];
}
@end

22
bind/objc/SeqCustom.m Normal file
View file

@ -0,0 +1,22 @@
// 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.
//go:build ignore
// +build ignore
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
@import Testpkg;
@interface tests : XCTestCase
@end
@implementation tests
- (void)testBasics {
CustomTestpkgHi();
}
@end

481
bind/objc/SeqTest.m Normal file
View file

@ -0,0 +1,481 @@
// 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.
//go:build ignore
// +build ignore
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#import "testpkg/Testpkg.h"
// Objective-C implementation of testpkg.I2.
@interface Number : NSObject <TestpkgI2> {
}
@property int32_t value;
// TODO(hyangah): error:error is not good.
- (BOOL)error:(BOOL)e error:(NSError **)error;
- (int64_t)times:(int32_t)v;
@end
// numI is incremented when the first numI objective-C implementation is
// deallocated.
static int numI = 0;
@implementation Number {
}
@synthesize value;
- (NSString *)stringError:(NSString *)s
error:(NSError **)error {
if ([s isEqualToString:@"number"]) {
return @"OK";
}
*error = [NSError errorWithDomain:@"SeqTest" code:1 userInfo:@{NSLocalizedDescriptionKey: @"NumberError"}];
return NULL;
}
- (BOOL)error:(BOOL)triggerError error:(NSError **)error {
if (!triggerError) {
return YES;
}
if (error != NULL) {
*error = [NSError errorWithDomain:@"SeqTest" code:1 userInfo:NULL];
}
return NO;
}
- (int64_t)times:(int32_t)v {
return v * value;
}
- (void)dealloc {
if (self.value == 0) {
numI++;
}
}
@end
// Objective-C implementation of testpkg.NullTest.
@interface NullTest : NSObject <TestpkgNullTest> {
}
- (TestpkgNullTest *)null;
@end
@implementation NullTest {
}
- (TestpkgNullTest *)null {
return nil;
}
@end
// Objective-C implementation of testpkg.InterfaceDupper.
@interface IDup : NSObject <TestpkgInterfaceDupper> {
}
@end
@implementation IDup {
}
- (id<TestpkgInterface>)iDup:(id<TestpkgInterface>)i {
return i;
}
@end
// Objective-C implementation of testpkg.ConcreteDupper.
@interface CDup : NSObject <TestpkgConcreteDupper> {
}
@end
@implementation CDup {
}
- (TestpkgConcrete *)cDup:(TestpkgConcrete *)c {
return c;
}
@end
// Objective-C implementation of testpkg.EmptyThrower.
@interface EmptyErrorer: NSObject <TestpkgEmptyErrorer> {
}
@end
@implementation EmptyErrorer {
}
- (BOOL)emptyError:(NSError **)error {
*error = [NSError errorWithDomain:@"SeqTest" code:1 userInfo:NULL];
return NO;
}
@end
@interface tests : XCTestCase
@end
@implementation tests
- (void)setUp {
[super setUp];
}
- (void)tearDown {
[super tearDown];
}
- (void)testBasics {
TestpkgHi();
TestpkgInt(42);
}
- (void)testAdd {
int64_t sum = TestpkgAdd(31, 21);
XCTAssertEqual(sum, 52, @"TestpkgSum(31, 21) = %lld, want 52\n", sum);
}
- (void)testHello:(NSString *)input {
NSString *got = TestpkgAppendHello(input);
NSString *want = [NSString stringWithFormat:@"Hello, %@!", input];
XCTAssertEqualObjects(got, want, @"want %@\nTestpkgHello(%@)= %@", want, input, got);
}
- (void)testHellos {
[self testHello:@"세계"]; // korean, utf-8, world.
unichar t[] = {
0xD83D, 0xDCA9,
}; // utf-16, pile of poo.
[self testHello:[NSString stringWithCharacters:t length:2]];
}
- (void)testString {
NSString *input = @"";
NSString *got = TestpkgStrDup(input);
XCTAssertEqualObjects(got, input, @"want %@\nTestpkgEcho(%@)= %@", input, input, got);
input = @"FOO";
got = TestpkgStrDup(input);
XCTAssertEqualObjects(got, input, @"want %@\nTestpkgEcho(%@)= %@", input, input, got);
}
- (void)testStruct {
TestpkgS2 *s = TestpkgNewS2(10.0, 100.0);
XCTAssertNotNil(s, @"TestpkgNewS2 returned NULL");
double x = [s x];
double y = [s y];
double sum = [s sum];
XCTAssertTrue(x == 10.0 && y == 100.0 && sum == 110.0,
@"TestpkgS2(10.0, 100.0).X=%f Y=%f SUM=%f; want 10, 100, 110", x, y, sum);
double sum2 = TestpkgCallSSum(s);
XCTAssertEqual(sum, sum2, @"TestpkgCallSSum(s)=%f; want %f as returned by s.Sum", sum2, sum);
[s setX:7];
[s setY:70];
x = [s x];
y = [s y];
sum = [s sum];
XCTAssertTrue(x == 7 && y == 70 && sum == 77,
@"TestpkgS2(7, 70).X=%f Y=%f SUM=%f; want 7, 70, 77", x, y, sum);
NSString *first = @"trytwotested";
NSString *second = @"test";
NSString *got = [s tryTwoStrings:first second:second];
NSString *want = [first stringByAppendingString:second];
XCTAssertEqualObjects(got, want, @"TestpkgS_TryTwoStrings(%@, %@)= %@; want %@", first, second, got, want);
}
- (void)testCollectS {
@autoreleasepool {
[self testStruct];
}
TestpkgGC();
long numS = TestpkgCollectS2(
1, 10); // within 10 seconds, collect the S used in testStruct.
XCTAssertEqual(numS, 1, @"%ld S objects were collected; S used in testStruct is supposed to "
@"be collected.",
numS);
}
- (void)testBytesAppend {
NSString *a = @"Foo";
NSString *b = @"Bar";
NSData *data_a = [a dataUsingEncoding:NSUTF8StringEncoding];
NSData *data_b = [b dataUsingEncoding:NSUTF8StringEncoding];
NSData *gotData = TestpkgBytesAppend(data_a, data_b);
NSString *got = [[NSString alloc] initWithData:gotData encoding:NSUTF8StringEncoding];
NSString *want = [a stringByAppendingString:b];
XCTAssertEqualObjects(got, want, @"want %@\nTestpkgBytesAppend(%@, %@) = %@", want, a, b, got);
}
- (void)testInterface {
// Test Go object implementing testpkg.I is handled correctly.
id<TestpkgI2> goObj = TestpkgNewI();
int64_t got = [goObj times:10];
XCTAssertEqual(got, 100, @"TestpkgNewI().times(10) = %lld; want %d", got, 100);
int32_t key = -1;
TestpkgRegisterI(key, goObj);
int64_t got2 = TestpkgMultiply(key, 10);
XCTAssertEqual(got, got2, @"TestpkgMultiply(10 * 10) = %lld; want %lld", got2, got);
TestpkgUnregisterI(key);
// Test Objective-C objects implementing testpkg.I is handled correctly.
@autoreleasepool {
for (int32_t i = 0; i < 10; i++) {
Number *num = [[Number alloc] init];
num.value = i;
TestpkgRegisterI(i, num);
}
TestpkgGC();
}
// Registered Objective-C objects are pinned on Go side which must
// prevent deallocation from Objective-C.
for (int32_t i = 0; i < 10; i++) {
int64_t got = TestpkgMultiply(i, 2);
XCTAssertEqual(got, i * 2,@"TestpkgMultiply(%d, 2) = %lld; want %d", i, got, i * 2);
TestpkgUnregisterI(i);
TestpkgGC();
}
// Unregistered all Objective-C objects.
}
- (void)testCollectI {
@autoreleasepool {
[self testInterface];
}
XCTAssertEqual(numI, 1, @"%d I objects were collected; I used in testInterface is supposed "
@"to be collected.", numI);
}
- (void)testConst {
XCTAssertEqualObjects(TestpkgAString, @"a string", @"TestpkgAString = %@, want 'a string'", TestpkgAString);
XCTAssertEqual(TestpkgAnInt, 7, @"TestpkgAnInt = %lld, want 7", TestpkgAnInt);
XCTAssertTrue(ABS(TestpkgAFloat - 0.12345) < 0.0001, @"TestpkgAFloat = %f, want 0.12345", TestpkgAFloat);
XCTAssertTrue(TestpkgABool == YES, @"TestpkgABool = %@, want YES", TestpkgAFloat ? @"YES" : @"NO");
XCTAssertEqual(TestpkgMinInt32, INT32_MIN, @"TestpkgMinInt32 = %d, want %d", TestpkgMinInt32, INT32_MIN);
XCTAssertEqual(TestpkgMaxInt32, INT32_MAX, @"TestpkgMaxInt32 = %d, want %d", TestpkgMaxInt32, INT32_MAX);
XCTAssertEqual(TestpkgMinInt64, INT64_MIN, @"TestpkgMinInt64 = %lld, want %lld", TestpkgMinInt64, INT64_MIN);
XCTAssertEqual(TestpkgMaxInt64, INT64_MAX, @"TestpkgMaxInt64 = %lld, want %lld", TestpkgMaxInt64, INT64_MAX);
XCTAssertTrue(ABS(TestpkgSmallestNonzeroFloat64 -
4.940656458412465441765687928682213723651e-324) < 1e-323, @"TestpkgSmallestNonzeroFloat64 = %f, want %f",
TestpkgSmallestNonzeroFloat64,
4.940656458412465441765687928682213723651e-324);
XCTAssertTrue(ABS(TestpkgMaxFloat64 -
1.797693134862315708145274237317043567981e+308) < 0.0001, @"TestpkgMaxFloat64 = %f, want %f", TestpkgMaxFloat64,
1.797693134862315708145274237317043567981e+308);
XCTAssertTrue(ABS(TestpkgSmallestNonzeroFloat32 -
1.401298464324817070923729583289916131280e-45) < 1e-44, @"TestpkgSmallestNonzeroFloat32 = %f, want %f",
TestpkgSmallestNonzeroFloat32,
1.401298464324817070923729583289916131280e-45);
XCTAssertTrue(ABS(TestpkgMaxFloat32 - 3.40282346638528859811704183484516925440e+38) < 0.0001,
@"TestpkgMaxFloat32 = %f, want %f", TestpkgMaxFloat32, 3.40282346638528859811704183484516925440e+38);
XCTAssertTrue(ABS(TestpkgLog2E - 1 / 0.693147180559945309417232121458176568075500134360255254120680009) < 0.0001,
@"TestpkgLog2E = %f, want %f", TestpkgLog2E, 1 / 0.693147180559945309417232121458176568075500134360255254120680009);
}
- (void)testIssue12307 {
Number *num = [[Number alloc] init];
num.value = 1024;
NSError *error;
XCTAssertFalse(TestpkgCallIError(num, YES, &error), @"TestpkgCallIError(Number, YES) succeeded; want error");
NSError *error2;
XCTAssertTrue(TestpkgCallIError(num, NO, &error2), @"TestpkgCallIError(Number, NO) failed(%@); want success", error2);
}
- (void)testErrorField {
NSString *wantMsg = @"an error message";
NSError *want = [NSError errorWithDomain:@"SeqTest" code:1 userInfo:@{NSLocalizedDescriptionKey: wantMsg}];
TestpkgNode *n = TestpkgNewNode(@"ErrTest");
n.err = want;
NSError *got = n.err;
XCTAssertEqual(got, want, @"got different objects after roundtrip");
NSString *gotMsg = TestpkgErrorMessage(want);
XCTAssertEqualObjects(gotMsg, wantMsg, @"err = %@, want %@", gotMsg, wantMsg);
}
- (void)testErrorDup {
NSError *err = Testpkg.globalErr;
XCTAssertTrue(TestpkgIsGlobalErr(err), @"A Go error must preserve its identity across the boundary");
XCTAssertEqualObjects([err localizedDescription], @"global err", "A Go error message must be preserved");
}
- (void)testVar {
NSString *s = Testpkg.stringVar;
XCTAssertEqualObjects(s, @"a string var", @"Testpkg.StringVar = %@, want 'a string var'", s);
s = @"a new string var";
Testpkg.stringVar = s;
NSString *s2 = Testpkg.stringVar;
XCTAssertEqualObjects(s2, s, @"Testpkg.stringVar = %@, want %@", s2, s);
int64_t i = Testpkg.intVar;
XCTAssertEqual(i, 77, @"Testpkg.intVar = %lld, want 77", i);
Testpkg.intVar = 777;
i = Testpkg.intVar;
XCTAssertEqual(i, 777, @"Testpkg.intVar = %lld, want 777", i);
[Testpkg setIntVar:7777];
i = [Testpkg intVar];
XCTAssertEqual(i, 7777, @"Testpkg.intVar = %lld, want 7777", i);
TestpkgNode *n0 = Testpkg.nodeVar;
XCTAssertEqualObjects(n0.v, @"a struct var", @"Testpkg.NodeVar = %@, want 'a struct var'", n0.v);
TestpkgNode *n1 = TestpkgNewNode(@"a new struct var");
Testpkg.nodeVar = n1;
TestpkgNode *n2 = Testpkg.nodeVar;
XCTAssertEqualObjects(n2.v, @"a new struct var", @"Testpkg.NodeVar = %@, want 'a new struct var'", n2.v);
Number *num = [[Number alloc] init];
num.value = 12345;
Testpkg.interfaceVar2 = num;
id<TestpkgI2> iface = Testpkg.interfaceVar2;
int64_t x = [iface times:10];
int64_t y = [num times:10];
XCTAssertEqual(x, y, @"Testpkg.InterfaceVar2 Times 10 = %lld, want %lld", x, y);
}
- (void)testIssue12403 {
Number *num = [[Number alloc] init];
num.value = 1024;
NSError *error;
NSString *ret = TestpkgCallIStringError(num, @"alphabet", &error);
XCTAssertNil(ret, @"TestpkgCallIStringError(Number, 'alphabet') succeeded(%@); want error", ret);
NSString *desc = [error localizedDescription];
XCTAssertEqualObjects(desc, @"NumberError", @"TestpkgCallIStringError(Number, 'alphabet') returned unexpected error message %@", desc);
NSError *error2;
NSString *ret2 = TestpkgCallIStringError(num, @"number", &error2);
XCTAssertNotNil(ret2, @"TestpkgCallIStringError(Number, 'number') failed(%@); want success", error2);
XCTAssertEqualObjects(ret2, @"OK", @"TestpkgCallIStringError(Number, 'number') returned unexpected results %@", ret2);
}
- (void)testStrDup:(NSString *)want {
NSString *got = TestpkgStrDup(want);
XCTAssertEqualObjects(want, got, @"StrDup returned %@; expected %@", got, want);
}
- (void)testUnicodeStrings {
[self testStrDup:@"abcxyz09{}"];
[self testStrDup:@"Hello, 世界"];
[self testStrDup:@"\uffff\U00010000\U00010001\U00012345\U0010ffff"];
}
- (void)testByteArrayRead {
NSData *arr = [NSMutableData dataWithLength:8];
long n;
XCTAssertTrue(TestpkgReadIntoByteArray(arr, &n, nil), @"ReadIntoByteArray failed");
XCTAssertEqual(n, 8, @"ReadIntoByteArray wrote %ld bytes, expected %d", n, 8);
const uint8_t *b = [arr bytes];
for (int i = 0; i < [arr length]; i++) {
XCTAssertEqual(b[i], i, @"ReadIntoByteArray wrote %d at %d; expected %d", b[i], i, i);
}
// Test that immutable data cannot be changed from Go
const uint8_t buf[] = {42};
arr = [NSData dataWithBytes:buf length:1];
XCTAssertTrue(TestpkgReadIntoByteArray(arr, &n, nil), @"ReadIntoByteArray failed");
XCTAssertEqual(n, 1, @"ReadIntoByteArray wrote %ld bytes, expected %d", n, 8);
b = [arr bytes];
XCTAssertEqual(b[0], 42, @"ReadIntoByteArray wrote to an immutable NSData; expected no change");
}
- (void)testNilField {
TestpkgNullFieldStruct *s = TestpkgNewNullFieldStruct();
XCTAssertNil([s f], @"NullFieldStruct has non-nil field; expected nil");
}
- (void)testNullReferences {
NullTest *t = [[NullTest alloc] init];
XCTAssertTrue(TestpkgCallWithNull(nil, t), @"Testpkg.CallWithNull failed");
id<TestpkgI> i = TestpkgNewNullInterface();
XCTAssertNil(i, @"NewNullInterface() returned %p; expected nil", i);
TestpkgS *s = TestpkgNewNullStruct();
XCTAssertNil(s, @"NewNullStruct() returned %p; expected nil", s);
TestpkgIssue20330 *nullArger = TestpkgNewIssue20330();
XCTAssertTrue([nullArger callWithNull:nil], @"Issue20330.CallWithNull failed");
}
- (void)testReturnsError {
NSError *error;
NSString *value = TestpkgReturnsError(TRUE, &error);
NSString *got = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
NSString *want = @"Error";
XCTAssertEqualObjects(got, want, @"want %@\nTestpkgReturnsError(TRUE) = (%@, %@)", want, value, got);
}
- (void)testImportedPkg {
XCTAssertEqualObjects(SecondpkgHelloString, SecondpkgHello(), @"imported string should match");
id<SecondpkgI> i = TestpkgNewImportedI();
SecondpkgS *s = TestpkgNewImportedS();
XCTAssertEqual(8, [i f:8], @"numbers should match");
XCTAssertEqual(8, [s f:8], @"numbers should match");
i = TestpkgWithImportedI(i);
s = TestpkgWithImportedS(s);
i = [Testpkg importedVarI];
s = [Testpkg importedVarS];
[Testpkg setImportedVarI:i];
[Testpkg setImportedVarS:s];
TestpkgImportedFields *fields = TestpkgNewImportedFields();
i = [fields i];
s = [fields s];
[fields setI:i];
[fields setS:s];
}
- (void)testRoundTripEquality {
Number *want = [[Number alloc] init];
Number *got = (Number *)TestpkgI2Dup(want);
XCTAssertEqual(got, want, @"ObjC object passed through Go should not be wrapped");
IDup *idup = [[IDup alloc] init];
XCTAssertTrue(TestpkgCallIDupper(idup), @"Go interface passed through ObjC should not be wrapped");
CDup *cdup = [[CDup alloc] init];
XCTAssertTrue(TestpkgCallCDupper(cdup), @"Go struct passed through ObjC should not be wrapped");
}
- (void)testEmptyError {
NSError *error;
XCTAssertFalse(TestpkgEmptyError(&error), @"GoTestpkgEmptyError succeeded; want error");
XCTAssertNotNil(error, @"TestpkgEmptyError returned nil error");
id<TestpkgEmptyErrorer> empty = [[EmptyErrorer alloc] init];
XCTAssertFalse(TestpkgCallEmptyError(empty, &error), @"TestpkgCallEmptyError succeeded; want error");
XCTAssertNotNil(error, @"TestpkgCallEmptyError returned nil error");
}
- (void)testSIGPIPE {
TestpkgTestSIGPIPE();
}
- (void)testTags {
XCTAssertEqual(42, TestpkgTaggedConst, @"Tagged const must exist");
}
- (void)testConstructors {
id<TestpkgInterface> i = [[TestpkgConcrete alloc] init];
[i f];
TestpkgS2 *s = [[TestpkgS2 alloc] init:1 y:2];
XCTAssertEqual(3.0, [s sum]);
XCTAssertEqualObjects(@"gostring", [s tryTwoStrings:@"go" second:@"string"]);
TestpkgS3 *s3 __attribute__((unused)) = [[TestpkgS3 alloc] init];
TestpkgS4 *s4 = [[TestpkgS4 alloc] initWithInt:123];
XCTAssertEqual(123, s4.i);
s4 = [[TestpkgS4 alloc] initWithFloat: 123.456];
XCTAssertEqual(123, s4.i);
s4 = [[TestpkgS4 alloc] initWithBoolAndError: false];
XCTAssertEqual(0, s4.i);
s4 = [[TestpkgS4 alloc] initWithBoolAndError: true];
XCTAssertEqual(s4, NULL);
}
@end

98
bind/objc/SeqWrappers.m Normal file
View file

@ -0,0 +1,98 @@
// 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.
//go:build ignore
// +build ignore
@import ObjectiveC.message;
@import Foundation;
@import XCTest;
@import Objcpkg;
@interface TestNSObject : NSObject
- (NSString *)description;
- (NSString *)super_description;
@end
@implementation TestNSObject
- (NSString *)description {
return @"hej";
}
- (NSString *)super_description {
return [super description];
}
@end
@interface wrappers : XCTestCase
@end
@implementation wrappers
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testFunction {
ObjcpkgFunc();
}
- (void)testMethod {
ObjcpkgMethod();
}
- (void)testNew {
ObjcpkgNew();
}
- (void)testError {
ObjcpkgError();
}
- (void)testClass {
ObjcpkgGoNSDate *d = [[ObjcpkgGoNSDate alloc] init];
NSString *desc = [d description];
XCTAssertEqual(d, [d getSelf], "GoNSDate self not identical");
XCTAssertEqual(ObjcpkgHash, [d hash], "GoNSDate hash not identical");
XCTAssertTrue([desc isEqualToString:ObjcpkgDescriptionStr], "GoNSDate description mismatch: %@", desc);
ObjcpkgGoUIResponder *resp = [[ObjcpkgGoUIResponder alloc] init];
[resp pressesBegan:nil withEvent:nil];
XCTAssertTrue([resp called], "GoUIResponder.pressesBegan not called");
}
- (void)testSuper {
ObjcpkgGoNSObject *o = [[ObjcpkgGoNSObject alloc] init];
struct objc_super _super = {
.receiver = o,
.super_class = [NSObject class],
};
NSString *superDesc = ((NSString *(*)(struct objc_super*, SEL))objc_msgSendSuper)(&_super, @selector(description));
XCTAssertTrue([superDesc isEqualToString:[o description]], "GoNSObject description mismatch");
[o setUseSelf:TRUE];
XCTAssertTrue([ObjcpkgDescriptionStr isEqualToString:[o description]], "GoNSObject description mismatch");
}
- (void)testIdentity {
NSDate *d = [[NSDate alloc] init];
NSDate *d2 = ObjcpkgDupNSDate(d);
XCTAssertEqual(d, d2, @"ObjcpkgDupNSDate failed to duplicate ObjC instance");
ObjcpkgGoNSDate *gd = [[ObjcpkgGoNSDate alloc] init];
NSDate *gd2 = ObjcpkgDupNSDate(gd);
XCTAssertEqual(gd, gd2, @"ObjcpkgDupNSDate failed to duplicate Go instance");
NSDate *gd3 = ObjcpkgNewGoNSDate();
NSDate *gd4 = ObjcpkgDupNSDate(gd3);
XCTAssertEqual(gd4, gd3, @"ObjcpkgDupNSDate failed to duplicate instance created in Go");
}
@end

6
bind/objc/doc.go Normal file
View file

@ -0,0 +1,6 @@
// 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 objc implements the Objective-C language bindings.
package objc

35
bind/objc/ref.h Normal file
View file

@ -0,0 +1,35 @@
// 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.
#ifndef __GO_REF_HDR__
#define __GO_REF_HDR__
#include <Foundation/Foundation.h>
// GoSeqRef is an object tagged with an integer for passing back and
// forth across the language boundary. A GoSeqRef may represent either
// an instance of a Go object, or an Objective-C object passed to Go.
// The explicit allocation of a GoSeqRef is used to pin a Go object
// when it is passed to Objective-C. The Go seq package maintains a
// reference to the Go object in a map keyed by the refnum along with
// a reference count. When the reference count reaches zero, the Go
// seq package will clear the corresponding entry in the map.
@interface GoSeqRef : NSObject {
}
@property(readonly) int32_t refnum;
@property(strong) id obj; // NULL when representing a Go object.
// new GoSeqRef object to proxy a Go object. The refnum must be
// provided from Go side.
- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj;
- (int32_t)incNum;
@end
@protocol goSeqRefInterface
-(GoSeqRef*) _ref;
@end
#endif

View file

@ -0,0 +1,91 @@
// Copyright 2016 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 main
// Go support functions for Objective-C. Note that this
// file is copied into and compiled with the generated
// bindings.
/*
#cgo CFLAGS: -x objective-c -fobjc-arc -fmodules -fblocks -Werror
#cgo LDFLAGS: -framework Foundation
#include <stdint.h>
#include <stdlib.h>
#include "seq.h"
*/
import "C"
import (
"unsafe"
"golang.org/x/mobile/bind/seq"
)
// DestroyRef is called by Objective-C to inform Go it is done with a reference.
//export DestroyRef
func DestroyRef(refnum C.int32_t) {
seq.Delete(int32(refnum))
}
// encodeString copies a Go string and returns it as a nstring.
func encodeString(s string) C.nstring {
n := C.int(len(s))
if n == 0 {
return C.nstring{}
}
ptr := C.malloc(C.size_t(n))
if ptr == nil {
panic("encodeString: malloc failed")
}
copy((*[1<<31 - 1]byte)(ptr)[:n], s)
return C.nstring{ptr: ptr, len: n}
}
// decodeString converts a nstring to a Go string. The
// data in str is freed after use.
func decodeString(str C.nstring) string {
if str.ptr == nil {
return ""
}
s := C.GoStringN((*C.char)(str.ptr), str.len)
C.free(str.ptr)
return s
}
// fromSlice converts a slice to a nbyteslice.
// If cpy is set, a malloc'ed copy of the data is returned.
func fromSlice(s []byte, cpy bool) C.nbyteslice {
if s == nil || len(s) == 0 {
return C.nbyteslice{}
}
ptr, n := unsafe.Pointer(&s[0]), C.int(len(s))
if cpy {
nptr := C.malloc(C.size_t(n))
if nptr == nil {
panic("fromSlice: malloc failed")
}
copy((*[1<<31 - 1]byte)(nptr)[:n], (*[1<<31 - 1]byte)(ptr)[:n])
ptr = nptr
}
return C.nbyteslice{ptr: ptr, len: n}
}
// toSlice takes a nbyteslice and returns a byte slice with the data. If cpy is
// set, the slice contains a copy of the data. If not, the generated Go code
// calls releaseByteSlice after use.
func toSlice(s C.nbyteslice, cpy bool) []byte {
if s.ptr == nil || s.len == 0 {
return nil
}
var b []byte
if cpy {
b = C.GoBytes(s.ptr, C.int(s.len))
C.free(s.ptr)
} else {
b = (*[1<<31 - 1]byte)(unsafe.Pointer(s.ptr))[:s.len:s.len]
}
return b
}

63
bind/objc/seq_darwin.h Normal file
View file

@ -0,0 +1,63 @@
// 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.
#ifndef __GO_SEQ_DARWIN_HDR__
#define __GO_SEQ_DARWIN_HDR__
#include <Foundation/Foundation.h>
#include "ref.h"
#include "Universe.objc.h"
#ifdef DEBUG
#define LOG_DEBUG(...) NSLog(__VA_ARGS__);
#else
#define LOG_DEBUG(...) ;
#endif
#define LOG_INFO(...) NSLog(__VA_ARGS__);
#define LOG_FATAL(...) \
{ \
NSLog(__VA_ARGS__); \
@throw \
[NSException exceptionWithName:NSInternalInconsistencyException \
reason:[NSString stringWithFormat:__VA_ARGS__] \
userInfo:NULL]; \
}
// Platform specific types
typedef struct nstring {
void *ptr;
int len;
} nstring;
typedef struct nbyteslice {
void *ptr;
int len;
} nbyteslice;
typedef int nint;
extern void init_seq();
// go_seq_dec_ref decrements the reference count for the
// specified refnum. It is called from Go from a finalizer.
extern void go_seq_dec_ref(int32_t refnum);
// go_seq_inc_ref increments the reference count for the
// specified refnum. It is called from Go just before converting
// a proxy to its refnum.
extern void go_seq_inc_ref(int32_t refnum);
extern int32_t go_seq_to_refnum(id obj);
// go_seq_go_to_refnum is a special case of go_seq_to_refnum
extern int32_t go_seq_go_to_refnum(GoSeqRef *ref);
extern GoSeqRef *go_seq_from_refnum(int32_t refnum);
// go_seq_objc_from_refnum is a special case of go_seq_from_refnum for
// Objective-C objects that implement a Go interface.
extern id go_seq_objc_from_refnum(int32_t refnum);
extern nbyteslice go_seq_from_objc_bytearray(NSData *data, int copy);
extern nstring go_seq_from_objc_string(NSString *s);
extern NSData *go_seq_to_objc_bytearray(nbyteslice, int copy);
extern NSString *go_seq_to_objc_string(nstring str);
#endif // __GO_SEQ_DARWIN_HDR__

View file

@ -0,0 +1,381 @@
// Copyright 2016 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.
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <Foundation/Foundation.h>
#include "seq.h"
#include "_cgo_export.h"
// * Objective-C implementation of a Go interface type
//
// For an interface testpkg.I, gobind defines a protocol GoSeqTestpkgI.
// Reference tracker (tracker) maintains two maps:
// 1) _refs: objective-C object pointer -> a refnum (starting from 42).
// 2) _objs: refnum -> RefCounter.
//
// Whenever a user's object conforming the protocol is sent to Go (through
// a function or method that takes I), _refs is consulted to find the refnum
// of the object. If not found, the refnum is assigned and stored.
//
// _objs is also updated so that the RefCounter is incremented and the
// user's object is pinned.
//
// When a Go side needs to call a method of the interface, the Go side
// notifies the Objective-C side of the object's refnum. Upon receiving the
// request, Objective-C side looks up the object from _objs map, and sends
// the method to the object.
//
// The RefCount counts the references on objective-C objects from Go side,
// and pins the objective-C objects until there is no more references from
// Go side.
//
// * Objective-C proxy of a Go object (struct or interface type)
//
// For Go type object, a objective-C proxy instance is created whenever
// the object reference is passed into objective-C.
//
// While crossing the language barrier there is a brief window where the foreign
// proxy object might be finalized but the refnum is not yet translated to its object.
// If the proxy object was the last reference to the foreign object, the refnum
// will be invalid by the time it is looked up in the foreign reference tracker.
//
// To make sure the foreign object is kept live while its refnum is in transit,
// increment its refererence count before crossing. The other side will decrement
// it again immediately after the refnum is converted to its object.
// Note that this file is copied into and compiled with the generated
// bindings.
// A simple thread-safe mutable dictionary.
@interface goSeqDictionary : NSObject {
}
@property NSMutableDictionary *dict;
@end
@implementation goSeqDictionary
- (id)init {
if (self = [super init]) {
_dict = [[NSMutableDictionary alloc] init];
}
return self;
}
- (id)get:(id)key {
@synchronized(self) {
return [_dict objectForKey:key];
}
}
- (void)put:(id)obj withKey:(id)key {
@synchronized(self) {
[_dict setObject:obj forKey:key];
}
}
@end
// NULL_REFNUM is also known to bind/seq/ref.go and bind/java/Seq.java
#define NULL_REFNUM 41
// RefTracker encapsulates a map of objective-C objects passed to Go and
// the reference number counter which is incremented whenever an objective-C
// object that implements a Go interface is created.
@interface RefTracker : NSObject {
int32_t _next;
NSMutableDictionary *_refs; // map: object ptr -> refnum
NSMutableDictionary *_objs; // map: refnum -> RefCounter*
}
- (id)init;
// decrements the counter of the objective-C object with the reference number.
// This is called whenever a Go proxy to this object is finalized.
// When the counter reaches 0, the object is removed from the map.
- (void)dec:(int32_t)refnum;
// increments the counter of the objective-C object with the reference number.
// This is called whenever a Go proxy is converted to its refnum and send
// across the language barrier.
- (void)inc:(int32_t)refnum;
// returns the object of the reference number.
- (id)get:(int32_t)refnum;
// returns the reference number of the object and increments the ref count.
// This is called whenever an Objective-C object is sent to Go side.
- (int32_t)assignRefnumAndIncRefcount:(id)obj;
@end
static RefTracker *tracker = NULL;
#define IS_FROM_GO(refnum) ((refnum) < 0)
// init_seq is called when the Go side is initialized.
void init_seq() { tracker = [[RefTracker alloc] init]; }
void go_seq_dec_ref(int32_t refnum) {
@autoreleasepool {
[tracker dec:refnum];
}
}
void go_seq_inc_ref(int32_t refnum) {
@autoreleasepool {
[tracker inc:refnum];
}
}
NSData *go_seq_to_objc_bytearray(nbyteslice s, int copy) {
if (s.ptr == NULL) {
return NULL;
}
BOOL freeWhenDone = copy ? YES : NO;
return [NSData dataWithBytesNoCopy:s.ptr length:s.len freeWhenDone:freeWhenDone];
}
NSString *go_seq_to_objc_string(nstring str) {
if (str.len == 0) { // empty string.
return @"";
}
NSString * res = [[NSString alloc] initWithBytesNoCopy:str.ptr
length:str.len
encoding:NSUTF8StringEncoding
freeWhenDone:YES];
return res;
}
id go_seq_objc_from_refnum(int32_t refnum) {
id obj = [tracker get:refnum];
// Go called IncForeignRef just before converting its proxy to its refnum. Decrement it here.
// It's very important to decrement *after* fetching the reference from the tracker, in case
// there are no other proxy references to the object.
[tracker dec:refnum];
return obj;
}
GoSeqRef *go_seq_from_refnum(int32_t refnum) {
if (refnum == NULL_REFNUM) {
return nil;
}
if (IS_FROM_GO(refnum)) {
return [[GoSeqRef alloc] initWithRefnum:refnum obj:NULL];
}
return [[GoSeqRef alloc] initWithRefnum:refnum obj:go_seq_objc_from_refnum(refnum)];
}
int32_t go_seq_to_refnum(id obj) {
if (obj == nil) {
return NULL_REFNUM;
}
return [tracker assignRefnumAndIncRefcount:obj];
}
int32_t go_seq_go_to_refnum(GoSeqRef *ref) {
int32_t refnum = [ref incNum];
if (!IS_FROM_GO(refnum)) {
LOG_FATAL(@"go_seq_go_to_refnum on objective-c objects is not permitted");
}
return refnum;
}
nbyteslice go_seq_from_objc_bytearray(NSData *data, int copy) {
struct nbyteslice res = {NULL, 0};
int sz = data.length;
if (sz == 0) {
return res;
}
void *ptr;
// If the argument was not a NSMutableData, copy the data so that
// the NSData is not changed from Go. The corresponding free is called
// by releaseByteSlice.
if (copy || ![data isKindOfClass:[NSMutableData class]]) {
void *arr_copy = malloc(sz);
if (arr_copy == NULL) {
LOG_FATAL(@"malloc failed");
}
memcpy(arr_copy, [data bytes], sz);
ptr = arr_copy;
} else {
ptr = (void *)[data bytes];
}
res.ptr = ptr;
res.len = sz;
return res;
}
nstring go_seq_from_objc_string(NSString *s) {
nstring res = {NULL, 0};
int len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
if (len == 0) {
if (s.length > 0) {
LOG_INFO(@"unable to encode an NSString into UTF-8");
}
return res;
}
char *buf = (char *)malloc(len);
if (buf == NULL) {
LOG_FATAL(@"malloc failed");
}
NSUInteger used;
[s getBytes:buf
maxLength:len
usedLength:&used
encoding:NSUTF8StringEncoding
options:0
range:NSMakeRange(0, [s length])
remainingRange:NULL];
res.ptr = buf;
res.len = used;
return res;
}
@implementation GoSeqRef {
}
- (id)init {
LOG_FATAL(@"GoSeqRef init is disallowed");
}
- (int32_t)incNum {
IncGoRef(_refnum);
return _refnum;
}
// called when an object from Go is passed in.
- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj {
self = [super init];
if (self) {
_refnum = refnum;
_obj = obj;
}
return self;
}
- (void)dealloc {
if (IS_FROM_GO(_refnum)) {
DestroyRef(_refnum);
}
}
@end
// RefCounter is a pair of (GoSeqProxy, count). GoSeqProxy has a strong
// reference to an Objective-C object. The count corresponds to
// the number of Go proxy objects.
//
// RefTracker maintains a map of refnum to RefCounter, for every
// Objective-C objects passed to Go. This map allows the transact
// call to relay the method call to the right Objective-C object, and
// prevents the Objective-C objects from being deallocated
// while they are still referenced from Go side.
@interface RefCounter : NSObject {
}
@property(strong, readonly) id obj;
@property int cnt;
- (id)initWithObject:(id)obj;
@end
@implementation RefCounter {
}
- (id)initWithObject:(id)obj {
self = [super init];
if (self) {
_obj = obj;
_cnt = 0;
}
return self;
}
@end
@implementation RefTracker {
}
- (id)init {
self = [super init];
if (self) {
_next = 42;
_refs = [[NSMutableDictionary alloc] init];
_objs = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dec:(int32_t)refnum { // called whenever a go proxy object is finalized.
if (IS_FROM_GO(refnum)) {
LOG_FATAL(@"dec:invalid refnum for Objective-C objects");
}
@synchronized(self) {
id key = @(refnum);
RefCounter *counter = [_objs objectForKey:key];
if (counter == NULL) {
LOG_FATAL(@"unknown refnum");
}
int n = counter.cnt;
if (n <= 0) {
LOG_FATAL(@"refcount underflow");
} else if (n == 1) {
LOG_DEBUG(@"remove the reference %d", refnum);
NSValue *ptr = [NSValue valueWithPointer:(const void *)(counter.obj)];
[_refs removeObjectForKey:ptr];
[_objs removeObjectForKey:key];
} else {
counter.cnt = n - 1;
}
}
}
// inc is called whenever a ObjC refnum crosses from Go to ObjC
- (void)inc:(int32_t)refnum {
if (IS_FROM_GO(refnum)) {
LOG_FATAL(@"dec:invalid refnum for Objective-C objects");
}
@synchronized(self) {
id key = @(refnum);
RefCounter *counter = [_objs objectForKey:key];
if (counter == NULL) {
LOG_FATAL(@"unknown refnum");
}
counter.cnt++;
}
}
- (id)get:(int32_t)refnum {
if (IS_FROM_GO(refnum)) {
LOG_FATAL(@"get:invalid refnum for Objective-C objects");
}
@synchronized(self) {
RefCounter *counter = _objs[@(refnum)];
if (counter == NULL) {
LOG_FATAL(@"unidentified object refnum: %d", refnum);
}
return counter.obj;
}
}
- (int32_t)assignRefnumAndIncRefcount:(id)obj {
@synchronized(self) {
NSValue *ptr = [NSValue valueWithPointer:(const void *)(obj)];
NSNumber *refnum = [_refs objectForKey:ptr];
if (refnum == NULL) {
refnum = @(_next++);
_refs[ptr] = refnum;
}
RefCounter *counter = [_objs objectForKey:refnum];
if (counter == NULL) {
counter = [[RefCounter alloc] initWithObject:obj];
counter.cnt = 1;
_objs[refnum] = counter;
} else {
counter.cnt++;
}
return (int32_t)([refnum intValue]);
}
}
@end

1009
bind/objc/seq_test.go Normal file

File diff suppressed because it is too large Load diff

67
bind/printer.go Normal file
View file

@ -0,0 +1,67 @@
// Copyright 2014 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 (
"bytes"
"fmt"
)
type Printer struct {
Buf *bytes.Buffer
IndentEach []byte
indentText []byte
needIndent bool
}
func (p *Printer) writeIndent() error {
if !p.needIndent {
return nil
}
p.needIndent = false
_, err := p.Buf.Write(p.indentText)
return err
}
func (p *Printer) Write(b []byte) (n int, err error) {
wrote := 0
for len(b) > 0 {
if err := p.writeIndent(); err != nil {
return wrote, err
}
i := bytes.IndexByte(b, '\n')
if i < 0 {
break
}
n, err = p.Buf.Write(b[0 : i+1])
wrote += n
if err != nil {
return wrote, err
}
b = b[i+1:]
p.needIndent = true
}
if len(b) > 0 {
n, err = p.Buf.Write(b)
wrote += n
}
return wrote, err
}
func (p *Printer) Printf(format string, args ...interface{}) {
if _, err := fmt.Fprintf(p, format, args...); err != nil {
panic(fmt.Sprintf("printer: %v", err))
}
}
func (p *Printer) Indent() {
p.indentText = append(p.indentText, p.IndentEach...)
}
func (p *Printer) Outdent() {
if len(p.indentText) > len(p.IndentEach)-1 {
p.indentText = p.indentText[len(p.IndentEach):]
}
}

52
bind/seq.go.support Normal file
View file

@ -0,0 +1,52 @@
// Copyright 2016 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 main
// Go support functions for generated Go bindings. This file is
// copied into the generated main package, and compiled along
// with the bindings.
// #cgo android CFLAGS: -D__GOBIND_ANDROID__
// #cgo darwin CFLAGS: -D__GOBIND_DARWIN__
// #include <stdlib.h>
// #include "seq.h"
import "C"
import (
"fmt"
"os"
"os/signal"
"syscall"
_ "golang.org/x/mobile/bind/java"
_seq "golang.org/x/mobile/bind/seq"
)
func init() {
_seq.FinalizeRef = func(ref *_seq.Ref) {
refnum := ref.Bind_Num
if refnum < 0 {
panic(fmt.Sprintf("not a foreign ref: %d", refnum))
}
C.go_seq_dec_ref(C.int32_t(refnum))
}
_seq.IncForeignRef = func(refnum int32) {
if refnum < 0 {
panic(fmt.Sprintf("not a foreign ref: %d", refnum))
}
C.go_seq_inc_ref(C.int32_t(refnum))
}
// Workaround for issue #17393.
signal.Notify(make(chan os.Signal), syscall.SIGPIPE)
}
// IncGoRef is called by foreign code to pin a Go object while its refnum is crossing
// the language barrier
//export IncGoRef
func IncGoRef(refnum C.int32_t) {
_seq.Inc(int32(refnum))
}
func main() {}

153
bind/seq/ref.go Normal file
View file

@ -0,0 +1,153 @@
// Copyright 2014 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 seq
//#cgo LDFLAGS: -llog
//#include <android/log.h>
//#include <string.h>
//import "C"
import (
"fmt"
"runtime"
"sync"
)
type countedObj struct {
obj interface{}
cnt int32
}
// also known to bind/java/Seq.java and bind/objc/seq_darwin.m
const NullRefNum = 41
// refs stores Go objects that have been passed to another language.
var refs struct {
sync.Mutex
next int32 // next reference number to use for Go object, always negative
refs map[interface{}]int32
objs map[int32]countedObj
}
func init() {
refs.Lock()
refs.next = -24 // Go objects get negative reference numbers. Arbitrary starting point.
refs.refs = make(map[interface{}]int32)
refs.objs = make(map[int32]countedObj)
refs.Unlock()
}
// A Ref represents a Java or Go object passed across the language
// boundary.
type Ref struct {
Bind_Num int32
}
type proxy interface {
// Use a strange name and hope that user code does not implement it
Bind_proxy_refnum__() int32
}
// ToRefNum increments the reference count for an object and
// returns its refnum.
func ToRefNum(obj interface{}) int32 {
// We don't track foreign objects, so if obj is a proxy
// return its refnum.
if r, ok := obj.(proxy); ok {
refnum := r.Bind_proxy_refnum__()
if refnum <= 0 {
panic(fmt.Errorf("seq: proxy contained invalid Go refnum: %d", refnum))
}
return refnum
}
refs.Lock()
num := refs.refs[obj]
if num != 0 {
s := refs.objs[num]
refs.objs[num] = countedObj{s.obj, s.cnt + 1}
} else {
num = refs.next
refs.next--
if refs.next > 0 {
panic("refs.next underflow")
}
refs.refs[obj] = num
refs.objs[num] = countedObj{obj, 1}
}
refs.Unlock()
return num
}
// FromRefNum returns the Ref for a refnum. If the refnum specifies a
// foreign object, a finalizer is set to track its lifetime.
func FromRefNum(num int32) *Ref {
if num == NullRefNum {
return nil
}
ref := &Ref{num}
if num > 0 {
// This is a foreign object reference.
// Track its lifetime with a finalizer.
runtime.SetFinalizer(ref, FinalizeRef)
}
return ref
}
// Bind_IncNum increments the foreign reference count and
// return the refnum.
func (r *Ref) Bind_IncNum() int32 {
refnum := r.Bind_Num
IncForeignRef(refnum)
// Make sure this reference is not finalized before
// the foreign reference count is incremented.
runtime.KeepAlive(r)
return refnum
}
// Get returns the underlying object.
func (r *Ref) Get() interface{} {
refnum := r.Bind_Num
refs.Lock()
o, ok := refs.objs[refnum]
refs.Unlock()
if !ok {
panic(fmt.Sprintf("unknown ref %d", refnum))
}
// This is a Go reference and its refnum was incremented
// before crossing the language barrier.
Delete(refnum)
return o.obj
}
// Inc increments the reference count for a refnum. Called from Bind_proxy_refnum
// functions.
func Inc(num int32) {
refs.Lock()
o, ok := refs.objs[num]
if !ok {
panic(fmt.Sprintf("seq.Inc: unknown refnum: %d", num))
}
refs.objs[num] = countedObj{o.obj, o.cnt + 1}
refs.Unlock()
}
// Delete decrements the reference count and removes the pinned object
// from the object map when the reference count becomes zero.
func Delete(num int32) {
refs.Lock()
defer refs.Unlock()
o, ok := refs.objs[num]
if !ok {
panic(fmt.Sprintf("seq.Delete unknown refnum: %d", num))
}
if o.cnt <= 1 {
delete(refs.objs, num)
delete(refs.refs, o.obj)
} else {
refs.objs[num] = countedObj{o.obj, o.cnt - 1}
}
}

21
bind/seq/seq.go Normal file
View file

@ -0,0 +1,21 @@
// Copyright 2014 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 seq implements the machine-dependent seq serialization format.
//
// Implementations of Transact and FinalizeRef are provided by a
// specific foreign language binding package, e.g. go.mobile/bind/java.
//
// Designed only for use by the code generated by gobind. Don't try to
// use this directly.
package seq
import _ "golang.org/x/mobile/internal/mobileinit"
// FinalizeRef is the finalizer used on foreign objects.
var FinalizeRef func(ref *Ref)
// IncRef increments the foreign reference count for ref while it is in transit.
// The count is decremented after the ref is received and translated on the foreign side.
var IncForeignRef func(refnum int32)

49
bind/seq/string.go Normal file
View file

@ -0,0 +1,49 @@
// Copyright 2014 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 seq
import "unicode/utf16"
// Based heavily on package unicode/utf16 from the Go standard library.
const (
replacementChar = '\uFFFD' // Unicode replacement character
maxRune = '\U0010FFFF' // Maximum valid Unicode code point.
)
const (
// 0xd800-0xdc00 encodes the high 10 bits of a pair.
// 0xdc00-0xe000 encodes the low 10 bits of a pair.
// the value is those 20 bits plus 0x10000.
surr1 = 0xd800
surr2 = 0xdc00
surr3 = 0xe000
surrSelf = 0x10000
)
// UTF16Encode utf16 encodes s into chars. It returns the resulting
// length in units of uint16. It is assumed that the chars slice
// has enough room for the encoded string.
func UTF16Encode(s string, chars []uint16) int {
n := 0
for _, v := range s {
switch {
case v < 0, surr1 <= v && v < surr3, v > maxRune:
v = replacementChar
fallthrough
case v < surrSelf:
chars[n] = uint16(v)
n += 1
default:
// surrogate pair, two uint16 values
r1, r2 := utf16.EncodeRune(v)
chars[n] = uint16(r1)
chars[n+1] = uint16(r2)
n += 2
}
}
return n
}

28
bind/seq/string_test.go Normal file
View file

@ -0,0 +1,28 @@
// Copyright 2014 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 seq
import (
"testing"
"unicode/utf16"
)
var strData = []string{
"abcxyz09{}",
"Hello, 世界",
string([]rune{0xffff, 0x10000, 0x10001, 0x12345, 0x10ffff}),
}
func TestString(t *testing.T) {
for _, test := range strData {
chars := make([]uint16, 4*len(test))
nchars := UTF16Encode(test, chars)
chars = chars[:nchars]
got := string(utf16.Decode(chars))
if got != test {
t.Errorf("UTF16: got %q, want %q", got, test)
}
}
}

25
bind/testdata/basictypes.go vendored Normal file
View file

@ -0,0 +1,25 @@
// Copyright 2014 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 basictypes
const (
AString = "a string"
AnInt = 7
AnInt2 = 1<<63 - 1
AFloat = 0.2015
ARune = rune(32)
ABool = true
ALongString = "LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString"
)
func Ints(x int8, y int16, z int32, t int64, u int) {}
func Error() error { return nil }
func ErrorPair() (int, error) { return 0, nil }
func ByteArrays(x []byte) []byte { return nil }
func Bool(bool) bool { return true }

73
bind/testdata/basictypes.go.golden vendored Normal file
View file

@ -0,0 +1,73 @@
// Code generated by gobind. DO NOT EDIT.
// Package main is an autogenerated binder stub for package basictypes.
//
// autogenerated by gobind -lang=go basictypes
package main
/*
#include <stdlib.h>
#include <stdint.h>
#include "seq.h"
#include "basictypes.h"
*/
import "C"
import (
"basictypes"
_seq "golang.org/x/mobile/bind/seq"
)
// suppress the error if seq ends up unused
var _ = _seq.FromRefNum
//export proxybasictypes__Bool
func proxybasictypes__Bool(param_p0 C.char) C.char {
_param_p0 := param_p0 != 0
res_0 := basictypes.Bool(_param_p0)
var _res_0 C.char = 0
if res_0 {
_res_0 = 1
}
return _res_0
}
//export proxybasictypes__ByteArrays
func proxybasictypes__ByteArrays(param_x C.nbyteslice) C.nbyteslice {
_param_x := toSlice(param_x, false)
res_0 := basictypes.ByteArrays(_param_x)
_res_0 := fromSlice(res_0, true)
return _res_0
}
//export proxybasictypes__Error
func proxybasictypes__Error() C.int32_t {
res_0 := basictypes.Error()
var _res_0 C.int32_t = _seq.NullRefNum
if res_0 != nil {
_res_0 = C.int32_t(_seq.ToRefNum(res_0))
}
return _res_0
}
//export proxybasictypes__ErrorPair
func proxybasictypes__ErrorPair() (C.nint, C.int32_t) {
res_0, res_1 := basictypes.ErrorPair()
_res_0 := C.nint(res_0)
var _res_1 C.int32_t = _seq.NullRefNum
if res_1 != nil {
_res_1 = C.int32_t(_seq.ToRefNum(res_1))
}
return _res_0, _res_1
}
//export proxybasictypes__Ints
func proxybasictypes__Ints(param_x C.int8_t, param_y C.int16_t, param_z C.int32_t, param_t C.int64_t, param_u C.nint) {
_param_x := int8(param_x)
_param_y := int16(param_y)
_param_z := int32(param_z)
_param_t := int64(param_t)
_param_u := int(param_u)
basictypes.Ints(_param_x, _param_y, _param_z, _param_t, _param_u)
}

61
bind/testdata/basictypes.java.c.golden vendored Normal file
View file

@ -0,0 +1,61 @@
// Code generated by gobind. DO NOT EDIT.
// JNI functions for the Go <=> Java bridge.
//
// autogenerated by gobind -lang=java basictypes
#include <android/log.h>
#include <stdint.h>
#include "seq.h"
#include "_cgo_export.h"
#include "basictypes.h"
JNIEXPORT void JNICALL
Java_basictypes_Basictypes__1init(JNIEnv *env, jclass _unused) {
jclass clazz;
}
JNIEXPORT jboolean JNICALL
Java_basictypes_Basictypes_bool(JNIEnv* env, jclass _clazz, jboolean p0) {
char _p0 = (char)p0;
char r0 = proxybasictypes__Bool(_p0);
jboolean _r0 = r0 ? JNI_TRUE : JNI_FALSE;
return _r0;
}
JNIEXPORT jbyteArray JNICALL
Java_basictypes_Basictypes_byteArrays(JNIEnv* env, jclass _clazz, jbyteArray x) {
nbyteslice _x = go_seq_from_java_bytearray(env, x, 0);
nbyteslice r0 = proxybasictypes__ByteArrays(_x);
go_seq_release_byte_array(env, x, _x.ptr);
jbyteArray _r0 = go_seq_to_java_bytearray(env, r0, 1);
return _r0;
}
JNIEXPORT void JNICALL
Java_basictypes_Basictypes_error(JNIEnv* env, jclass _clazz) {
int32_t r0 = proxybasictypes__Error();
jobject _r0 = go_seq_from_refnum(env, r0, proxy_class__error, proxy_class__error_cons);
go_seq_maybe_throw_exception(env, _r0);
}
JNIEXPORT jlong JNICALL
Java_basictypes_Basictypes_errorPair(JNIEnv* env, jclass _clazz) {
struct proxybasictypes__ErrorPair_return res = proxybasictypes__ErrorPair();
jlong _r0 = (jlong)res.r0;
jobject _r1 = go_seq_from_refnum(env, res.r1, proxy_class__error, proxy_class__error_cons);
go_seq_maybe_throw_exception(env, _r1);
return _r0;
}
JNIEXPORT void JNICALL
Java_basictypes_Basictypes_ints(JNIEnv* env, jclass _clazz, jbyte x, jshort y, jint z, jlong t, jlong u) {
int8_t _x = (int8_t)x;
int16_t _y = (int16_t)y;
int32_t _z = (int32_t)z;
int64_t _t = (int64_t)t;
nint _u = (nint)u;
proxybasictypes__Ints(_x, _y, _z, _t, _u);
}

37
bind/testdata/basictypes.java.golden vendored Normal file
View file

@ -0,0 +1,37 @@
// Code generated by gobind. DO NOT EDIT.
// Java class basictypes.Basictypes is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java basictypes
package basictypes;
import go.Seq;
public abstract class Basictypes {
static {
Seq.touch(); // for loading the native library
_init();
}
private Basictypes() {} // uninstantiable
// touch is called from other bound packages to initialize this package
public static void touch() {}
private static native void _init();
public static final boolean ABool = true;
public static final double AFloat = 0.2015;
public static final String ALongString = "LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString";
public static final int ARune = 32;
public static final String AString = "a string";
public static final long AnInt = 7L;
public static final long AnInt2 = 9223372036854775807L;
public static native boolean bool(boolean p0);
public static native byte[] byteArrays(byte[] x);
public static native void error() throws Exception;
public static native long errorPair() throws Exception;
public static native void ints(byte x, short y, int z, long t, long u);
}

12
bind/testdata/basictypes.java.h.golden vendored Normal file
View file

@ -0,0 +1,12 @@
// Code generated by gobind. DO NOT EDIT.
// JNI function headers for the Go <=> Java bridge.
//
// autogenerated by gobind -lang=java basictypes
#ifndef __Basictypes_H__
#define __Basictypes_H__
#include <jni.h>
#endif

View file

@ -0,0 +1,11 @@
// Objective-C API for talking to basictypes Go package.
// gobind -lang=objc basictypes
//
// File is generated by gobind. Do not edit.
#ifndef __GO_basictypes_H__
#define __GO_basictypes_H__
#include <stdint.h>
#include <objc/objc.h>
#endif

32
bind/testdata/basictypes.objc.h.golden vendored Normal file
View file

@ -0,0 +1,32 @@
// Objective-C API for talking to basictypes Go package.
// gobind -lang=objc basictypes
//
// File is generated by gobind. Do not edit.
#ifndef __Basictypes_H__
#define __Basictypes_H__
@import Foundation;
#include "ref.h"
#include "Universe.objc.h"
FOUNDATION_EXPORT const BOOL BasictypesABool;
FOUNDATION_EXPORT const double BasictypesAFloat;
FOUNDATION_EXPORT NSString* _Nonnull const BasictypesALongString;
FOUNDATION_EXPORT const int32_t BasictypesARune;
FOUNDATION_EXPORT NSString* _Nonnull const BasictypesAString;
FOUNDATION_EXPORT const int64_t BasictypesAnInt;
FOUNDATION_EXPORT const int64_t BasictypesAnInt2;
FOUNDATION_EXPORT BOOL BasictypesBool(BOOL p0);
FOUNDATION_EXPORT NSData* _Nullable BasictypesByteArrays(NSData* _Nullable x);
FOUNDATION_EXPORT BOOL BasictypesError(NSError* _Nullable* _Nullable error);
FOUNDATION_EXPORT BOOL BasictypesErrorPair(long* _Nullable ret0_, NSError* _Nullable* _Nullable error);
FOUNDATION_EXPORT void BasictypesInts(int8_t x, int16_t y, int32_t z, int64_t t, long u);
#endif

82
bind/testdata/basictypes.objc.m.golden vendored Normal file
View file

@ -0,0 +1,82 @@
// Objective-C API for talking to basictypes Go package.
// gobind -lang=objc basictypes
//
// File is generated by gobind. Do not edit.
#include <Foundation/Foundation.h>
#include "seq.h"
#include "_cgo_export.h"
#include "Basictypes.objc.h"
const BOOL BasictypesABool = YES;
const double BasictypesAFloat = 0.2015;
NSString* const BasictypesALongString = @"LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString";
const int32_t BasictypesARune = 32;
NSString* const BasictypesAString = @"a string";
const int64_t BasictypesAnInt = 7LL;
const int64_t BasictypesAnInt2 = 9223372036854775807LL;
BOOL BasictypesBool(BOOL p0) {
char _p0 = (char)p0;
char r0 = proxybasictypes__Bool(_p0);
BOOL _ret0_ = r0 ? YES : NO;
return _ret0_;
}
NSData* _Nullable BasictypesByteArrays(NSData* _Nullable x) {
nbyteslice _x = go_seq_from_objc_bytearray(x, 0);
nbyteslice r0 = proxybasictypes__ByteArrays(_x);
if (![x isKindOfClass:[NSMutableData class]]) {
free(_x.ptr);
}
NSData *_ret0_ = go_seq_to_objc_bytearray(r0, 1);
return _ret0_;
}
BOOL BasictypesError(NSError* _Nullable* _Nullable error) {
int32_t r0 = proxybasictypes__Error();
Universeerror* _error = nil;
GoSeqRef* _error_ref = go_seq_from_refnum(r0);
if (_error_ref != NULL) {
_error = _error_ref.obj;
if (_error == nil) {
_error = [[Universeerror alloc] initWithRef:_error_ref];
}
}
if (_error != nil && error != nil) {
*error = _error;
}
return (_error == nil);
}
BOOL BasictypesErrorPair(long* _Nullable ret0_, NSError* _Nullable* _Nullable error) {
struct proxybasictypes__ErrorPair_return res = proxybasictypes__ErrorPair();
long _ret0_ = (long)res.r0;
Universeerror* _error = nil;
GoSeqRef* _error_ref = go_seq_from_refnum(res.r1);
if (_error_ref != NULL) {
_error = _error_ref.obj;
if (_error == nil) {
_error = [[Universeerror alloc] initWithRef:_error_ref];
}
}
*ret0_ = _ret0_;
if (_error != nil && error != nil) {
*error = _error;
}
return (_error == nil);
}
void BasictypesInts(int8_t x, int16_t y, int32_t z, int64_t t, long u) {
int8_t _x = (int8_t)x;
int16_t _y = (int16_t)y;
int32_t _z = (int32_t)z;
int64_t _t = (int64_t)t;
nint _u = (nint)u;
proxybasictypes__Ints(_x, _y, _z, _t, _u);
}
__attribute__((constructor)) static void init() {
init_seq();
}

171
bind/testdata/benchmark/benchmark.go vendored Normal file
View file

@ -0,0 +1,171 @@
// Copyright 2016 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 benchmark contains benchmarking bound functions for internal use.
package benchmark
import (
"log"
"time"
)
type Benchmarks interface {
// It seems to be much faster to call a native function from Java
// when there is already a native call earlier in the stack.
// Run runs a named benchmark from a different thread, with
// no native call prior in the stack.
Run(name string, n int)
// RunDirect runs a named benchmark directly, with the native
// context from the call itself.
RunDirect(name string, n int)
// Callbacks for Go benchmarks
NewI() I
Noargs()
Onearg(_ int)
Oneret() int
Ref(_ I)
Manyargs(_, _, _, _, _, _, _, _, _, _ int)
String(_ string)
StringRetShort() string
StringRetLong() string
Slice(_ []byte)
}
type (
I interface {
F()
}
AnI struct {
}
)
func (_ *AnI) F() {
}
func NewI() I {
return new(AnI)
}
func runBenchmark(name string, f func(n int)) {
// Run once for warmup
f(1)
n := 1000
var dt time.Duration
minDuration := 1 * time.Second
for dt < minDuration {
n *= 2
t0 := time.Now()
f(n)
dt = time.Since(t0)
}
log.Printf("Benchmark%s %d %d ns/op\n", name, n, dt.Nanoseconds()/int64(n))
}
func runGoBenchmark(name string, f func()) {
runBenchmark("Go"+name, func(n int) {
for i := 0; i < n; i++ {
f()
}
})
runBenchmark("Go"+name+"Direct", func(n int) {
done := make(chan struct{})
go func() {
for i := 0; i < n; i++ {
f()
}
close(done)
}()
<-done
})
}
func RunBenchmarks(b Benchmarks) {
names := []string{
"Empty",
"Noargs",
"Onearg",
"Oneret",
"Manyargs",
"Refforeign",
"Refgo",
"StringShort",
"StringLong",
"StringShortUnicode",
"StringLongUnicode",
"StringRetShort",
"StringRetLong",
"SliceShort",
"SliceLong",
}
for _, name := range names {
runBenchmark("Foreign"+name, func(n int) {
b.Run(name, n)
})
runBenchmark("Foreign"+name+"Direct", func(n int) {
b.RunDirect(name, n)
})
}
runGoBenchmark("Empty", func() {})
runGoBenchmark("Noarg", func() { b.Noargs() })
runGoBenchmark("Onearg", func() { b.Onearg(0) })
runGoBenchmark("Oneret", func() { b.Oneret() })
foreignRef := b.NewI()
runGoBenchmark("Refforeign", func() { b.Ref(foreignRef) })
goRef := NewI()
runGoBenchmark("Refgo", func() { b.Ref(goRef) })
runGoBenchmark("Manyargs", func() { b.Manyargs(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) })
runGoBenchmark("StringShort", func() { b.String(ShortString) })
runGoBenchmark("StringLong", func() { b.String(LongString) })
runGoBenchmark("StringShortUnicode", func() { b.String(ShortStringUnicode) })
runGoBenchmark("StringLongUnicode", func() { b.String(LongStringUnicode) })
runGoBenchmark("StringRetShort", func() { b.StringRetShort() })
runGoBenchmark("StringRetLong", func() { b.StringRetLong() })
runGoBenchmark("SliceShort", func() { b.Slice(ShortSlice) })
runGoBenchmark("SliceLong", func() { b.Slice(LongSlice) })
}
func Noargs() {
}
func Onearg(_ int) {
}
func Manyargs(_, _, _, _, _, _, _, _, _, _ int) {
}
func Oneret() int {
return 0
}
func String(_ string) {
}
func StringRetShort() string {
return ShortString
}
func StringRetLong() string {
return LongString
}
func Slice(_ []byte) {
}
func Ref(_ I) {
}
var (
ShortSlice = make([]byte, 10)
LongSlice = make([]byte, 100000)
)
const (
ShortString = "Hello, World!"
LongString = "Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!, World!"
ShortStringUnicode = "Hello, 世界!"
LongStringUnicode = "Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界! Hello, 世界!, 世界!"
)

9
bind/testdata/cgopkg/cgopkg.go vendored Normal file
View file

@ -0,0 +1,9 @@
package cgopkg
import "C"
import (
_ "golang.org/x/mobile/gl"
)
func Dummy() {}

62
bind/testdata/classes.go vendored Normal file
View file

@ -0,0 +1,62 @@
// Copyright 2014 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 java
import (
gopkg "Java/java"
"Java/java/io"
"Java/java/lang"
"Java/java/lang/System"
"Java/java/util/Spliterators"
"Java/java/util/concurrent"
)
type Runnable struct {
lang.Runnable
}
func (r *Runnable) Run(this gopkg.Runnable) {
}
type InputStream struct {
io.InputStream
}
func (_ *InputStream) Read() (int32, error) {
return 0, nil
}
func NewInputStream() *InputStream {
return new(InputStream)
}
type Future struct {
concurrent.Future
}
func (_ *Future) Get() (lang.Object, error) {
return nil, nil
}
// Use a trailing underscore to override multiple overloaded methods.
func (_ *Future) Get_(_ int64, _ concurrent.TimeUnit) (lang.Object, error) {
return nil, nil
}
type Object struct {
lang.Object
}
func innerClassTypes() {
// java.util.Spliterators.iterator use inner class types
// for the return value as well as parameters.
Spliterators.Iterator(nil)
}
func returnType() {
// Implicit types (java.io.Console) should be wrapped.
cons := System.Console()
cons.Flush()
}

2136
bind/testdata/classes.go.golden vendored Normal file

File diff suppressed because it is too large Load diff

892
bind/testdata/classes.java.c.golden vendored Normal file
View file

@ -0,0 +1,892 @@
// Code generated by gobind. DO NOT EDIT.
#include <jni.h>
#include "seq.h"
#include "classes.h"
static jclass class_java_lang_Runnable;
static jmethodID m_java_lang_Runnable_run;
static jclass class_java_io_InputStream;
static jmethodID m_java_io_InputStream_read__;
static jmethodID m_java_io_InputStream_read___3B;
static jmethodID m_java_io_InputStream_read___3BII;
static jmethodID m_java_io_InputStream_toString;
static jclass class_java_util_concurrent_Future;
static jmethodID m_java_util_concurrent_Future_get__;
static jmethodID m_java_util_concurrent_Future_get__JLjava_util_concurrent_TimeUnit_2;
static jclass class_java_lang_Object;
static jmethodID m_java_lang_Object_toString;
static jclass class_java_util_concurrent_TimeUnit;
static jmethodID m_java_util_concurrent_TimeUnit_toString;
static jclass class_java_util_Spliterators;
static jmethodID m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2;
static jmethodID m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2;
static jmethodID m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2;
static jmethodID m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2;
static jmethodID m_java_util_Spliterators_toString;
ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2(jint a0) {
JNIEnv *env = go_seq_push_local_frame(1);
jobject _a0 = go_seq_from_refnum(env, a0, NULL, NULL);
jobject res = (*env)->CallStaticObjectMethod(env, class_java_util_Spliterators, m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2, _a0);
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_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2(jint a0) {
JNIEnv *env = go_seq_push_local_frame(1);
jobject _a0 = go_seq_from_refnum(env, a0, NULL, NULL);
jobject res = (*env)->CallStaticObjectMethod(env, class_java_util_Spliterators, m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2, _a0);
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_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2(jint a0) {
JNIEnv *env = go_seq_push_local_frame(1);
jobject _a0 = go_seq_from_refnum(env, a0, NULL, NULL);
jobject res = (*env)->CallStaticObjectMethod(env, class_java_util_Spliterators, m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2, _a0);
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_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2(jint a0) {
JNIEnv *env = go_seq_push_local_frame(1);
jobject _a0 = go_seq_from_refnum(env, a0, NULL, NULL);
jobject res = (*env)->CallStaticObjectMethod(env, class_java_util_Spliterators, m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2, _a0);
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;
}
static jclass class_java_lang_System;
static jmethodID m_s_java_lang_System_console;
static jmethodID m_java_lang_System_toString;
ret_jint cproxy_s_java_lang_System_console() {
JNIEnv *env = go_seq_push_local_frame(0);
jobject res = (*env)->CallStaticObjectMethod(env, class_java_lang_System, m_s_java_lang_System_console);
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;
}
static jclass class_java_Future;
static jclass sclass_java_Future;
static jmethodID m_java_Future_get__;
static jmethodID sm_java_Future_get__;
static jmethodID m_java_Future_get__JLjava_util_concurrent_TimeUnit_2;
static jmethodID sm_java_Future_get__JLjava_util_concurrent_TimeUnit_2;
static jclass class_java_InputStream;
static jclass sclass_java_InputStream;
static jmethodID m_java_InputStream_read__;
static jmethodID sm_java_InputStream_read__;
static jmethodID m_java_InputStream_read___3B;
static jmethodID sm_java_InputStream_read___3B;
static jmethodID m_java_InputStream_read___3BII;
static jmethodID sm_java_InputStream_read___3BII;
static jmethodID m_java_InputStream_toString;
static jmethodID sm_java_InputStream_toString;
static jclass class_java_Object;
static jclass sclass_java_Object;
static jmethodID m_java_Object_toString;
static jmethodID sm_java_Object_toString;
static jclass class_java_Runnable;
static jclass sclass_java_Runnable;
static jmethodID m_java_Runnable_run;
static jmethodID sm_java_Runnable_run;
static jclass class_java_util_Iterator;
static jclass class_java_util_Spliterator;
static jclass class_java_util_PrimitiveIterator_OfInt;
static jclass class_java_util_Spliterator_OfInt;
static jclass class_java_util_PrimitiveIterator_OfLong;
static jclass class_java_util_Spliterator_OfLong;
static jclass class_java_util_PrimitiveIterator_OfDouble;
static jclass class_java_util_Spliterator_OfDouble;
static jclass class_java_io_Console;
static jmethodID m_java_io_Console_flush;
static jmethodID m_java_io_Console_toString;
void init_proxies() {
JNIEnv *env = go_seq_push_local_frame(20);
jclass clazz;
clazz = go_seq_find_class("java/lang/Runnable");
if (clazz != NULL) {
class_java_lang_Runnable = (*env)->NewGlobalRef(env, clazz);
m_java_lang_Runnable_run = go_seq_get_method_id(clazz, "run", "()V");
}
clazz = go_seq_find_class("java/io/InputStream");
if (clazz != NULL) {
class_java_io_InputStream = (*env)->NewGlobalRef(env, clazz);
m_java_io_InputStream_read__ = go_seq_get_method_id(clazz, "read", "()I");
m_java_io_InputStream_read___3B = go_seq_get_method_id(clazz, "read", "([B)I");
m_java_io_InputStream_read___3BII = go_seq_get_method_id(clazz, "read", "([BII)I");
m_java_io_InputStream_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
}
clazz = go_seq_find_class("java/util/concurrent/Future");
if (clazz != NULL) {
class_java_util_concurrent_Future = (*env)->NewGlobalRef(env, clazz);
m_java_util_concurrent_Future_get__ = go_seq_get_method_id(clazz, "get", "()Ljava/lang/Object;");
m_java_util_concurrent_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(clazz, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
}
clazz = go_seq_find_class("java/lang/Object");
if (clazz != NULL) {
class_java_lang_Object = (*env)->NewGlobalRef(env, clazz);
m_java_lang_Object_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
}
clazz = go_seq_find_class("java/util/concurrent/TimeUnit");
if (clazz != NULL) {
class_java_util_concurrent_TimeUnit = (*env)->NewGlobalRef(env, clazz);
m_java_util_concurrent_TimeUnit_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
}
clazz = go_seq_find_class("java/util/Spliterators");
if (clazz != NULL) {
class_java_util_Spliterators = (*env)->NewGlobalRef(env, clazz);
m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator;)Ljava/util/Iterator;");
m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfInt;)Ljava/util/PrimitiveIterator$OfInt;");
m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfLong;)Ljava/util/PrimitiveIterator$OfLong;");
m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfDouble;)Ljava/util/PrimitiveIterator$OfDouble;");
m_java_util_Spliterators_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
}
clazz = go_seq_find_class("java/lang/System");
if (clazz != NULL) {
class_java_lang_System = (*env)->NewGlobalRef(env, clazz);
m_s_java_lang_System_console = go_seq_get_static_method_id(clazz, "console", "()Ljava/io/Console;");
m_java_lang_System_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
}
clazz = go_seq_find_class("java/Future");
if (clazz != NULL) {
class_java_Future = (*env)->NewGlobalRef(env, clazz);
sclass_java_Future = (*env)->GetSuperclass(env, clazz);
sclass_java_Future = (*env)->NewGlobalRef(env, sclass_java_Future);
m_java_Future_get__ = go_seq_get_method_id(clazz, "get", "()Ljava/lang/Object;");
sm_java_Future_get__ = go_seq_get_method_id(sclass_java_Future, "get", "()Ljava/lang/Object;");
m_java_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(clazz, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
sm_java_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(sclass_java_Future, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
}
clazz = go_seq_find_class("java/InputStream");
if (clazz != NULL) {
class_java_InputStream = (*env)->NewGlobalRef(env, clazz);
sclass_java_InputStream = (*env)->GetSuperclass(env, clazz);
sclass_java_InputStream = (*env)->NewGlobalRef(env, sclass_java_InputStream);
m_java_InputStream_read__ = go_seq_get_method_id(clazz, "read", "()I");
sm_java_InputStream_read__ = go_seq_get_method_id(sclass_java_InputStream, "read", "()I");
m_java_InputStream_read___3B = go_seq_get_method_id(clazz, "read", "([B)I");
sm_java_InputStream_read___3B = go_seq_get_method_id(sclass_java_InputStream, "read", "([B)I");
m_java_InputStream_read___3BII = go_seq_get_method_id(clazz, "read", "([BII)I");
sm_java_InputStream_read___3BII = go_seq_get_method_id(sclass_java_InputStream, "read", "([BII)I");
m_java_InputStream_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
sm_java_InputStream_toString = go_seq_get_method_id(sclass_java_InputStream, "toString", "()Ljava/lang/String;");
}
clazz = go_seq_find_class("java/Object");
if (clazz != NULL) {
class_java_Object = (*env)->NewGlobalRef(env, clazz);
sclass_java_Object = (*env)->GetSuperclass(env, clazz);
sclass_java_Object = (*env)->NewGlobalRef(env, sclass_java_Object);
m_java_Object_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
sm_java_Object_toString = go_seq_get_method_id(sclass_java_Object, "toString", "()Ljava/lang/String;");
}
clazz = go_seq_find_class("java/Runnable");
if (clazz != NULL) {
class_java_Runnable = (*env)->NewGlobalRef(env, clazz);
sclass_java_Runnable = (*env)->GetSuperclass(env, clazz);
sclass_java_Runnable = (*env)->NewGlobalRef(env, sclass_java_Runnable);
m_java_Runnable_run = go_seq_get_method_id(clazz, "run", "()V");
sm_java_Runnable_run = go_seq_get_method_id(sclass_java_Runnable, "run", "()V");
}
clazz = go_seq_find_class("java/util/Iterator");
if (clazz != NULL) {
class_java_util_Iterator = (*env)->NewGlobalRef(env, clazz);
}
clazz = go_seq_find_class("java/util/Spliterator");
if (clazz != NULL) {
class_java_util_Spliterator = (*env)->NewGlobalRef(env, clazz);
}
clazz = go_seq_find_class("java/util/PrimitiveIterator$OfInt");
if (clazz != NULL) {
class_java_util_PrimitiveIterator_OfInt = (*env)->NewGlobalRef(env, clazz);
}
clazz = go_seq_find_class("java/util/Spliterator$OfInt");
if (clazz != NULL) {
class_java_util_Spliterator_OfInt = (*env)->NewGlobalRef(env, clazz);
}
clazz = go_seq_find_class("java/util/PrimitiveIterator$OfLong");
if (clazz != NULL) {
class_java_util_PrimitiveIterator_OfLong = (*env)->NewGlobalRef(env, clazz);
}
clazz = go_seq_find_class("java/util/Spliterator$OfLong");
if (clazz != NULL) {
class_java_util_Spliterator_OfLong = (*env)->NewGlobalRef(env, clazz);
}
clazz = go_seq_find_class("java/util/PrimitiveIterator$OfDouble");
if (clazz != NULL) {
class_java_util_PrimitiveIterator_OfDouble = (*env)->NewGlobalRef(env, clazz);
}
clazz = go_seq_find_class("java/util/Spliterator$OfDouble");
if (clazz != NULL) {
class_java_util_Spliterator_OfDouble = (*env)->NewGlobalRef(env, clazz);
}
clazz = go_seq_find_class("java/io/Console");
if (clazz != NULL) {
class_java_io_Console = (*env)->NewGlobalRef(env, clazz);
m_java_io_Console_flush = go_seq_get_method_id(clazz, "flush", "()V");
m_java_io_Console_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
}
go_seq_pop_local_frame(env);
}
jint cproxy_java_lang_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_java_lang_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;
}
ret_jint cproxy_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)->CallIntMethod(env, _this, 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_io_InputStream_read___3B(jint this, nbyteslice a0) {
JNIEnv *env = go_seq_push_local_frame(2);
// Must be a Java object
jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
jint res = (*env)->CallIntMethod(env, _this, m_java_io_InputStream_read___3B, _a0);
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_io_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2) {
JNIEnv *env = go_seq_push_local_frame(4);
// Must be a Java object
jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
jint _a1 = a1;
jint _a2 = a2;
jint res = (*env)->CallIntMethod(env, _this, m_java_io_InputStream_read___3BII, _a0, _a1, _a2);
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_nstring cproxy_java_io_InputStream_toString(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);
jstring res = (*env)->CallObjectMethod(env, _this, m_java_io_InputStream_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __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
jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
jobject res = (*env)->CallObjectMethod(env, _this, m_java_util_concurrent_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_java_util_concurrent_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_java_util_concurrent_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_nstring cproxy_java_lang_Object_toString(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);
jstring res = (*env)->CallObjectMethod(env, _this, m_java_lang_Object_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __res = {_res, _exc_ref};
return __res;
}
ret_nstring cproxy_java_util_concurrent_TimeUnit_toString(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);
jstring res = (*env)->CallObjectMethod(env, _this, m_java_util_concurrent_TimeUnit_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __res = {_res, _exc_ref};
return __res;
}
ret_nstring cproxy_java_util_Spliterators_toString(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);
jstring res = (*env)->CallObjectMethod(env, _this, m_java_util_Spliterators_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __res = {_res, _exc_ref};
return __res;
}
ret_nstring cproxy_java_lang_System_toString(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);
jstring res = (*env)->CallObjectMethod(env, _this, m_java_lang_System_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __res = {_res, _exc_ref};
return __res;
}
ret_jint cproxy_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_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_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_java_Future, sm_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_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_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_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_java_Future, sm_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_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_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_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_java_InputStream, sm_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 cproxy_java_InputStream_read___3B(jint this, nbyteslice a0) {
JNIEnv *env = go_seq_push_local_frame(2);
// Must be a Java object
jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
jint res = (*env)->CallIntMethod(env, _this, m_java_InputStream_read___3B, _a0);
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_java_InputStream_read___3B(jint this, nbyteslice a0) {
JNIEnv *env = go_seq_push_local_frame(2);
// Must be a Java object
jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
jint res = (*env)->CallNonvirtualIntMethod(env, _this, sclass_java_InputStream, sm_java_InputStream_read___3B, _a0);
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_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2) {
JNIEnv *env = go_seq_push_local_frame(4);
// Must be a Java object
jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
jint _a1 = a1;
jint _a2 = a2;
jint res = (*env)->CallIntMethod(env, _this, m_java_InputStream_read___3BII, _a0, _a1, _a2);
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_java_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2) {
JNIEnv *env = go_seq_push_local_frame(4);
// Must be a Java object
jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
jint _a1 = a1;
jint _a2 = a2;
jint res = (*env)->CallNonvirtualIntMethod(env, _this, sclass_java_InputStream, sm_java_InputStream_read___3BII, _a0, _a1, _a2);
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_nstring cproxy_java_InputStream_toString(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);
jstring res = (*env)->CallObjectMethod(env, _this, m_java_InputStream_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __res = {_res, _exc_ref};
return __res;
}
ret_nstring csuper_java_InputStream_toString(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);
jstring res = (*env)->CallNonvirtualObjectMethod(env, _this, sclass_java_InputStream, sm_java_InputStream_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __res = {_res, _exc_ref};
return __res;
}
ret_nstring cproxy_java_Object_toString(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);
jstring res = (*env)->CallObjectMethod(env, _this, m_java_Object_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __res = {_res, _exc_ref};
return __res;
}
ret_nstring csuper_java_Object_toString(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);
jstring res = (*env)->CallNonvirtualObjectMethod(env, _this, sclass_java_Object, sm_java_Object_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __res = {_res, _exc_ref};
return __res;
}
jint cproxy_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_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_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_java_Runnable, sm_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 cproxy_java_io_Console_flush(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_java_io_Console_flush);
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;
}
ret_nstring cproxy_java_io_Console_toString(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);
jstring res = (*env)->CallObjectMethod(env, _this, m_java_io_Console_toString);
jobject _exc = go_seq_get_exception(env);
int32_t _exc_ref = go_seq_to_refnum(env, _exc);
if (_exc != NULL) {
res = NULL;
}
nstring _res = go_seq_from_java_string(env, res);
go_seq_pop_local_frame(env);
ret_nstring __res = {_res, _exc_ref};
return __res;
}
// Code generated by gobind. DO NOT EDIT.
// JNI functions for the Go <=> Java bridge.
//
// autogenerated by gobind -lang=java classes
#include <android/log.h>
#include <stdint.h>
#include "seq.h"
#include "_cgo_export.h"
#include "java.h"
jclass proxy_class_java_Future;
jmethodID proxy_class_java_Future_cons;
jclass proxy_class_java_InputStream;
jmethodID proxy_class_java_InputStream_cons;
jclass proxy_class_java_Object;
jmethodID proxy_class_java_Object_cons;
jclass proxy_class_java_Runnable;
jmethodID proxy_class_java_Runnable_cons;
JNIEXPORT void JNICALL
Java_java_Java__1init(JNIEnv *env, jclass _unused) {
jclass clazz;
clazz = (*env)->FindClass(env, "java/Future");
proxy_class_java_Future = (*env)->NewGlobalRef(env, clazz);
proxy_class_java_Future_cons = (*env)->GetMethodID(env, clazz, "<init>", "(I)V");
clazz = (*env)->FindClass(env, "java/Object");
proxy_class_java_Object = (*env)->NewGlobalRef(env, clazz);
proxy_class_java_Object_cons = (*env)->GetMethodID(env, clazz, "<init>", "(I)V");
clazz = (*env)->FindClass(env, "java/Runnable");
proxy_class_java_Runnable = (*env)->NewGlobalRef(env, clazz);
proxy_class_java_Runnable_cons = (*env)->GetMethodID(env, clazz, "<init>", "(I)V");
}
JNIEXPORT jobject JNICALL
Java_java_Java_newInputStream(JNIEnv* env, jclass _clazz) {
int32_t r0 = proxyjava__NewInputStream();
jobject _r0 = go_seq_from_refnum(env, r0, proxy_class_java_InputStream, proxy_class_java_InputStream_cons);
return _r0;
}
JNIEXPORT jint JNICALL
Java_java_Future__1_1New(JNIEnv *env, jclass clazz) {
return new_java_Future();
}
JNIEXPORT jobject JNICALL
Java_java_Future_get__(JNIEnv* env, jobject __this__) {
int32_t o = go_seq_to_refnum_go(env, __this__);
struct proxyjava_Future_Get_return res = proxyjava_Future_Get(o);
jobject _r0 = go_seq_from_refnum(env, res.r0, NULL, NULL);
jobject _r1 = go_seq_from_refnum(env, res.r1, proxy_class__error, proxy_class__error_cons);
go_seq_maybe_throw_exception(env, _r1);
return _r0;
}
JNIEXPORT jobject JNICALL
Java_java_Future_get__JLjava_util_concurrent_TimeUnit_2(JNIEnv* env, jobject __this__, jlong p0, jobject p1) {
int32_t o = go_seq_to_refnum_go(env, __this__);
int64_t _p0 = (int64_t)p0;
int32_t _p1 = go_seq_to_refnum(env, p1);
struct proxyjava_Future_Get__return res = proxyjava_Future_Get_(o, _p0, _p1);
jobject _r0 = go_seq_from_refnum(env, res.r0, NULL, NULL);
jobject _r1 = go_seq_from_refnum(env, res.r1, proxy_class__error, proxy_class__error_cons);
go_seq_maybe_throw_exception(env, _r1);
return _r0;
}
JNIEXPORT void JNICALL
Java_java_Future_setFuture(JNIEnv *env, jobject this, jobject v) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t _v = go_seq_to_refnum(env, v);
proxyjava_Future_Future_Set(o, _v);
}
JNIEXPORT jobject JNICALL
Java_java_Future_getFuture(JNIEnv *env, jobject this) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t r0 = proxyjava_Future_Future_Get(o);
jobject _r0 = go_seq_from_refnum(env, r0, NULL, NULL);
return _r0;
}
JNIEXPORT jint JNICALL
Java_java_InputStream__1_1NewInputStream(JNIEnv *env, jclass clazz) {
int32_t refnum = proxyjava__NewInputStream();
return refnum;
}
JNIEXPORT jint JNICALL
Java_java_InputStream_read__(JNIEnv* env, jobject __this__) {
int32_t o = go_seq_to_refnum_go(env, __this__);
struct proxyjava_InputStream_Read_return res = proxyjava_InputStream_Read(o);
jint _r0 = (jint)res.r0;
jobject _r1 = go_seq_from_refnum(env, res.r1, proxy_class__error, proxy_class__error_cons);
go_seq_maybe_throw_exception(env, _r1);
return _r0;
}
JNIEXPORT void JNICALL
Java_java_InputStream_setInputStream(JNIEnv *env, jobject this, jobject v) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t _v = go_seq_to_refnum(env, v);
proxyjava_InputStream_InputStream_Set(o, _v);
}
JNIEXPORT jobject JNICALL
Java_java_InputStream_getInputStream(JNIEnv *env, jobject this) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t r0 = proxyjava_InputStream_InputStream_Get(o);
jobject _r0 = go_seq_from_refnum(env, r0, NULL, NULL);
return _r0;
}
JNIEXPORT jint JNICALL
Java_java_Object__1_1New(JNIEnv *env, jclass clazz) {
return new_java_Object();
}
JNIEXPORT void JNICALL
Java_java_Object_setObject(JNIEnv *env, jobject this, jobject v) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t _v = go_seq_to_refnum(env, v);
proxyjava_Object_Object_Set(o, _v);
}
JNIEXPORT jobject JNICALL
Java_java_Object_getObject(JNIEnv *env, jobject this) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t r0 = proxyjava_Object_Object_Get(o);
jobject _r0 = go_seq_from_refnum(env, r0, NULL, NULL);
return _r0;
}
JNIEXPORT jint JNICALL
Java_java_Runnable__1_1New(JNIEnv *env, jclass clazz) {
return new_java_Runnable();
}
JNIEXPORT void JNICALL
Java_java_Runnable_run(JNIEnv* env, jobject __this__) {
int32_t o = go_seq_to_refnum_go(env, __this__);
int32_t _this_ = go_seq_to_refnum(env, __this__);
proxyjava_Runnable_Run(o, _this_);
}
JNIEXPORT void JNICALL
Java_java_Runnable_setRunnable(JNIEnv *env, jobject this, jobject v) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t _v = go_seq_to_refnum(env, v);
proxyjava_Runnable_Runnable_Set(o, _v);
}
JNIEXPORT jobject JNICALL
Java_java_Runnable_getRunnable(JNIEnv *env, jobject this) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t r0 = proxyjava_Runnable_Runnable_Get(o);
jobject _r0 = go_seq_from_refnum(env, r0, NULL, NULL);
return _r0;
}

155
bind/testdata/classes.java.golden vendored Normal file
View file

@ -0,0 +1,155 @@
// Code generated by gobind. DO NOT EDIT.
// Java class java.Future is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java classes
package java;
import go.Seq;
public final class Future implements Seq.GoObject, java.util.concurrent.Future {
static { Java.touch(); }
private final int refnum;
@Override public final int incRefnum() {
Seq.incGoRef(refnum, this);
return refnum;
}
Future(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); }
public Future() { this.refnum = __New(); Seq.trackGoRef(refnum, this); }
private static native int __New();
public final native java.util.concurrent.Future getFuture();
public final native void setFuture(java.util.concurrent.Future v);
@Override public native java.lang.Object get() throws java.lang.InterruptedException, java.util.concurrent.ExecutionException;
/**
* Use a trailing underscore to override multiple overloaded methods.
*/
@Override public native java.lang.Object get(long p0, java.util.concurrent.TimeUnit p1) throws java.lang.InterruptedException, java.util.concurrent.ExecutionException, java.util.concurrent.TimeoutException;
}
// Code generated by gobind. DO NOT EDIT.
// Java class java.InputStream is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java classes
package java;
import go.Seq;
public final class InputStream extends java.io.InputStream implements Seq.GoObject {
static { Java.touch(); }
private final int refnum;
@Override public final int incRefnum() {
Seq.incGoRef(refnum, this);
return refnum;
}
public InputStream() {
super();
this.refnum = __NewInputStream();
Seq.trackGoRef(refnum, this);
}
private static native int __NewInputStream();
public final native java.io.InputStream getInputStream();
public final native void setInputStream(java.io.InputStream v);
@Override public native int read() throws java.io.IOException;
}
// Code generated by gobind. DO NOT EDIT.
// Java class java.Object is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java classes
package java;
import go.Seq;
public final class Object extends java.lang.Object implements Seq.GoObject {
static { Java.touch(); }
private final int refnum;
@Override public final int incRefnum() {
Seq.incGoRef(refnum, this);
return refnum;
}
Object(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); }
public Object() { this.refnum = __New(); Seq.trackGoRef(refnum, this); }
private static native int __New();
public final native java.lang.Object getObject();
public final native void setObject(java.lang.Object v);
}
// Code generated by gobind. DO NOT EDIT.
// Java class java.Runnable is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java classes
package java;
import go.Seq;
public final class Runnable implements Seq.GoObject, java.lang.Runnable {
static { Java.touch(); }
private final int refnum;
@Override public final int incRefnum() {
Seq.incGoRef(refnum, this);
return refnum;
}
Runnable(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); }
public Runnable() { this.refnum = __New(); Seq.trackGoRef(refnum, this); }
private static native int __New();
public final native java.lang.Runnable getRunnable();
public final native void setRunnable(java.lang.Runnable v);
@Override public native void run();
}
// Code generated by gobind. DO NOT EDIT.
// Java class java.Java is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java classes
package java;
import go.Seq;
public abstract class Java {
static {
Seq.touch(); // for loading the native library
_init();
}
private Java() {} // uninstantiable
// touch is called from other bound packages to initialize this package
public static void touch() {}
private static native void _init();
public static native InputStream newInputStream();
}

103
bind/testdata/classes.java.h.golden vendored Normal file
View file

@ -0,0 +1,103 @@
// Code generated by gobind. DO NOT EDIT.
#include <jni.h>
#include "seq.h"
extern void init_proxies();
typedef struct ret_jint {
jint res;
jint exc;
} ret_jint;
typedef struct ret_jboolean {
jboolean res;
jint exc;
} ret_jboolean;
typedef struct ret_jshort {
jshort res;
jint exc;
} ret_jshort;
typedef struct ret_jchar {
jchar res;
jint exc;
} ret_jchar;
typedef struct ret_jbyte {
jbyte res;
jint exc;
} ret_jbyte;
typedef struct ret_jlong {
jlong res;
jint exc;
} ret_jlong;
typedef struct ret_jfloat {
jfloat res;
jint exc;
} ret_jfloat;
typedef struct ret_jdouble {
jdouble res;
jint exc;
} ret_jdouble;
typedef struct ret_nstring {
nstring res;
jint exc;
} ret_nstring;
typedef struct ret_nbyteslice {
nbyteslice res;
jint exc;
} ret_nbyteslice;
extern jint cproxy_java_lang_Runnable_run(jint this);
extern ret_jint cproxy_java_io_InputStream_read__(jint this);
extern ret_jint cproxy_java_io_InputStream_read___3B(jint this, nbyteslice a0);
extern ret_jint cproxy_java_io_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2);
extern ret_nstring cproxy_java_io_InputStream_toString(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_nstring cproxy_java_lang_Object_toString(jint this);
extern ret_nstring cproxy_java_util_concurrent_TimeUnit_toString(jint this);
extern ret_nstring cproxy_java_util_Spliterators_toString(jint this);
extern ret_nstring cproxy_java_lang_System_toString(jint this);
extern ret_jint cproxy_java_Future_get__(jint this);
extern ret_jint csuper_java_Future_get__(jint this);
extern ret_jint cproxy_java_Future_get__JLjava_util_concurrent_TimeUnit_2(jint this, jlong a0, jint a1);
extern ret_jint csuper_java_Future_get__JLjava_util_concurrent_TimeUnit_2(jint this, jlong a0, jint a1);
extern ret_jint cproxy_java_InputStream_read__(jint this);
extern ret_jint csuper_java_InputStream_read__(jint this);
extern ret_jint cproxy_java_InputStream_read___3B(jint this, nbyteslice a0);
extern ret_jint csuper_java_InputStream_read___3B(jint this, nbyteslice a0);
extern ret_jint cproxy_java_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2);
extern ret_jint csuper_java_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2);
extern ret_nstring cproxy_java_InputStream_toString(jint this);
extern ret_nstring csuper_java_InputStream_toString(jint this);
extern ret_nstring cproxy_java_Object_toString(jint this);
extern ret_nstring csuper_java_Object_toString(jint this);
extern jint cproxy_java_Runnable_run(jint this);
extern jint csuper_java_Runnable_run(jint this);
extern jint cproxy_java_io_Console_flush(jint this);
extern ret_nstring cproxy_java_io_Console_toString(jint this);
extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2(jint a0);
extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2(jint a0);
extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2(jint a0);
extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2(jint a0);
extern ret_jint cproxy_s_java_lang_System_console();
// Code generated by gobind. DO NOT EDIT.
// JNI function headers for the Go <=> Java bridge.
//
// autogenerated by gobind -lang=java classes
#ifndef __Java_H__
#define __Java_H__
#include <jni.h>
extern jclass proxy_class_java_Future;
extern jmethodID proxy_class_java_Future_cons;
extern jclass proxy_class_java_InputStream;
extern jmethodID proxy_class_java_InputStream_cons;
extern jclass proxy_class_java_Object;
extern jmethodID proxy_class_java_Object_cons;
extern jclass proxy_class_java_Runnable;
extern jmethodID proxy_class_java_Runnable_cons;
#endif

10
bind/testdata/customprefix.go vendored Normal file
View file

@ -0,0 +1,10 @@
// 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.
// Data for -pkgpath and -prefix options.
package customprefix
func F() {
}

View file

@ -0,0 +1,23 @@
// Code generated by gobind. DO NOT EDIT.
// JNI functions for the Go <=> Java bridge.
//
// autogenerated by gobind -lang=java -javapkg=com.example customprefix
#include <android/log.h>
#include <stdint.h>
#include "seq.h"
#include "_cgo_export.h"
#include "customprefix.h"
JNIEXPORT void JNICALL
Java_com_example_customprefix_Customprefix__1init(JNIEnv *env, jclass _unused) {
jclass clazz;
}
JNIEXPORT void JNICALL
Java_com_example_customprefix_Customprefix_f(JNIEnv* env, jclass _clazz) {
proxycustomprefix__F();
}

26
bind/testdata/customprefix.java.golden vendored Normal file
View file

@ -0,0 +1,26 @@
// Code generated by gobind. DO NOT EDIT.
// Java class com.example.customprefix.Customprefix is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java -javapkg=com.example customprefix
package com.example.customprefix;
import go.Seq;
public abstract class Customprefix {
static {
Seq.touch(); // for loading the native library
_init();
}
private Customprefix() {} // 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 f();
}

View file

@ -0,0 +1,12 @@
// Code generated by gobind. DO NOT EDIT.
// JNI function headers for the Go <=> Java bridge.
//
// autogenerated by gobind -lang=java -javapkg=com.example customprefix
#ifndef __Customprefix_H__
#define __Customprefix_H__
#include <jni.h>
#endif

View file

@ -0,0 +1,11 @@
// Objective-C API for talking to customprefix Go package.
// gobind -lang=objc customprefix
//
// File is generated by gobind. Do not edit.
#ifndef __GO_customprefix_H__
#define __GO_customprefix_H__
#include <stdint.h>
#include <objc/objc.h>
#endif

View file

@ -0,0 +1,16 @@
// Objective-C API for talking to customprefix Go package.
// gobind -lang=objc customprefix
//
// File is generated by gobind. Do not edit.
#ifndef __Customprefix_H__
#define __Customprefix_H__
@import Foundation;
#include "ref.h"
#include "Universe.objc.h"
FOUNDATION_EXPORT void CustomprefixF(void);
#endif

View file

@ -0,0 +1,18 @@
// Objective-C API for talking to customprefix Go package.
// gobind -lang=objc customprefix
//
// File is generated by gobind. Do not edit.
#include <Foundation/Foundation.h>
#include "seq.h"
#include "_cgo_export.h"
#include "Customprefix.objc.h"
void CustomprefixF(void) {
proxycustomprefix__F();
}
__attribute__((constructor)) static void init() {
init_seq();
}

View file

@ -0,0 +1,11 @@
// Objective-C API for talking to customprefix Go package.
// gobind -lang=objc -prefix="EX" customprefix
//
// File is generated by gobind. Do not edit.
#ifndef __GO_customprefix_H__
#define __GO_customprefix_H__
#include <stdint.h>
#include <objc/objc.h>
#endif

View file

@ -0,0 +1,16 @@
// Objective-C API for talking to customprefix Go package.
// gobind -lang=objc -prefix="EX" customprefix
//
// File is generated by gobind. Do not edit.
#ifndef __EXCustomprefix_H__
#define __EXCustomprefix_H__
@import Foundation;
#include "ref.h"
#include "Universe.objc.h"
FOUNDATION_EXPORT void EXCustomprefixF(void);
#endif

View file

@ -0,0 +1,18 @@
// Objective-C API for talking to customprefix Go package.
// gobind -lang=objc -prefix="EX" customprefix
//
// File is generated by gobind. Do not edit.
#include <Foundation/Foundation.h>
#include "seq.h"
#include "_cgo_export.h"
#include "EXCustomprefix.objc.h"
void EXCustomprefixF(void) {
proxycustomprefix__F();
}
__attribute__((constructor)) static void init() {
init_seq();
}

59
bind/testdata/doc.go vendored Normal file
View file

@ -0,0 +1,59 @@
// Copyright 2016 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 doc tests that Go documentation is transferred
// to the generated code.
package doc
// F is a function.
func F() {}
// C is a constant.
const C = true
// V is a var.
var V string
// A group of vars.
var (
// A specific var.
Specific string
NoDocVar float64
)
// Before is a method.
func (_ *S) Before() {}
// S is a struct.
type S struct {
// SF is a field.
SF string
// blank (unexported) field.
_ string
// Anonymous field.
*S2
// Multiple fields.
F1, F2 string
}
// After is another method.
func (_ *S) After() {}
// A generic comment with <HTML>.
type (
// S2 is a struct.
S2 struct{}
NoDoc struct{}
)
// NewS is a constructor.
func NewS() *S {
return nil
}
// I is an interface.
type I interface {
// IM is a method.
IM()
}

190
bind/testdata/doc.go.golden vendored Normal file
View file

@ -0,0 +1,190 @@
// Code generated by gobind. DO NOT EDIT.
// Package main is an autogenerated binder stub for package doc.
//
// autogenerated by gobind -lang=go doc
package main
/*
#include <stdlib.h>
#include <stdint.h>
#include "seq.h"
#include "doc.h"
*/
import "C"
import (
"doc"
_seq "golang.org/x/mobile/bind/seq"
)
// suppress the error if seq ends up unused
var _ = _seq.FromRefNum
//export new_doc_NoDoc
func new_doc_NoDoc() C.int32_t {
return C.int32_t(_seq.ToRefNum(new(doc.NoDoc)))
}
//export proxydoc_S_SF_Set
func proxydoc_S_SF_Set(refnum C.int32_t, v C.nstring) {
ref := _seq.FromRefNum(int32(refnum))
_v := decodeString(v)
ref.Get().(*doc.S).SF = _v
}
//export proxydoc_S_SF_Get
func proxydoc_S_SF_Get(refnum C.int32_t) C.nstring {
ref := _seq.FromRefNum(int32(refnum))
v := ref.Get().(*doc.S).SF
_v := encodeString(v)
return _v
}
//export proxydoc_S_S2_Set
func proxydoc_S_S2_Set(refnum C.int32_t, v C.int32_t) {
ref := _seq.FromRefNum(int32(refnum))
// Must be a Go object
var _v *doc.S2
if _v_ref := _seq.FromRefNum(int32(v)); _v_ref != nil {
_v = _v_ref.Get().(*doc.S2)
}
ref.Get().(*doc.S).S2 = _v
}
//export proxydoc_S_S2_Get
func proxydoc_S_S2_Get(refnum C.int32_t) C.int32_t {
ref := _seq.FromRefNum(int32(refnum))
v := ref.Get().(*doc.S).S2
var _v C.int32_t = _seq.NullRefNum
if v != nil {
_v = C.int32_t(_seq.ToRefNum(v))
}
return _v
}
//export proxydoc_S_F1_Set
func proxydoc_S_F1_Set(refnum C.int32_t, v C.nstring) {
ref := _seq.FromRefNum(int32(refnum))
_v := decodeString(v)
ref.Get().(*doc.S).F1 = _v
}
//export proxydoc_S_F1_Get
func proxydoc_S_F1_Get(refnum C.int32_t) C.nstring {
ref := _seq.FromRefNum(int32(refnum))
v := ref.Get().(*doc.S).F1
_v := encodeString(v)
return _v
}
//export proxydoc_S_F2_Set
func proxydoc_S_F2_Set(refnum C.int32_t, v C.nstring) {
ref := _seq.FromRefNum(int32(refnum))
_v := decodeString(v)
ref.Get().(*doc.S).F2 = _v
}
//export proxydoc_S_F2_Get
func proxydoc_S_F2_Get(refnum C.int32_t) C.nstring {
ref := _seq.FromRefNum(int32(refnum))
v := ref.Get().(*doc.S).F2
_v := encodeString(v)
return _v
}
//export proxydoc_S_After
func proxydoc_S_After(refnum C.int32_t) {
ref := _seq.FromRefNum(int32(refnum))
v := ref.Get().(*doc.S)
v.After()
}
//export proxydoc_S_Before
func proxydoc_S_Before(refnum C.int32_t) {
ref := _seq.FromRefNum(int32(refnum))
v := ref.Get().(*doc.S)
v.Before()
}
//export new_doc_S
func new_doc_S() C.int32_t {
return C.int32_t(_seq.ToRefNum(new(doc.S)))
}
//export new_doc_S2
func new_doc_S2() C.int32_t {
return C.int32_t(_seq.ToRefNum(new(doc.S2)))
}
//export proxydoc_I_IM
func proxydoc_I_IM(refnum C.int32_t) {
ref := _seq.FromRefNum(int32(refnum))
v := ref.Get().(doc.I)
v.IM()
}
type proxydoc_I _seq.Ref
func (p *proxydoc_I) Bind_proxy_refnum__() int32 {
return (*_seq.Ref)(p).Bind_IncNum()
}
func (p *proxydoc_I) IM() {
C.cproxydoc_I_IM(C.int32_t(p.Bind_proxy_refnum__()))
}
//export var_setdoc_NoDocVar
func var_setdoc_NoDocVar(v C.double) {
_v := float64(v)
doc.NoDocVar = _v
}
//export var_getdoc_NoDocVar
func var_getdoc_NoDocVar() C.double {
v := doc.NoDocVar
_v := C.double(v)
return _v
}
//export var_setdoc_Specific
func var_setdoc_Specific(v C.nstring) {
_v := decodeString(v)
doc.Specific = _v
}
//export var_getdoc_Specific
func var_getdoc_Specific() C.nstring {
v := doc.Specific
_v := encodeString(v)
return _v
}
//export var_setdoc_V
func var_setdoc_V(v C.nstring) {
_v := decodeString(v)
doc.V = _v
}
//export var_getdoc_V
func var_getdoc_V() C.nstring {
v := doc.V
_v := encodeString(v)
return _v
}
//export proxydoc__F
func proxydoc__F() {
doc.F()
}
//export proxydoc__NewS
func proxydoc__NewS() C.int32_t {
res_0 := doc.NewS()
var _res_0 C.int32_t = _seq.NullRefNum
if res_0 != nil {
_res_0 = C.int32_t(_seq.ToRefNum(res_0))
}
return _res_0
}

194
bind/testdata/doc.java.c.golden vendored Normal file
View file

@ -0,0 +1,194 @@
// Code generated by gobind. DO NOT EDIT.
// JNI functions for the Go <=> Java bridge.
//
// autogenerated by gobind -lang=java doc
#include <android/log.h>
#include <stdint.h>
#include "seq.h"
#include "_cgo_export.h"
#include "doc.h"
jclass proxy_class_doc_I;
jmethodID proxy_class_doc_I_cons;
static jmethodID mid_I_IM;
jclass proxy_class_doc_NoDoc;
jmethodID proxy_class_doc_NoDoc_cons;
jclass proxy_class_doc_S;
jmethodID proxy_class_doc_S_cons;
jclass proxy_class_doc_S2;
jmethodID proxy_class_doc_S2_cons;
JNIEXPORT void JNICALL
Java_doc_Doc__1init(JNIEnv *env, jclass _unused) {
jclass clazz;
clazz = (*env)->FindClass(env, "doc/NoDoc");
proxy_class_doc_NoDoc = (*env)->NewGlobalRef(env, clazz);
proxy_class_doc_NoDoc_cons = (*env)->GetMethodID(env, clazz, "<init>", "(I)V");
clazz = (*env)->FindClass(env, "doc/S");
proxy_class_doc_S = (*env)->NewGlobalRef(env, clazz);
proxy_class_doc_S_cons = (*env)->GetMethodID(env, clazz, "<init>", "(I)V");
clazz = (*env)->FindClass(env, "doc/S2");
proxy_class_doc_S2 = (*env)->NewGlobalRef(env, clazz);
proxy_class_doc_S2_cons = (*env)->GetMethodID(env, clazz, "<init>", "(I)V");
clazz = (*env)->FindClass(env, "doc/Doc$proxyI");
proxy_class_doc_I = (*env)->NewGlobalRef(env, clazz);
proxy_class_doc_I_cons = (*env)->GetMethodID(env, clazz, "<init>", "(I)V");
clazz = (*env)->FindClass(env, "doc/I");
mid_I_IM = (*env)->GetMethodID(env, clazz, "im", "()V");
}
JNIEXPORT void JNICALL
Java_doc_Doc_f(JNIEnv* env, jclass _clazz) {
proxydoc__F();
}
JNIEXPORT jobject JNICALL
Java_doc_Doc_newS(JNIEnv* env, jclass _clazz) {
int32_t r0 = proxydoc__NewS();
jobject _r0 = go_seq_from_refnum(env, r0, proxy_class_doc_S, proxy_class_doc_S_cons);
return _r0;
}
JNIEXPORT jint JNICALL
Java_doc_NoDoc__1_1New(JNIEnv *env, jclass clazz) {
return new_doc_NoDoc();
}
JNIEXPORT jint JNICALL
Java_doc_S__1_1NewS(JNIEnv *env, jclass clazz) {
int32_t refnum = proxydoc__NewS();
return refnum;
}
JNIEXPORT void JNICALL
Java_doc_S_after(JNIEnv* env, jobject __this__) {
int32_t o = go_seq_to_refnum_go(env, __this__);
proxydoc_S_After(o);
}
JNIEXPORT void JNICALL
Java_doc_S_before(JNIEnv* env, jobject __this__) {
int32_t o = go_seq_to_refnum_go(env, __this__);
proxydoc_S_Before(o);
}
JNIEXPORT void JNICALL
Java_doc_S_setSF(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);
proxydoc_S_SF_Set(o, _v);
}
JNIEXPORT jstring JNICALL
Java_doc_S_getSF(JNIEnv *env, jobject this) {
int32_t o = go_seq_to_refnum_go(env, this);
nstring r0 = proxydoc_S_SF_Get(o);
jstring _r0 = go_seq_to_java_string(env, r0);
return _r0;
}
JNIEXPORT void JNICALL
Java_doc_S_setS2(JNIEnv *env, jobject this, jobject v) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t _v = go_seq_to_refnum(env, v);
proxydoc_S_S2_Set(o, _v);
}
JNIEXPORT jobject JNICALL
Java_doc_S_getS2(JNIEnv *env, jobject this) {
int32_t o = go_seq_to_refnum_go(env, this);
int32_t r0 = proxydoc_S_S2_Get(o);
jobject _r0 = go_seq_from_refnum(env, r0, proxy_class_doc_S2, proxy_class_doc_S2_cons);
return _r0;
}
JNIEXPORT void JNICALL
Java_doc_S_setF1(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);
proxydoc_S_F1_Set(o, _v);
}
JNIEXPORT jstring JNICALL
Java_doc_S_getF1(JNIEnv *env, jobject this) {
int32_t o = go_seq_to_refnum_go(env, this);
nstring r0 = proxydoc_S_F1_Get(o);
jstring _r0 = go_seq_to_java_string(env, r0);
return _r0;
}
JNIEXPORT void JNICALL
Java_doc_S_setF2(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);
proxydoc_S_F2_Set(o, _v);
}
JNIEXPORT jstring JNICALL
Java_doc_S_getF2(JNIEnv *env, jobject this) {
int32_t o = go_seq_to_refnum_go(env, this);
nstring r0 = proxydoc_S_F2_Get(o);
jstring _r0 = go_seq_to_java_string(env, r0);
return _r0;
}
JNIEXPORT jint JNICALL
Java_doc_S2__1_1New(JNIEnv *env, jclass clazz) {
return new_doc_S2();
}
JNIEXPORT void JNICALL
Java_doc_Doc_00024proxyI_im(JNIEnv* env, jobject __this__) {
int32_t o = go_seq_to_refnum_go(env, __this__);
proxydoc_I_IM(o);
}
void cproxydoc_I_IM(int32_t refnum) {
JNIEnv *env = go_seq_push_local_frame(0);
jobject o = go_seq_from_refnum(env, refnum, proxy_class_doc_I, proxy_class_doc_I_cons);
(*env)->CallVoidMethod(env, o, mid_I_IM);
go_seq_pop_local_frame(env);
}
JNIEXPORT void JNICALL
Java_doc_Doc_setNoDocVar(JNIEnv *env, jclass clazz, jdouble v) {
double _v = (double)v;
var_setdoc_NoDocVar(_v);
}
JNIEXPORT jdouble JNICALL
Java_doc_Doc_getNoDocVar(JNIEnv *env, jclass clazz) {
double r0 = var_getdoc_NoDocVar();
jdouble _r0 = (jdouble)r0;
return _r0;
}
JNIEXPORT void JNICALL
Java_doc_Doc_setSpecific(JNIEnv *env, jclass clazz, jstring v) {
nstring _v = go_seq_from_java_string(env, v);
var_setdoc_Specific(_v);
}
JNIEXPORT jstring JNICALL
Java_doc_Doc_getSpecific(JNIEnv *env, jclass clazz) {
nstring r0 = var_getdoc_Specific();
jstring _r0 = go_seq_to_java_string(env, r0);
return _r0;
}
JNIEXPORT void JNICALL
Java_doc_Doc_setV(JNIEnv *env, jclass clazz, jstring v) {
nstring _v = go_seq_from_java_string(env, v);
var_setdoc_V(_v);
}
JNIEXPORT jstring JNICALL
Java_doc_Doc_getV(JNIEnv *env, jclass clazz) {
nstring r0 = var_getdoc_V();
jstring _r0 = go_seq_to_java_string(env, r0);
return _r0;
}

324
bind/testdata/doc.java.golden vendored Normal file
View file

@ -0,0 +1,324 @@
// Code generated by gobind. DO NOT EDIT.
// Java class doc.NoDoc is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java doc
package doc;
import go.Seq;
/**
* A generic comment with &lt;HTML&gt;.
*/
public final class NoDoc implements Seq.Proxy {
static { Doc.touch(); }
private final int refnum;
@Override public final int incRefnum() {
Seq.incGoRef(refnum, this);
return refnum;
}
NoDoc(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); }
public NoDoc() { this.refnum = __New(); Seq.trackGoRef(refnum, this); }
private static native int __New();
@Override public boolean equals(Object o) {
if (o == null || !(o instanceof NoDoc)) {
return false;
}
NoDoc that = (NoDoc)o;
return true;
}
@Override public int hashCode() {
return java.util.Arrays.hashCode(new Object[] {});
}
@Override public String toString() {
StringBuilder b = new StringBuilder();
b.append("NoDoc").append("{");
return b.append("}").toString();
}
}
// Code generated by gobind. DO NOT EDIT.
// Java class doc.S is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java doc
package doc;
import go.Seq;
/**
* S is a struct.
*/
public final class S implements Seq.Proxy {
static { Doc.touch(); }
private final int refnum;
@Override public final int incRefnum() {
Seq.incGoRef(refnum, this);
return refnum;
}
/**
* NewS is a constructor.
*/
public S() {
this.refnum = __NewS();
Seq.trackGoRef(refnum, this);
}
private static native int __NewS();
S(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); }
/**
* SF is a field.
*/
public final native String getSF();
/**
* SF is a field.
*/
public final native void setSF(String v);
/**
* Anonymous field.
*/
public final native S2 getS2();
/**
* Anonymous field.
*/
public final native void setS2(S2 v);
/**
* Multiple fields.
*/
public final native String getF1();
/**
* Multiple fields.
*/
public final native void setF1(String v);
/**
* Multiple fields.
*/
public final native String getF2();
/**
* Multiple fields.
*/
public final native void setF2(String v);
/**
* After is another method.
*/
public native void after();
public native void before();
@Override public boolean equals(Object o) {
if (o == null || !(o instanceof S)) {
return false;
}
S that = (S)o;
String thisSF = getSF();
String thatSF = that.getSF();
if (thisSF == null) {
if (thatSF != null) {
return false;
}
} else if (!thisSF.equals(thatSF)) {
return false;
}
S2 thisS2 = getS2();
S2 thatS2 = that.getS2();
if (thisS2 == null) {
if (thatS2 != null) {
return false;
}
} else if (!thisS2.equals(thatS2)) {
return false;
}
String thisF1 = getF1();
String thatF1 = that.getF1();
if (thisF1 == null) {
if (thatF1 != null) {
return false;
}
} else if (!thisF1.equals(thatF1)) {
return false;
}
String thisF2 = getF2();
String thatF2 = that.getF2();
if (thisF2 == null) {
if (thatF2 != null) {
return false;
}
} else if (!thisF2.equals(thatF2)) {
return false;
}
return true;
}
@Override public int hashCode() {
return java.util.Arrays.hashCode(new Object[] {getSF(), getS2(), getF1(), getF2()});
}
@Override public String toString() {
StringBuilder b = new StringBuilder();
b.append("S").append("{");
b.append("SF:").append(getSF()).append(",");
b.append("S2:").append(getS2()).append(",");
b.append("F1:").append(getF1()).append(",");
b.append("F2:").append(getF2()).append(",");
return b.append("}").toString();
}
}
// Code generated by gobind. DO NOT EDIT.
// Java class doc.S2 is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java doc
package doc;
import go.Seq;
/**
* S2 is a struct.
*/
public final class S2 implements Seq.Proxy {
static { Doc.touch(); }
private final int refnum;
@Override public final int incRefnum() {
Seq.incGoRef(refnum, this);
return refnum;
}
S2(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); }
public S2() { this.refnum = __New(); Seq.trackGoRef(refnum, this); }
private static native int __New();
@Override public boolean equals(Object o) {
if (o == null || !(o instanceof S2)) {
return false;
}
S2 that = (S2)o;
return true;
}
@Override public int hashCode() {
return java.util.Arrays.hashCode(new Object[] {});
}
@Override public String toString() {
StringBuilder b = new StringBuilder();
b.append("S2").append("{");
return b.append("}").toString();
}
}
// Code generated by gobind. DO NOT EDIT.
// Java class doc.I is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java doc
package doc;
import go.Seq;
/**
* I is an interface.
*/
public interface I {
/**
* IM is a method.
*/
public void im();
}
// Code generated by gobind. DO NOT EDIT.
// Java class doc.Doc is a proxy for talking to a Go program.
//
// autogenerated by gobind -lang=java doc
package doc;
import go.Seq;
public abstract class Doc {
static {
Seq.touch(); // for loading the native library
_init();
}
private Doc() {} // uninstantiable
// touch is called from other bound packages to initialize this package
public static void touch() {}
private static native void _init();
private static final class proxyI implements Seq.Proxy, I {
private final int refnum;
@Override public final int incRefnum() {
Seq.incGoRef(refnum, this);
return refnum;
}
proxyI(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); }
public native void im();
}
/**
* C is a constant.
*/
public static final boolean C = true;
/**
* A group of vars.
*/
public static native void setNoDocVar(double v);
/**
* A group of vars.
*/
public static native double getNoDocVar();
/**
* A specific var.
*/
public static native void setSpecific(String v);
/**
* A specific var.
*/
public static native String getSpecific();
/**
* V is a var.
*/
public static native void setV(String v);
/**
* V is a var.
*/
public static native String getV();
/**
* F is a function.
*/
public static native void f();
/**
* NewS is a constructor.
*/
public static native S newS();
}

23
bind/testdata/doc.java.h.golden vendored Normal file
View file

@ -0,0 +1,23 @@
// Code generated by gobind. DO NOT EDIT.
// JNI function headers for the Go <=> Java bridge.
//
// autogenerated by gobind -lang=java doc
#ifndef __Doc_H__
#define __Doc_H__
#include <jni.h>
extern jclass proxy_class_doc_I;
extern jmethodID proxy_class_doc_I_cons;
void cproxydoc_I_IM(int32_t refnum);
extern jclass proxy_class_doc_NoDoc;
extern jmethodID proxy_class_doc_NoDoc_cons;
extern jclass proxy_class_doc_S;
extern jmethodID proxy_class_doc_S_cons;
extern jclass proxy_class_doc_S2;
extern jmethodID proxy_class_doc_S2_cons;
#endif

13
bind/testdata/doc.objc.go.h.golden vendored Normal file
View file

@ -0,0 +1,13 @@
// Objective-C API for talking to doc Go package.
// gobind -lang=objc doc
//
// File is generated by gobind. Do not edit.
#ifndef __GO_doc_H__
#define __GO_doc_H__
#include <stdint.h>
#include <objc/objc.h>
void cproxydoc_I_IM(int32_t refnum);
#endif

Some files were not shown because too many files have changed in this diff Show more