mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
tmp
This commit is contained in:
parent
2d2da09b85
commit
708bd50405
6
cmd/ebitenmobile/coffeecatch.c.go.go
Normal file
6
cmd/ebitenmobile/coffeecatch.c.go.go
Normal file
File diff suppressed because one or more lines are too long
6
cmd/ebitenmobile/coffeecatch.h.go.go
Normal file
6
cmd/ebitenmobile/coffeecatch.h.go.go
Normal file
File diff suppressed because one or more lines are too long
23
cmd/ebitenmobile/coffeecatch/LICENSE
Normal file
23
cmd/ebitenmobile/coffeecatch/LICENSE
Normal file
@ -0,0 +1,23 @@
|
||||
Copyright (c) 2013, Xavier Roche (http://www.httrack.com/)
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
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 HOLDER 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.
|
36
cmd/ebitenmobile/coffeecatch/Makefile
Normal file
36
cmd/ebitenmobile/coffeecatch/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
###############################################################################
|
||||
#
|
||||
# "FastLZ" compression library
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
CFILES = coffeecatch.c
|
||||
|
||||
all: gcc test
|
||||
|
||||
clean:
|
||||
rm -f *.o *.obj *.so* *.dll *.exe *.pdb *.exp *.lib sample
|
||||
|
||||
tar:
|
||||
rm -f coffeecatch.tgz
|
||||
tar cvfz coffeecatch.tgz coffeecatch.txt coffeecatch.c coffeecatch.h Makefile LICENSE README.md
|
||||
|
||||
gcc:
|
||||
gcc -c -fPIC -O3 -g3 -pthread \
|
||||
-W -Wall -Wextra -Werror -Wno-unused-function \
|
||||
-D_REENTRANT -D_GNU_SOURCE \
|
||||
$(CFILES)
|
||||
gcc -shared -fPIC -O3 -Wl,-O1 -Wl,--no-undefined \
|
||||
-rdynamic -shared -Wl,-soname=libcoffeecatch.so \
|
||||
coffeecatch.o -o libcoffeecatch.so \
|
||||
-ldl -lpthread
|
||||
|
||||
test:
|
||||
gcc -c -fPIC -O3 -g3 \
|
||||
-W -Wall -Wextra -Werror -Wno-unused-function \
|
||||
-D_REENTRANT \
|
||||
sample.c -o sample.o
|
||||
gcc -fPIC -O3 -Wl,-O1 \
|
||||
-lcoffeecatch -L. \
|
||||
sample.o -o sample
|
||||
|
122
cmd/ebitenmobile/coffeecatch/README.md
Normal file
122
cmd/ebitenmobile/coffeecatch/README.md
Normal file
@ -0,0 +1,122 @@
|
||||
coffeecatch
|
||||
===========
|
||||
|
||||
**CoffeeCatch**, a *tiny* native POSIX signal catcher (especially useful for JNI code on **Android**/Dalvik, but it can be used in non-Java projects)
|
||||
|
||||
It allows to "gracefully" recover from a **signal** (`SIGSEGV`, `SIGBUS`...) as if it was an **exception**. It will not gracefully recover from allocator/mutexes corruption etc., however, but at least "most" gentle crashes (null pointer dereferencing, integer division, stack overflow etc.) should be handled without too much troubles.
|
||||
|
||||
```c
|
||||
/** Enter protected section. **/
|
||||
COFFEE_TRY() {
|
||||
/** Try to call 'call_some_native_function'. **/
|
||||
call_some_protected_function();
|
||||
} COFFEE_CATCH() {
|
||||
/** Caught a signal: throw Java exception. **/
|
||||
/** In pure C projects, you may print an error message (coffeecatch_get_message()). **/
|
||||
coffeecatch_throw_exception(env);
|
||||
} COFFEE_END();
|
||||
```
|
||||
|
||||
You may read the corresponding [discussion](http://blog.httrack.com/blog/2013/08/23/catching-posix-signals-on-android/) about this project.
|
||||
|
||||
The handler is thread-safe, but client must have exclusive control on the signal handlers (ie. the library is installing its own signal handlers on top of the existing ones).
|
||||
|
||||
**Libraries**
|
||||
|
||||
If you want to get useful stack traces, you should build all your libraries with `-funwind-tables` (this adds unwinding information). On ARM, you may also use the `--no-merge-exidx-entries` linker switch, to solve certain issues with unwinding (the switch is possibly not needed anymore). On Android, this can be achieved by using this line in the `Android.mk` file in each library block:
|
||||
```
|
||||
LOCAL_CFLAGS := -funwind-tables -Wl,--no-merge-exidx-entries
|
||||
```
|
||||
|
||||
**Example**
|
||||
|
||||
* Inside JNI (typically, Android)
|
||||
|
||||
*First, build the library, or just add the two files in the list of local files to be built:*
|
||||
```
|
||||
LOCAL_SRC_FILES += coffeecatch.c coffeejni.c
|
||||
```
|
||||
|
||||
*then, use the COFFEE_TRY_JNI() macro to protect your call(s):*
|
||||
|
||||
```c
|
||||
/** The potentially dangerous function. **/
|
||||
jint call_dangerous_function(JNIEnv* env, jobject object) {
|
||||
// ... do dangerous things!
|
||||
return 42;
|
||||
}
|
||||
|
||||
/** Protected function stub. **/
|
||||
void foo_protected(JNIEnv* env, jobject object, jint *retcode) {
|
||||
/* Try to call 'call_dangerous_function', and raise proper Java Error upon
|
||||
* fatal error (SEGV, etc.). **/
|
||||
COFFEE_TRY_JNI(env, *retcode = call_dangerous_function(env, object));
|
||||
}
|
||||
|
||||
/** Regular JNI entry point. **/
|
||||
jint Java_com_example_android_MyNative_foo(JNIEnv* env, jobject object) {
|
||||
jint retcode = 0;
|
||||
foo_protected(env, object, &retcode);
|
||||
return retcode;
|
||||
}
|
||||
```
|
||||
|
||||
*and, in case of crash, get something like this (note: the last Exception with native backtrace is produced on Android >= 4.1.1)*:
|
||||
```
|
||||
FATAL EXCEPTION: AsyncTask #5
|
||||
java.lang.RuntimeException: An error occured while executing doInBackground()
|
||||
at android.os.AsyncTask$3.done(AsyncTask.java:299)
|
||||
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
|
||||
at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
|
||||
at java.util.concurrent.FutureTask.run(FutureTask.java:239)
|
||||
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
|
||||
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
|
||||
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
|
||||
at java.lang.Thread.run(Thread.java:841)
|
||||
Caused by: java.lang.Error: signal 11 (Address not mapped to object) at address 0x42 [at libexample.so:0xa024]
|
||||
at com.example.jni.ExampleLib.main(Native Method)
|
||||
at com.example.ExampleActivity$Runner.runInternal(ExampleActivity.java:998)
|
||||
at com.example.ExampleActivity$Runner.doInBackground(ExampleActivity.java:919)
|
||||
at com.example.ExampleActivity$Runner.doInBackground(ExampleActivity.java:1)
|
||||
at android.os.AsyncTask$2.call(AsyncTask.java:287)
|
||||
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
|
||||
... 4 more
|
||||
Caused by: java.lang.Error: signal 11 (Address not mapped to object) at address 0x42 [at libexample.so:0xa024]
|
||||
at data.app_lib.com_example.libexample_so.0xa024(Native Method)
|
||||
at data.app_lib.com_example.libexample_so.0x705fc(hts_main2:0x8f74:0)
|
||||
at data.app_lib.com_example.libexamplejni_so.0x4cc8(ExampleLib_main:0xf8:0)
|
||||
at data.app_lib.com_example.libexamplejni_so.0x52d8(Java_com_example_jni_ExampleLib_main:0x64:0)
|
||||
at system.lib.libdvm_so.0x1dc4c(dvmPlatformInvoke:0x70:0)
|
||||
at system.lib.libdvm_so.0x4dcab(dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*):0x18a:0)
|
||||
at system.lib.libdvm_so.0x385e1(dvmCheckCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*):0x8:0)
|
||||
at system.lib.libdvm_so.0x4f699(dvmResolveNativeMethod(unsigned int const*, JValue*, Method const*, Thread*):0xb8:0)
|
||||
at system.lib.libdvm_so.0x27060(Native Method)
|
||||
at system.lib.libdvm_so.0x2b580(dvmInterpret(Thread*, Method const*, JValue*):0xb8:0)
|
||||
at system.lib.libdvm_so.0x5fcbd(dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list):0x124:0)
|
||||
at system.lib.libdvm_so.0x5fce7(dvmCallMethod(Thread*, Method const*, Object*, JValue*, ...):0x14:0)
|
||||
at system.lib.libdvm_so.0x54a6f(Native Method)
|
||||
at system.lib.libc_so.0xca58(__thread_entry:0x48:0)
|
||||
at system.lib.libc_so.0xcbd4(pthread_create:0xd0:0)
|
||||
```
|
||||
|
||||
* Outside JNI code
|
||||
|
||||
The COFFEE_TRY()/COFFEE_CATCH()/COFFEE_END() syntax can be used:
|
||||
|
||||
```c
|
||||
void my_function() {
|
||||
COFFEE_TRY() {
|
||||
/** Try to call 'call_some_native_function'. **/
|
||||
call_some_native_function();
|
||||
} COFFEE_CATCH() {
|
||||
/** Caught a signal. **/
|
||||
const char*const message = coffeecatch_get_message();
|
||||
fprintf(stderr, "**FATAL ERROR: %s\n", message);
|
||||
} COFFEE_END();
|
||||
}
|
||||
```
|
||||
|
||||
* Hints
|
||||
|
||||
If you wish to catch signals and continue running your program rather than ending it (this may be dangerous, especially if a crash was spotted within a C library function, such as `malloc()`), use the `coffeecatch_cancel_pending_alarm()` function to cancel the default pending alarm triggered to avoid deadlocks.
|
||||
|
1418
cmd/ebitenmobile/coffeecatch/coffeecatch.c
Normal file
1418
cmd/ebitenmobile/coffeecatch/coffeecatch.c
Normal file
File diff suppressed because it is too large
Load Diff
228
cmd/ebitenmobile/coffeecatch/coffeecatch.h
Normal file
228
cmd/ebitenmobile/coffeecatch/coffeecatch.h
Normal file
@ -0,0 +1,228 @@
|
||||
/* CoffeeCatch, a tiny native signal handler/catcher for JNI code.
|
||||
* (especially for Android/Dalvik)
|
||||
*
|
||||
* Copyright (c) 2013, Xavier Roche (http://www.httrack.com/)
|
||||
* All rights reserved.
|
||||
* See the "License" section below for the licensing terms.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Allows to "gracefully" recover from a signal (segv, sibus...) as if it was
|
||||
* a Java exception. It will not gracefully recover from allocator/mutexes
|
||||
* corruption etc., however, but at least "most" gentle crashes (null pointer
|
||||
* dereferencing, integer division, stack overflow etc.) should be handled
|
||||
* without too much troubles.
|
||||
*
|
||||
* The handler is thread-safe, but client must have exclusive control on the
|
||||
* signal handlers (ie. the library is installing its own signal handlers on
|
||||
* top of the existing ones).
|
||||
*
|
||||
* You must build all your libraries with `-funwind-tables', to get proper
|
||||
* unwinding information on all binaries. On ARM, you may also use the
|
||||
* `--no-merge-exidx-entries` linker switch, to solve certain issues with
|
||||
* unwinding (the switch is possibly not needed anymore).
|
||||
* On Android, this can be achieved by using this line in the Android.mk file
|
||||
* in each library block:
|
||||
* LOCAL_CFLAGS := -funwind-tables -Wl,--no-merge-exidx-entries
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* COFFEE_TRY() {
|
||||
* call_some_native_function()
|
||||
* } COFFEE_CATCH() {
|
||||
* const char*const message = coffeecatch_get_message();
|
||||
* jclass cls = (*env)->FindClass(env, "java/lang/RuntimeException");
|
||||
* (*env)->ThrowNew(env, cls, strdup(message));
|
||||
* } COFFEE_END();
|
||||
*
|
||||
* Implementation notes:
|
||||
*
|
||||
* Currently the library is installing both alternate stack and signal
|
||||
* handlers for known signals (SIGABRT, SIGILL, SIGTRAP, SIGBUS, SIGFPE,
|
||||
* SIGSEGV, SIGSTKFLT), and is using sigsetjmp()/siglongjmp() to return to
|
||||
* "userland" (compared to signal handler context). As a security, an alarm
|
||||
* is started as soon as a fatal signal is detected (ie. not something the
|
||||
* JVM will handle) to kill the process after a grace period. Be sure your
|
||||
* program will exit quickly after the error is caught, or call alarm(0)
|
||||
* to cancel the pending time-bomb.
|
||||
* The signal handlers had to be written with caution, because the virtual
|
||||
* machine might be using signals (including SEGV) to handle JIT compiler,
|
||||
* and some clever optimizations (such as NullPointerException handling)
|
||||
* We are using several signal-unsafe functions, namely:
|
||||
* - siglongjmp() to return to userland
|
||||
* - pthread_getspecific() to get thread-specific setup
|
||||
*
|
||||
* License:
|
||||
*
|
||||
* Copyright (c) 2013, Xavier Roche (http://www.httrack.com/)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef COFFEECATCH_H
|
||||
#define COFFEECATCH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Setup crash handler to enter in a protected section. If a recognized signal
|
||||
* is received in this section, the execution will be diverted to the
|
||||
* COFFEE_CATCH() block.
|
||||
*
|
||||
* Note: you MUST use the following pattern when using this macro:
|
||||
* COFFEE_TRY() {
|
||||
* .. protected section without exit point
|
||||
* } COFFEE_CATCH() {
|
||||
* .. handler section without exit point
|
||||
* } COFFEE_END();
|
||||
*
|
||||
* You can not exit the protected section block, or the handler section block,
|
||||
* using statements such as "return", because the cleanup code would not be
|
||||
* executed.
|
||||
*
|
||||
* It is advised to enclose this complete try/catch/end block in a dedicated
|
||||
* function declared extern or __attribute__ ((noinline)).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* void my_native_function(JNIEnv* env, jobject object, jint *retcode) {
|
||||
* COFFEE_TRY() {
|
||||
* *retcode = call_dangerous_function(env, object);
|
||||
* } COFFEE_CATCH() {
|
||||
* const char*const message = coffeecatch_get_message();
|
||||
* jclass cls = (*env)->FindClass(env, "java/lang/RuntimeException");
|
||||
* (*env)->ThrowNew(env, cls, strdup(message));
|
||||
* *retcode = -1;
|
||||
* } COFFEE_END();
|
||||
* }
|
||||
*
|
||||
* In addition, the following restrictions MUST be followed:
|
||||
* - the function must be declared extern, or with the special attribute
|
||||
* __attribute__ ((noinline)).
|
||||
* - you must not use local variables before the complete try/catch/end block,
|
||||
* or define them as "volatile".
|
||||
* - your function should not ignore the crash silently, as the library will
|
||||
* ensure the process is killed after a grace period (typically 30s) to
|
||||
* prevent any deadlock that may occur if the crash was caught inside a
|
||||
* non-signal-safe function, for example (such as malloc()).
|
||||
*
|
||||
COFFEE_TRY()
|
||||
**/
|
||||
|
||||
/**
|
||||
* Declare the signal handler block. This block will be executed if a signal
|
||||
* was received, and recognized, in the previous COFFEE_TRY() {} section.
|
||||
* You may call audit functions in this block, such as coffeecatch_get_signal()
|
||||
* or coffeecatch_get_message().
|
||||
*
|
||||
COFFEE_CATCH()
|
||||
**/
|
||||
|
||||
/**
|
||||
* Declare the end of the COFFEE_TRY()/COFFEE_CATCH() section.
|
||||
* Diagnostic functions must not be called beyond this point.
|
||||
*
|
||||
COFFEE_END()
|
||||
**/
|
||||
|
||||
/**
|
||||
* Get the signal associated with the crash.
|
||||
* This function can only be called inside a COFFEE_CATCH() block.
|
||||
*/
|
||||
extern int coffeecatch_get_signal(void);
|
||||
|
||||
/**
|
||||
* Get the full error message associated with the crash.
|
||||
* This function can only be called inside a COFFEE_CATCH() block, and the
|
||||
* returned pointer is only valid within this block. (you may want to copy
|
||||
* the string in a static buffer, or use strdup())
|
||||
*/
|
||||
const char* coffeecatch_get_message(void);
|
||||
|
||||
/**
|
||||
* Raise an abort() signal in the current thread. If the current code section
|
||||
* is protected, the 'exp', 'file' and 'line' information are stored for
|
||||
* further audit.
|
||||
*/
|
||||
extern void coffeecatch_abort(const char* exp, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Assertion check. If the expression is false, an abort() signal is raised
|
||||
* using coffeecatch_abort().
|
||||
*/
|
||||
#define coffeecatch_assert(EXP) (void)( (EXP) || (coffeecatch_abort(#EXP, __FILE__, __LINE__), 0) )
|
||||
|
||||
/**
|
||||
* Get the backtrace size, or 0 upon error.
|
||||
* This function can only be called inside a COFFEE_CATCH() block.
|
||||
*/
|
||||
extern size_t coffeecatch_get_backtrace_size(void);
|
||||
|
||||
/**
|
||||
* Get the backtrace pointer, or 0 upon error.
|
||||
* This function can only be called inside a COFFEE_CATCH() block.
|
||||
*/
|
||||
extern uintptr_t coffeecatch_get_backtrace(ssize_t index);
|
||||
|
||||
/**
|
||||
* Enumerate the backtrace with information.
|
||||
* This function can only be called inside a COFFEE_CATCH() block.
|
||||
*/
|
||||
extern void coffeecatch_get_backtrace_info(void (*fun)(void *arg,
|
||||
const char *module,
|
||||
uintptr_t addr,
|
||||
const char *function,
|
||||
uintptr_t offset), void *arg);
|
||||
|
||||
/**
|
||||
* Cancel any pending alarm() triggered after a signal was caught.
|
||||
* Calling this function is dangerous, because it exposes the process to
|
||||
* a possible deadlock if the signal was caught due to internal low-level
|
||||
* library error (mutex being in a locked state, for example).
|
||||
*/
|
||||
extern int coffeecatch_cancel_pending_alarm(void);
|
||||
|
||||
/** Internal functions & definitions, not to be used directly. **/
|
||||
#include <setjmp.h>
|
||||
extern int coffeecatch_inside(void);
|
||||
extern int coffeecatch_setup(void);
|
||||
extern sigjmp_buf* coffeecatch_get_ctx(void);
|
||||
extern void coffeecatch_cleanup(void);
|
||||
#define COFFEE_TRY() \
|
||||
if (coffeecatch_inside() || \
|
||||
(coffeecatch_setup() == 0 \
|
||||
&& sigsetjmp(*coffeecatch_get_ctx(), 1) == 0))
|
||||
#define COFFEE_CATCH() else
|
||||
#define COFFEE_END() coffeecatch_cleanup()
|
||||
/** End of internal functions & definitions. **/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
185
cmd/ebitenmobile/coffeecatch/coffeejni.c
Normal file
185
cmd/ebitenmobile/coffeecatch/coffeejni.c
Normal file
@ -0,0 +1,185 @@
|
||||
/* CoffeeCatch, a tiny native signal handler/catcher for JNI code.
|
||||
* (especially for Android/Dalvik)
|
||||
*
|
||||
* Copyright (c) 2013, Xavier Roche (http://www.httrack.com/)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef COFFEECATCH_JNI_H
|
||||
#define COFFEECATCH_JNI_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <jni.h>
|
||||
#include <assert.h>
|
||||
#include "coffeecatch.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct t_bt_fun {
|
||||
JNIEnv* env;
|
||||
jclass cls;
|
||||
jclass cls_ste;
|
||||
jmethodID cons_ste;
|
||||
jobjectArray elements;
|
||||
size_t size;
|
||||
size_t index;
|
||||
} t_bt_fun;
|
||||
|
||||
static char* bt_print(const char *function, uintptr_t offset) {
|
||||
if (function != NULL) {
|
||||
char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer), "%s:%p", function, (void*) offset);
|
||||
return strdup(buffer);
|
||||
} else {
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
static char* bt_addr(uintptr_t addr) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%p", (void*) addr);
|
||||
return strdup(buffer);
|
||||
}
|
||||
|
||||
#define IS_VALID_CLASS_CHAR(C) ( \
|
||||
((C) >= 'a' && (C) <= 'z') \
|
||||
|| ((C) >= 'A' && (C) <= 'Z') \
|
||||
|| ((C) >= '0' && (C) <= '9') \
|
||||
|| (C) == '_' \
|
||||
)
|
||||
|
||||
static char* bt_module(const char *module) {
|
||||
if (module != NULL) {
|
||||
size_t i;
|
||||
char *copy;
|
||||
if (*module == '/') {
|
||||
module++;
|
||||
}
|
||||
copy = strdup(module);
|
||||
/* Pseudo-java-class. */
|
||||
for(i = 0; copy[i] != '\0'; i++) {
|
||||
if (copy[i] == '/') {
|
||||
copy[i] = '.';
|
||||
} else if (!IS_VALID_CLASS_CHAR(copy[i])) {
|
||||
copy[i] = '_';
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
} else {
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_fun(void *arg, const char *module, uintptr_t addr,
|
||||
const char *function, uintptr_t offset) {
|
||||
t_bt_fun *const t = (t_bt_fun*) arg;
|
||||
JNIEnv*const env = t->env;
|
||||
jstring declaringClass = (*env)->NewStringUTF(env, bt_module(module));
|
||||
jstring methodName = (*env)->NewStringUTF(env, bt_addr(addr));
|
||||
jstring fileName = (*env)->NewStringUTF(env, bt_print(function, offset));
|
||||
const int lineNumber = function != NULL ? 0 : -2; /* "-2" is "inside JNI code" */
|
||||
jobject trace = (*env)->NewObject(env, t->cls_ste, t->cons_ste,
|
||||
declaringClass, methodName, fileName,
|
||||
lineNumber);
|
||||
if (t->index < t->size) {
|
||||
(*t->env)->SetObjectArrayElement(t->env, t->elements, t->index++, trace);
|
||||
}
|
||||
}
|
||||
|
||||
void coffeecatch_throw_exception(JNIEnv* env) {
|
||||
jclass cls = (*env)->FindClass(env, "java/lang/Error");
|
||||
jclass cls_ste = (*env)->FindClass(env, "java/lang/StackTraceElement");
|
||||
|
||||
jmethodID cons = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
|
||||
jmethodID cons_cause = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
|
||||
jmethodID cons_ste = (*env)->GetMethodID(env, cls_ste, "<init>",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
|
||||
jmethodID meth_sste = (*env)->GetMethodID(env, cls, "setStackTrace",
|
||||
"([Ljava/lang/StackTraceElement;)V");
|
||||
|
||||
/* Exception message. */
|
||||
const char*const message = coffeecatch_get_message();
|
||||
jstring str = (*env)->NewStringUTF(env, strdup(message));
|
||||
|
||||
/* Final exception. */
|
||||
jthrowable exception;
|
||||
|
||||
/* Add pseudo-stack trace. */
|
||||
const ssize_t bt_size = coffeecatch_get_backtrace_size();
|
||||
|
||||
assert(cls != NULL);
|
||||
assert(cls_ste != NULL);
|
||||
assert(cons != NULL);
|
||||
assert(cons_cause != NULL);
|
||||
assert(cons_ste != NULL);
|
||||
assert(meth_sste != NULL);
|
||||
|
||||
assert(message != NULL);
|
||||
assert(str != NULL);
|
||||
|
||||
/* Can we produce a stack trace ? */
|
||||
if (bt_size > 0) {
|
||||
/* Create secondary exception. */
|
||||
jthrowable cause = (jthrowable) (*env)->NewObject(env, cls, cons, str);
|
||||
|
||||
/* Stack trace. */
|
||||
jobjectArray elements =
|
||||
(*env)->NewObjectArray(env, bt_size, cls_ste, NULL);
|
||||
if (elements != NULL) {
|
||||
t_bt_fun t;
|
||||
t.env = env;
|
||||
t.cls = cls;
|
||||
t.cls_ste = cls_ste;
|
||||
t.cons_ste = cons_ste;
|
||||
t.elements = elements;
|
||||
t.index = 0;
|
||||
t.size = bt_size;
|
||||
coffeecatch_get_backtrace_info(bt_fun, &t);
|
||||
(*env)->CallVoidMethod(env, cause, meth_sste, elements);
|
||||
}
|
||||
|
||||
/* Primary exception */
|
||||
exception = (jthrowable) (*env)->NewObject(env, cls, cons_cause, str, cause);
|
||||
} else {
|
||||
/* Simple exception */
|
||||
exception = (jthrowable) (*env)->NewObject(env, cls, cons, str);
|
||||
}
|
||||
|
||||
/* Throw exception. */
|
||||
if (exception != NULL) {
|
||||
(*env)->Throw(env, exception);
|
||||
} else {
|
||||
(*env)->ThrowNew(env, cls, strdup(message));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
131
cmd/ebitenmobile/coffeecatch/coffeejni.h
Normal file
131
cmd/ebitenmobile/coffeecatch/coffeejni.h
Normal file
@ -0,0 +1,131 @@
|
||||
/* CoffeeCatch, a tiny native signal handler/catcher for JNI code.
|
||||
* (especially for Android/Dalvik)
|
||||
*
|
||||
* Copyright (c) 2013, Xavier Roche (http://www.httrack.com/)
|
||||
* All rights reserved.
|
||||
* See the "License" section below for the licensing terms.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Allows to "gracefully" recover from a signal (segv, sibus...) as if it was
|
||||
* a Java exception. It will not gracefully recover from allocator/mutexes
|
||||
* corruption etc., however, but at least "most" gentle crashes (null pointer
|
||||
* dereferencing, integer division, stack overflow etc.) should be handled
|
||||
* without too much troubles.
|
||||
*
|
||||
* The handler is thread-safe, but client must have exclusive control on the
|
||||
* signal handlers (ie. the library is installing its own signal handlers on
|
||||
* top of the existing ones).
|
||||
*
|
||||
* You must build all your libraries with `-funwind-tables', to get proper
|
||||
* unwinding information on all binaries. On ARM, you may also use the
|
||||
* `--no-merge-exidx-entries` linker switch, to solve certain issues with
|
||||
* unwinding (the switch is possibly not needed anymore).
|
||||
* On Android, this can be achieved by using this line in the Android.mk file
|
||||
* in each library block:
|
||||
* LOCAL_CFLAGS := -funwind-tables -Wl,--no-merge-exidx-entries
|
||||
*
|
||||
* Example:
|
||||
* COFFEE_TRY_JNI(env, *retcode = call_dangerous_function(env, object));
|
||||
*
|
||||
* Implementation notes:
|
||||
*
|
||||
* Currently the library is installing both alternate stack and signal
|
||||
* handlers for known signals (SIGABRT, SIGILL, SIGTRAP, SIGBUS, SIGFPE,
|
||||
* SIGSEGV, SIGSTKFLT), and is using sigsetjmp()/siglongjmp() to return to
|
||||
* "userland" (compared to signal handler context). As a security, an alarm
|
||||
* is started as soon as a fatal signal is detected (ie. not something the
|
||||
* JVM will handle) to kill the process after a grace period. Be sure your
|
||||
* program will exit quickly after the error is caught, or call alarm(0)
|
||||
* to cancel the pending time-bomb.
|
||||
* The signal handlers had to be written with caution, because the virtual
|
||||
* machine might be using signals (including SEGV) to handle JIT compiler,
|
||||
* and some clever optimizations (such as NullPointerException handling)
|
||||
* We are using several signal-unsafe functions, namely:
|
||||
* - siglongjmp() to return to userland
|
||||
* - pthread_getspecific() to get thread-specific setup
|
||||
*
|
||||
* License:
|
||||
*
|
||||
* Copyright (c) 2013, Xavier Roche (http://www.httrack.com/)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef COFFEECATCH_JNI_H
|
||||
#define COFFEECATCH_JNI_H
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Setup crash handler to enter in a protected section. If a recognized signal
|
||||
* is received in this section, an appropriate native Java Error will be
|
||||
* raised.
|
||||
*
|
||||
* You can not exit the protected section block CODE_TO_BE_EXECUTED, using
|
||||
* statements such as "return", because the cleanup code would not be
|
||||
* executed.
|
||||
*
|
||||
* It is advised to enclose the complete CODE_TO_BE_EXECUTED block in a
|
||||
* dedicated function declared extern or __attribute__ ((noinline)).
|
||||
*
|
||||
* You must build all your libraries with `-funwind-tables', to get proper
|
||||
* unwinding information on all binaries. On Android, this can be achieved
|
||||
* by using this line in the Android.mk file in each library block:
|
||||
* LOCAL_CFLAGS := -funwind-tables
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* void my_native_function(JNIEnv* env, jobject object, jint *retcode) {
|
||||
* COFFEE_TRY_JNI(env, *retcode = call_dangerous_function(env, object));
|
||||
* }
|
||||
*
|
||||
* In addition, the following restrictions MUST be followed:
|
||||
* - the function must be declared extern, or with the special attribute
|
||||
* __attribute__ ((noinline)).
|
||||
* - you must not use local variables before the COFFEE_TRY_JNI block,
|
||||
* or define them as "volatile".
|
||||
*
|
||||
COFFEE_TRY_JNI(JNIEnv* env, CODE_TO_BE_EXECUTED)
|
||||
*/
|
||||
|
||||
/** Internal functions & definitions, not to be used directly. **/
|
||||
extern void coffeecatch_throw_exception(JNIEnv* env);
|
||||
#define COFFEE_TRY_JNI(ENV, CODE) \
|
||||
do { \
|
||||
COFFEE_TRY() { \
|
||||
CODE; \
|
||||
} COFFEE_CATCH() { \
|
||||
coffeecatch_throw_exception(ENV); \
|
||||
} COFFEE_END(); \
|
||||
} while(0)
|
||||
/** End of internal functions & definitions. **/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
43
cmd/ebitenmobile/coffeecatch/sample.c
Normal file
43
cmd/ebitenmobile/coffeecatch/sample.c
Normal file
@ -0,0 +1,43 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "coffeecatch.h"
|
||||
|
||||
int recurse_madness(int level) {
|
||||
static int var[] = { 1, 2 };
|
||||
if (level > 2000) {
|
||||
return 1 + level;
|
||||
} else {
|
||||
return recurse_madness(level + 1)*var[level];
|
||||
}
|
||||
}
|
||||
|
||||
static char string_buffer[256];
|
||||
|
||||
static __attribute__ ((noinline)) void demo(int *fault) {
|
||||
COFFEE_TRY() {
|
||||
recurse_madness(42);
|
||||
*fault = 0;
|
||||
} COFFEE_CATCH() {
|
||||
const char*const message = coffeecatch_get_message();
|
||||
snprintf(string_buffer, sizeof(string_buffer), "%s", message);
|
||||
*fault = 1;
|
||||
} COFFEE_END();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int fault;
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
printf("running demo...\n");
|
||||
demo(&fault);
|
||||
if (fault != 0) {
|
||||
fprintf(stderr, "** crash detected: %s\n", string_buffer);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
printf("success!\n");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
6
cmd/ebitenmobile/coffeejni.c.go.go
Normal file
6
cmd/ebitenmobile/coffeejni.c.go.go
Normal file
File diff suppressed because one or more lines are too long
6
cmd/ebitenmobile/coffeejni.h.go.go
Normal file
6
cmd/ebitenmobile/coffeejni.h.go.go
Normal file
File diff suppressed because one or more lines are too long
@ -12,6 +12,22 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:generate file2byteslice -package=main -input=gobind.go -output=gobind.src.go -var gobindsrc
|
||||
//go:generate file2byteslice -package=main -input=gobind.go -output=gobind.go.go -var gobind_go
|
||||
|
||||
//go:generate file2byteslice -package=main -input=coffeecatch/coffeecatch.c -output=coffeecatch.c.go -var coffeecatch_c -buildtags=ebitenmobilegobind
|
||||
//go:generate file2byteslice -package=main -input=coffeecatch.c.go -output=coffeecatch.c.go.go -var coffeecatch_c_go
|
||||
//go:generate rm coffeecatch.c.go
|
||||
|
||||
//go:generate file2byteslice -package=main -input=coffeecatch/coffeecatch.h -output=coffeecatch.h.go -var coffeecatch_h -buildtags=ebitenmobilegobind
|
||||
//go:generate file2byteslice -package=main -input=coffeecatch.h.go -output=coffeecatch.h.go.go -var coffeecatch_h_go
|
||||
//go:generate rm coffeecatch.h.go
|
||||
|
||||
//go:generate file2byteslice -package=main -input=coffeecatch/coffeejni.c -output=coffeejni.c.go -var coffeejni_c -buildtags=ebitenmobilegobind
|
||||
//go:generate file2byteslice -package=main -input=coffeejni.c.go -output=coffeejni.c.go.go -var coffeejni_c_go
|
||||
//go:generate rm coffeejni.c.go
|
||||
|
||||
//go:generate file2byteslice -package=main -input=coffeecatch/coffeejni.h -output=coffeejni.h.go -var coffeejni_h -buildtags=ebitenmobilegobind
|
||||
//go:generate file2byteslice -package=main -input=coffeejni.h.go -output=coffeejni.h.go.go -var coffeejni_h_go
|
||||
//go:generate rm coffeejni.h.go
|
||||
|
||||
package main
|
||||
|
@ -80,8 +80,16 @@ func invokeOriginalGobind(lang string) (pkgName string, err error) {
|
||||
}
|
||||
|
||||
func run() error {
|
||||
writeFile := func(filename string, content string) error {
|
||||
if err := ioutil.WriteFile(filepath.Join(*outdir, filename), []byte(content), 0644); err != nil {
|
||||
readFile := func(filename string) ([]byte, error) {
|
||||
content, err := ioutil.ReadFile(filepath.Join(*outdir, filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
writeFile := func(filename string, content []byte) error {
|
||||
if err := ioutil.WriteFile(filepath.Join(*outdir, filename), content, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -105,22 +113,44 @@ func run() error {
|
||||
|
||||
switch lang {
|
||||
case "objc":
|
||||
// iOS
|
||||
if err := writeFile(filepath.Join("src", "gobind", prefixLower+"ebitenviewcontroller_ios.m"), replacePrefixes(objcM)); err != nil {
|
||||
if err := writeFile(filepath.Join("src", "gobind", prefixLower+"ebitenviewcontroller_ios.m"), []byte(replacePrefixes(objcM))); err != nil {
|
||||
return err
|
||||
}
|
||||
case "java":
|
||||
// Android
|
||||
dir := filepath.Join(strings.Split(*javaPkg, ".")...)
|
||||
dir = filepath.Join(dir, prefixLower)
|
||||
if err := writeFile(filepath.Join("java", dir, "EbitenView.java"), replacePrefixes(viewJava)); err != nil {
|
||||
if err := writeFile(filepath.Join("java", dir, "EbitenView.java"), []byte(replacePrefixes(viewJava))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeFile(filepath.Join("java", dir, "EbitenSurfaceView.java"), replacePrefixes(surfaceViewJava)); err != nil {
|
||||
if err := writeFile(filepath.Join("java", dir, "EbitenSurfaceView.java"), []byte(replacePrefixes(surfaceViewJava))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use CoffeeCatch (https://github.com/xroche/coffeecatch)
|
||||
binding, err := readFile(filepath.Join("src", "gobind", "ebitenmobileview_android.c"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeFile(filepath.Join("src", "gobind", "ebitenmobileview_android.c"), []byte(addCoffeeCatch(string(binding)))); err != nil {
|
||||
return err
|
||||
}
|
||||
case "go":
|
||||
// Do nothing.
|
||||
if err := writeFile(filepath.Join("src", "gobind", "coffeecatch.c"), coffeecatch_c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeFile(filepath.Join("src", "gobind", "coffeecatch.h"), coffeecatch_h); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeFile(filepath.Join("src", "gobind", "coffeejni.c"), coffeejni_c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeFile(filepath.Join("src", "gobind", "coffeejni.h"), coffeejni_h); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeFile(filepath.Join("src", "gobind", "cgoflags.go"), []byte(cgoFlagsGo)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported language: %s", lang))
|
||||
}
|
||||
@ -129,6 +159,42 @@ func run() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func addInclude(src string, filename string) string {
|
||||
idx0 := strings.LastIndex(src, "#include ")
|
||||
idx1 := strings.Index(src[idx0:], "\n") + idx0
|
||||
return src[:idx1] + "\n#include \"" + filename + "\"" + src[idx1:]
|
||||
}
|
||||
|
||||
func addCoffeeCatch(src string) string {
|
||||
src = addInclude(src, "coffeecatch.h")
|
||||
src = addInclude(src, "coffeejni.h")
|
||||
|
||||
const sig = "ebitenmobileview_Ebitenmobileview_update(JNIEnv* env, jclass _clazz) {"
|
||||
idx0 := strings.Index(src, sig) + len(sig)
|
||||
idx1 := strings.Index(src[idx0:], "}") + idx0
|
||||
p0 := src[:idx0]
|
||||
p1 := src[idx0:idx1]
|
||||
p2 := src[idx1:]
|
||||
|
||||
const coffeeCatchPre = `
|
||||
COFFEE_TRY() {
|
||||
`
|
||||
const coffeeCatchPost = `
|
||||
} COFFEE_CATCH() {
|
||||
coffeecatch_throw_exception(env);
|
||||
} COFFEE_END();
|
||||
`
|
||||
return p0 + coffeeCatchPre + p1 + coffeeCatchPost + p2
|
||||
}
|
||||
|
||||
const cgoFlagsGo = `// Code generated by ebitenmobile. DO NOT EDIT.
|
||||
|
||||
package main
|
||||
|
||||
// #cgo CFLAGS: -D_REENTRANT
|
||||
import "C"
|
||||
`
|
||||
|
||||
const objcM = `// Code generated by ebitenmobile. DO NOT EDIT.
|
||||
|
||||
// +build ios
|
||||
@ -252,8 +318,7 @@ const objcM = `// Code generated by ebitenmobile. DO NOT EDIT.
|
||||
[self updateTouches:touches];
|
||||
}
|
||||
|
||||
- (void)suspendGame
|
||||
{
|
||||
- (void)suspendGame {
|
||||
NSAssert(started_, @"suspendGame msut not be called before viewDidLoad is called");
|
||||
|
||||
@synchronized(self) {
|
||||
@ -261,8 +326,7 @@ const objcM = `// Code generated by ebitenmobile. DO NOT EDIT.
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resumeGame
|
||||
{
|
||||
- (void)resumeGame {
|
||||
NSAssert(started_, @"resumeGame msut not be called before viewDidLoad is called");
|
||||
|
||||
@synchronized(self) {
|
||||
|
6
cmd/ebitenmobile/gobind.go.go
Normal file
6
cmd/ebitenmobile/gobind.go.go
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -108,11 +108,22 @@ func prepareGomobileCommands() error {
|
||||
if err := os.Mkdir("src", 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join("src", "gobind.go"), gobindsrc, 0644); err != nil {
|
||||
if err := ioutil.WriteFile(filepath.Join("src", "gobind.go"), gobind_go, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runGo("build", "-o", filepath.Join("bin", "gobind"), "-tags", "ebitenmobilegobind", filepath.Join("src", "gobind.go")); err != nil {
|
||||
if err := ioutil.WriteFile(filepath.Join("src", "coffeecatch.c.go"), coffeecatch_c_go, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join("src", "coffeecatch.h.go"), coffeecatch_h_go, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join("src", "coffeejni.c.go"), coffeejni_c_go, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join("src", "coffeejni.h.go"), coffeejni_h_go, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runGo("build", "-o", filepath.Join("bin", "gobind"), "-tags", "ebitenmobilegobind", "./src"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,7 @@ package mobile
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"image"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -184,16 +182,6 @@ func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, tit
|
||||
}
|
||||
|
||||
func (u *UserInterface) run(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics, mainloop bool) (err error) {
|
||||
// Convert the panic to a regular error so that Java/Objective-C layer can treat this easily e.g., for
|
||||
// Crashlytics. A panic is treated as SIGABRT, and there is no way to handle this on Java/Objective-C layer
|
||||
// unfortunately.
|
||||
// TODO: Panic on other goroutines cannot be handled here.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("%v\n%q", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
u.m.Lock()
|
||||
u.width = width
|
||||
u.height = height
|
||||
|
Loading…
Reference in New Issue
Block a user