-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproxy_cgo.go
More file actions
196 lines (167 loc) · 6.8 KB
/
proxy_cgo.go
File metadata and controls
196 lines (167 loc) · 6.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package jni
/*
#include <jni.h>
#include <string.h>
#include <stdio.h>
extern jobject goProxyDispatch(JNIEnv *env, jobject thiz, jobject proxy,
jobject method, jobjectArray args);
static inline jint proxy_register_natives(JNIEnv *env, jclass cls) {
JNINativeMethod m = {
"invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
(void*)goProxyDispatch
};
return (*env)->RegisterNatives(env, cls, &m, 1);
}
extern jobject goProxyDispatchInner(JNIEnv *env, jlong handlerID,
jstring methodName, jobjectArray args,
jobject method);
extern jobject goAbstractDispatch(JNIEnv *env, jclass cls, jlong handlerID,
jstring methodName, jobjectArray args);
static inline jint abstract_register_natives(JNIEnv *env, jclass cls) {
JNINativeMethod m = {
"invoke",
"(JLjava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;",
(void*)goAbstractDispatch
};
return (*env)->RegisterNatives(env, cls, &m, 1);
}
// proxy_invoke is the full native dispatch: extract fields in C, call Go.
// It handles standard Object methods (hashCode, equals, toString) in C
// to avoid NullPointerException when Java unboxes null to a primitive.
static inline jobject proxy_invoke(JNIEnv *env, jobject thiz, jobject proxy,
jobject method, jobjectArray args,
jfieldID handlerIDField, jmethodID getNameMID) {
jlong handlerID = (*env)->GetLongField(env, thiz, handlerIDField);
jstring name = (jstring)(*env)->CallObjectMethod(env, method, getNameMID);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionClear(env);
return NULL;
}
const char *nameStr = (*env)->GetStringUTFChars(env, name, NULL);
if (nameStr == NULL) {
if (name != NULL) (*env)->DeleteLocalRef(env, name);
return NULL;
}
// hashCode() -> Integer: use System.identityHashCode(proxy)
if (strcmp(nameStr, "hashCode") == 0) {
(*env)->ReleaseStringUTFChars(env, name, nameStr);
(*env)->DeleteLocalRef(env, name);
jclass sysCls = (*env)->FindClass(env, "java/lang/System");
jmethodID ihcMid = (*env)->GetStaticMethodID(
env, sysCls, "identityHashCode", "(Ljava/lang/Object;)I");
jint hash = (*env)->CallStaticIntMethod(env, sysCls, ihcMid, proxy);
(*env)->DeleteLocalRef(env, sysCls);
jclass intCls = (*env)->FindClass(env, "java/lang/Integer");
jmethodID voMid = (*env)->GetStaticMethodID(
env, intCls, "valueOf", "(I)Ljava/lang/Integer;");
jobject boxed = (*env)->CallStaticObjectMethod(env, intCls, voMid, hash);
(*env)->DeleteLocalRef(env, intCls);
return boxed;
}
// equals(Object) -> Boolean: identity comparison
if (strcmp(nameStr, "equals") == 0) {
(*env)->ReleaseStringUTFChars(env, name, nameStr);
(*env)->DeleteLocalRef(env, name);
jobject other = (args != NULL)
? (*env)->GetObjectArrayElement(env, args, 0) : NULL;
jboolean eq = (*env)->IsSameObject(env, proxy, other);
jclass boolCls = (*env)->FindClass(env, "java/lang/Boolean");
jmethodID voMid = (*env)->GetStaticMethodID(
env, boolCls, "valueOf", "(Z)Ljava/lang/Boolean;");
jobject boxed = (*env)->CallStaticObjectMethod(env, boolCls, voMid, eq);
(*env)->DeleteLocalRef(env, boolCls);
if (other != NULL) (*env)->DeleteLocalRef(env, other);
return boxed;
}
// toString() -> String
if (strcmp(nameStr, "toString") == 0) {
(*env)->ReleaseStringUTFChars(env, name, nameStr);
(*env)->DeleteLocalRef(env, name);
char buf[64];
snprintf(buf, sizeof(buf), "GoProxy@%lld", (long long)handlerID);
return (*env)->NewStringUTF(env, buf);
}
(*env)->ReleaseStringUTFChars(env, name, nameStr);
// Delegate all other methods to Go, passing the Method object
// so handlers can inspect return type (e.g. to detect void).
jobject result = goProxyDispatchInner(env, handlerID, name, args, method);
(*env)->DeleteLocalRef(env, name);
return result;
}
*/
import "C"
import (
"fmt"
"unsafe"
"github.com/AndroidGoLab/jni/capi"
)
func init() {
proxyNativeRegistrar = registerProxyNativesImpl
proxyAbstractRegistrar = registerAbstractNativesImpl
}
func registerProxyNativesImpl(envPtr *capi.Env, cls capi.Class) error {
// Both capi.Env and C.JNIEnv are the same underlying C struct.
// Both capi.Class and C.jclass are the same underlying C type.
// Use reinterpret casts via pointers to avoid vet warnings.
cenv := *(*(*C.JNIEnv))(unsafe.Pointer(&envPtr))
ccls := *(*C.jclass)(unsafe.Pointer(&cls))
rc := C.proxy_register_natives(cenv, ccls)
if rc != 0 {
return fmt.Errorf("jni: RegisterNatives for GoInvocationHandler.invoke failed (rc=%d)", rc)
}
return nil
}
func registerAbstractNativesImpl(envPtr *capi.Env, cls capi.Class) error {
cenv := *(*(*C.JNIEnv))(unsafe.Pointer(&envPtr))
ccls := *(*C.jclass)(unsafe.Pointer(&cls))
rc := C.abstract_register_natives(cenv, ccls)
if rc != 0 {
return fmt.Errorf("jni: RegisterNatives for GoAbstractDispatch.invoke failed (rc=%d)", rc)
}
return nil
}
//export goProxyDispatch
func goProxyDispatch(
cenv *C.JNIEnv,
thiz C.jobject,
proxy C.jobject,
method C.jobject,
args C.jobjectArray,
) C.jobject {
// Do all JNI field/method access in C to avoid cross-package CGo type casts.
cfid := *(*C.jfieldID)(unsafe.Pointer(&fidHandlerID))
cmid := *(*C.jmethodID)(unsafe.Pointer(&midMethodGetName))
return C.proxy_invoke(cenv, thiz, proxy, method, args, cfid, cmid)
}
//export goProxyDispatchInner
func goProxyDispatchInner(
cenv *C.JNIEnv,
handlerID C.jlong,
methodName C.jstring,
args C.jobjectArray,
method C.jobject,
) C.jobject {
envPtr := *(*(*capi.Env))(unsafe.Pointer(&cenv))
capiMethodName := *(*capi.String)(unsafe.Pointer(&methodName))
capiArgs := *(*capi.ObjectArray)(unsafe.Pointer(&args))
capiMethod := *(*capi.Object)(unsafe.Pointer(&method))
result := dispatchProxyInvocation(envPtr, int64(handlerID), capiMethodName, capiArgs, capiMethod)
return *(*C.jobject)(unsafe.Pointer(&result))
}
//export goAbstractDispatch
func goAbstractDispatch(
cenv *C.JNIEnv,
cls C.jclass,
handlerID C.jlong,
methodName C.jstring,
args C.jobjectArray,
) C.jobject {
envPtr := *(*(*capi.Env))(unsafe.Pointer(&cenv))
capiMethodName := *(*capi.String)(unsafe.Pointer(&methodName))
capiArgs := *(*capi.ObjectArray)(unsafe.Pointer(&args))
// Abstract adapters don't have a Method object; pass 0 to fall back
// to the basic handler in dispatchProxyInvocation.
result := dispatchProxyInvocation(envPtr, int64(handlerID), capiMethodName, capiArgs, 0)
return *(*C.jobject)(unsafe.Pointer(&result))
}