Adding upstream version 0.0~git20250520.a1d9079+dfsg.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
590ac7ff5f
commit
20149b7f3a
456 changed files with 70406 additions and 0 deletions
10
.gitattributes
vendored
Normal file
10
.gitattributes
vendored
Normal 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
27
.gitignore
vendored
Normal 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
26
CONTRIBUTING.md
Normal 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
27
LICENSE
Normal 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
22
PATENTS
Normal 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
34
README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Go support for Mobile devices
|
||||
|
||||
[](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)
|
||||
|
||||

|
||||
|
||||
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
67
app/GoNativeActivity.java
Normal 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
201
app/android.c
Normal 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
824
app/android.go
Normal 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
213
app/app.go
Normal 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
237
app/app_test.go
Normal 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
495
app/darwin_desktop.go
Normal 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
251
app/darwin_desktop.m
Normal 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
215
app/darwin_ios.go
Normal 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
167
app/darwin_ios.m
Normal 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
88
app/doc.go
Normal 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
|
67
app/internal/apptest/apptest.go
Normal file
67
app/internal/apptest/apptest.go
Normal 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)
|
||||
}
|
||||
}
|
16
app/internal/callfn/callfn.go
Normal file
16
app/internal/callfn/callfn.go
Normal 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)
|
11
app/internal/callfn/callfn_386.s
Normal file
11
app/internal/callfn/callfn_386.s
Normal 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
|
11
app/internal/callfn/callfn_amd64.s
Normal file
11
app/internal/callfn/callfn_amd64.s
Normal 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
|
11
app/internal/callfn/callfn_arm.s
Normal file
11
app/internal/callfn/callfn_arm.s
Normal 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
|
11
app/internal/callfn/callfn_arm64.s
Normal file
11
app/internal/callfn/callfn_arm64.s
Normal 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
|
27
app/internal/testapp/AndroidManifest.xml
Normal file
27
app/internal/testapp/AndroidManifest.xml
Normal 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>
|
94
app/internal/testapp/testapp.go
Normal file
94
app/internal/testapp/testapp.go
Normal 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
83
app/shiny.go
Normal 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
175
app/x11.c
Normal 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
126
app/x11.go
Normal 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
24
asset/asset.go
Normal 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
109
asset/asset_android.go
Normal 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
23
asset/asset_desktop.go
Normal 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
13
asset/asset_ios.go
Normal 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
17
asset/doc.go
Normal 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
59
bind/bind.go
Normal 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
703
bind/bind_test.go
Normal 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
575
bind/gen.go
Normal 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
948
bind/genclasses.go
Normal 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
593
bind/gengo.go
Normal 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
1732
bind/genjava.go
Normal file
File diff suppressed because it is too large
Load diff
1428
bind/genobjc.go
Normal file
1428
bind/genobjc.go
Normal file
File diff suppressed because it is too large
Load diff
754
bind/genobjcw.go
Normal file
754
bind/genobjcw.go
Normal 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
9
bind/implicit.go
Normal 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
157
bind/java/ClassesTest.java
Normal 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);
|
||||
}
|
||||
}
|
15
bind/java/CustomPkgTest.java
Normal file
15
bind/java/CustomPkgTest.java
Normal 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
400
bind/java/Seq.java
Normal 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
162
bind/java/SeqBench.java
Normal 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
602
bind/java/SeqTest.java
Normal 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);
|
||||
}
|
||||
}
|
16
bind/java/context_android.c
Normal file
16
bind/java/context_android.c
Normal 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));
|
||||
}
|
21
bind/java/context_android.go
Normal file
21
bind/java/context_android.go
Normal 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
8
bind/java/doc.go
Normal 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
|
401
bind/java/seq_android.c.support
Normal file
401
bind/java/seq_android.c.support
Normal 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;
|
||||
}
|
98
bind/java/seq_android.go.support
Normal file
98
bind/java/seq_android.go.support
Normal 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
67
bind/java/seq_android.h
Normal 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
256
bind/java/seq_test.go
Normal 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
197
bind/objc/SeqBench.m
Normal 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 it’s 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
22
bind/objc/SeqCustom.m
Normal 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
481
bind/objc/SeqTest.m
Normal 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
98
bind/objc/SeqWrappers.m
Normal 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
6
bind/objc/doc.go
Normal 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
35
bind/objc/ref.h
Normal 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
|
91
bind/objc/seq_darwin.go.support
Normal file
91
bind/objc/seq_darwin.go.support
Normal 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
63
bind/objc/seq_darwin.h
Normal 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__
|
381
bind/objc/seq_darwin.m.support
Normal file
381
bind/objc/seq_darwin.m.support
Normal 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
1009
bind/objc/seq_test.go
Normal file
File diff suppressed because it is too large
Load diff
67
bind/printer.go
Normal file
67
bind/printer.go
Normal 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
52
bind/seq.go.support
Normal 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
153
bind/seq/ref.go
Normal 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
21
bind/seq/seq.go
Normal 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
49
bind/seq/string.go
Normal 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
28
bind/seq/string_test.go
Normal 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
25
bind/testdata/basictypes.go
vendored
Normal 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
73
bind/testdata/basictypes.go.golden
vendored
Normal 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
61
bind/testdata/basictypes.java.c.golden
vendored
Normal 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
37
bind/testdata/basictypes.java.golden
vendored
Normal 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
12
bind/testdata/basictypes.java.h.golden
vendored
Normal 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
|
11
bind/testdata/basictypes.objc.go.h.golden
vendored
Normal file
11
bind/testdata/basictypes.objc.go.h.golden
vendored
Normal 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
32
bind/testdata/basictypes.objc.h.golden
vendored
Normal 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
82
bind/testdata/basictypes.objc.m.golden
vendored
Normal 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
171
bind/testdata/benchmark/benchmark.go
vendored
Normal 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
9
bind/testdata/cgopkg/cgopkg.go
vendored
Normal 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
62
bind/testdata/classes.go
vendored
Normal 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
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
892
bind/testdata/classes.java.c.golden
vendored
Normal 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
155
bind/testdata/classes.java.golden
vendored
Normal 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
103
bind/testdata/classes.java.h.golden
vendored
Normal 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
10
bind/testdata/customprefix.go
vendored
Normal 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() {
|
||||
}
|
23
bind/testdata/customprefix.java.c.golden
vendored
Normal file
23
bind/testdata/customprefix.java.c.golden
vendored
Normal 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
26
bind/testdata/customprefix.java.golden
vendored
Normal 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();
|
||||
}
|
12
bind/testdata/customprefix.java.h.golden
vendored
Normal file
12
bind/testdata/customprefix.java.h.golden
vendored
Normal 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
|
11
bind/testdata/customprefix.objc.go.h.golden
vendored
Normal file
11
bind/testdata/customprefix.objc.go.h.golden
vendored
Normal 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
|
16
bind/testdata/customprefix.objc.h.golden
vendored
Normal file
16
bind/testdata/customprefix.objc.h.golden
vendored
Normal 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
|
18
bind/testdata/customprefix.objc.m.golden
vendored
Normal file
18
bind/testdata/customprefix.objc.m.golden
vendored
Normal 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();
|
||||
}
|
11
bind/testdata/customprefixEX.objc.go.h.golden
vendored
Normal file
11
bind/testdata/customprefixEX.objc.go.h.golden
vendored
Normal 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
|
16
bind/testdata/customprefixEX.objc.h.golden
vendored
Normal file
16
bind/testdata/customprefixEX.objc.h.golden
vendored
Normal 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
|
18
bind/testdata/customprefixEX.objc.m.golden
vendored
Normal file
18
bind/testdata/customprefixEX.objc.m.golden
vendored
Normal 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
59
bind/testdata/doc.go
vendored
Normal 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
190
bind/testdata/doc.go.golden
vendored
Normal 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
194
bind/testdata/doc.java.c.golden
vendored
Normal 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
324
bind/testdata/doc.java.golden
vendored
Normal 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 <HTML>.
|
||||
*/
|
||||
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
23
bind/testdata/doc.java.h.golden
vendored
Normal 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
13
bind/testdata/doc.objc.go.h.golden
vendored
Normal 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
Loading…
Add table
Add a link
Reference in a new issue