]>
Commit | Line | Data |
---|---|---|
7341eedb JF |
1 | /* Cycript - The Truly Universal Scripting Language |
2 | * Copyright (C) 2009-2016 Jay Freeman (saurik) | |
e17ad658 JF |
3 | */ |
4 | ||
f95d2598 | 5 | /* GNU Affero General Public License, Version 3 {{{ */ |
e17ad658 | 6 | /* |
f95d2598 JF |
7 | * This program is free software: you can redistribute it and/or modify |
8 | * it under the terms of the GNU Affero General Public License as published by | |
9 | * the Free Software Foundation, either version 3 of the License, or | |
10 | * (at your option) any later version. | |
11 | ||
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
e17ad658 | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
f95d2598 JF |
15 | * GNU Affero General Public License for more details. |
16 | ||
17 | * You should have received a copy of the GNU Affero General Public License | |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
e17ad658 JF |
19 | **/ |
20 | /* }}} */ | |
21 | ||
dbf05bfd JF |
22 | #include <map> |
23 | #include <sstream> | |
24 | #include <vector> | |
25 | ||
f2c357f9 JF |
26 | #include <dlfcn.h> |
27 | ||
def90846 JF |
28 | #ifdef __APPLE__ |
29 | #include <JavaVM/jni.h> | |
30 | #else | |
e17ad658 | 31 | #include <jni.h> |
def90846 | 32 | #endif |
dbf05bfd | 33 | |
f2c357f9 JF |
34 | #ifdef __ANDROID__ |
35 | // XXX: this is deprecated?!?!?!?!?!?! | |
36 | #include <sys/system_properties.h> | |
37 | #endif | |
38 | ||
dbf05bfd | 39 | #include "cycript.hpp" |
c9c16dde | 40 | #include "Error.hpp" |
dbf05bfd JF |
41 | #include "Execute.hpp" |
42 | #include "Internal.hpp" | |
43 | #include "JavaScript.hpp" | |
44 | #include "Pooling.hpp" | |
45 | ||
46 | #define _jnicall(expr) ({ \ | |
47 | jint _value(expr); \ | |
48 | if (_value != JNI_OK) \ | |
49 | CYThrow("_jnicall(%s) == %d", #expr, _value); \ | |
50 | }) | |
51 | ||
52 | #define _envcall(jni, expr) ({ \ | |
53 | __typeof__(jni->expr) _value(jni->expr); \ | |
54 | if (jthrowable _error = jni->ExceptionOccurred()) { \ | |
55 | jni->ExceptionClear(); \ | |
fc664744 | 56 | throw CYJavaError(CYJavaLocal<jthrowable>(jni, _error)); \ |
dbf05bfd JF |
57 | } \ |
58 | _value; }) | |
59 | ||
60 | #define _envcallv(jni, expr) do { \ | |
61 | jni->expr; \ | |
62 | if (jthrowable _error = jni->ExceptionOccurred()) { \ | |
63 | jni->ExceptionClear(); \ | |
fc664744 | 64 | throw CYJavaError(CYJavaLocal<jthrowable>(jni, _error)); \ |
dbf05bfd JF |
65 | } \ |
66 | } while (false) | |
67 | ||
c9c16dde | 68 | #define CYJavaTry \ |
fc664744 | 69 | CYJavaEnv jni(env); \ |
824bc1ec JF |
70 | auto &protect(*reinterpret_cast<CYProtect *>(jprotect)); \ |
71 | _disused JSContextRef context(protect); \ | |
72 | _disused JSObjectRef object(protect); \ | |
c9c16dde JF |
73 | try |
74 | #define CYJavaCatch(value) \ | |
75 | catch (const CYException &error) { \ | |
fc664744 | 76 | jni->Throw(CYCastJavaObject(jni, context, error.CastJSValue(context, "Error")).cast<jthrowable>()); \ |
c9c16dde JF |
77 | return value; \ |
78 | } | |
79 | ||
f2c357f9 | 80 | static JNIEnv *GetJNI(JSContextRef context); |
c9c16dde | 81 | |
fc664744 JF |
82 | #define CYJavaForEachPrimitive \ |
83 | CYJavaForEachPrimitive_(Z, z, Boolean, Boolean, boolean) \ | |
84 | CYJavaForEachPrimitive_(B, b, Byte, Byte, byte) \ | |
85 | CYJavaForEachPrimitive_(C, c, Char, Character, char) \ | |
86 | CYJavaForEachPrimitive_(S, s, Short, Short, short) \ | |
87 | CYJavaForEachPrimitive_(I, i, Int, Integer, int) \ | |
88 | CYJavaForEachPrimitive_(J, j, Long, Long, long) \ | |
89 | CYJavaForEachPrimitive_(F, f, Float, Float, float) \ | |
90 | CYJavaForEachPrimitive_(D, d, Double, Double, double) | |
c9c16dde | 91 | |
fc664744 JF |
92 | enum CYJavaPrimitive : char { |
93 | CYJavaPrimitiveObject, | |
94 | CYJavaPrimitiveVoid, | |
95 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ | |
96 | CYJavaPrimitive ## Type, | |
97 | CYJavaForEachPrimitive | |
98 | #undef CYJavaForEachPrimitive_ | |
99 | }; | |
dbf05bfd | 100 | |
0e832de4 JF |
101 | template <typename Type_> |
102 | struct IsJavaPrimitive { static const bool value = false; }; | |
103 | ||
104 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ | |
105 | template <> \ | |
106 | struct IsJavaPrimitive<j ## type> { static const bool value = true; }; | |
107 | CYJavaForEachPrimitive | |
108 | #undef CYJavaForEachPrimitive_ | |
109 | ||
fc664744 JF |
110 | // Java References {{{ |
111 | template <typename Value_> | |
112 | struct CYJavaRef { | |
113 | JNIEnv *jni_; | |
114 | Value_ value_; | |
dbf05bfd | 115 | |
fc664744 JF |
116 | _finline CYJavaRef(JNIEnv *jni, Value_ value) : |
117 | jni_(jni), | |
118 | value_(value) | |
119 | { | |
120 | } | |
dbf05bfd | 121 | |
477549f5 JF |
122 | _finline operator Value_() const { |
123 | return value_; | |
fc664744 | 124 | } |
c9c16dde | 125 | |
477549f5 | 126 | _finline JNIEnv *jni() const { |
fc664744 JF |
127 | return jni_; |
128 | } | |
c9c16dde | 129 | |
0e832de4 | 130 | // XXX: this is only needed to support CYJavaEnv relying on C variadics |
fc664744 JF |
131 | _finline Value_ get() const { |
132 | return value_; | |
133 | } | |
c9c16dde | 134 | |
fc664744 JF |
135 | template <typename Other_> |
136 | _finline CYJavaRef<Other_> cast() const { | |
137 | return {jni_, static_cast<Other_>(value_)}; | |
138 | } | |
139 | ||
4e2cc9d3 | 140 | // XXX: this should be tied into CYJavaFrame |
fc664744 JF |
141 | Value_ leak() { |
142 | Value_ value(value_); | |
143 | value_ = NULL; | |
144 | return value; | |
145 | } | |
146 | }; | |
147 | ||
148 | template <typename Value_, void (JNIEnv::*Delete_)(jobject)> | |
149 | struct CYJavaDelete : | |
150 | CYJavaRef<Value_> | |
151 | { | |
152 | _finline CYJavaDelete(JNIEnv *jni, Value_ value) : | |
153 | CYJavaRef<Value_>(jni, value) | |
154 | { | |
155 | } | |
156 | ||
4e2cc9d3 | 157 | void clear() { |
fc664744 JF |
158 | if (this->value_ != NULL) |
159 | (this->jni_->*Delete_)(this->value_); | |
4e2cc9d3 JF |
160 | this->value_ = NULL; |
161 | } | |
162 | ||
163 | ~CYJavaDelete() { | |
164 | clear(); | |
fc664744 JF |
165 | } |
166 | }; | |
167 | ||
168 | template <typename Value_> | |
169 | struct CYJavaGlobal : | |
170 | CYJavaDelete<Value_, &JNIEnv::DeleteGlobalRef> | |
171 | { | |
172 | typedef CYJavaDelete<Value_, &JNIEnv::DeleteGlobalRef> CYJavaBase; | |
173 | ||
174 | CYJavaGlobal() : | |
175 | CYJavaBase(NULL, NULL) | |
176 | { | |
177 | } | |
178 | ||
179 | template <typename Other_> | |
180 | CYJavaGlobal(const CYJavaRef<Other_> &other) : | |
181 | CYJavaBase(other.jni_, static_cast<Other_>(other.jni_->NewGlobalRef(other.value_))) | |
182 | { | |
183 | } | |
184 | ||
185 | CYJavaGlobal(const CYJavaGlobal<Value_> &other) : | |
186 | CYJavaGlobal(static_cast<const CYJavaRef<Value_> &>(other)) | |
187 | { | |
188 | } | |
189 | ||
190 | CYJavaGlobal(CYJavaGlobal &&value) : | |
191 | CYJavaBase(value.jni_, value.value_) | |
192 | { | |
193 | value.value_ = NULL; | |
194 | } | |
195 | }; | |
196 | ||
197 | template <typename Value_> | |
198 | struct CYJavaLocal : | |
199 | CYJavaDelete<Value_, &JNIEnv::DeleteLocalRef> | |
200 | { | |
201 | typedef CYJavaDelete<Value_, &JNIEnv::DeleteLocalRef> CYJavaBase; | |
202 | ||
203 | CYJavaLocal() : | |
204 | CYJavaBase(NULL, NULL) | |
205 | { | |
206 | } | |
207 | ||
208 | CYJavaLocal(JNIEnv *jni, Value_ value) : | |
209 | CYJavaBase(jni, value) | |
210 | { | |
211 | } | |
212 | ||
213 | template <typename Other_> | |
214 | CYJavaLocal(const CYJavaRef<Other_> &other) : | |
215 | CYJavaLocal(other.jni_, static_cast<Other_>(other.jni_->NewLocalRef(other.value_))) | |
216 | { | |
217 | } | |
218 | ||
4e2cc9d3 JF |
219 | template <typename Other_> |
220 | CYJavaLocal(CYJavaRef<Other_> &&other) : | |
221 | CYJavaLocal(other.jni_, other.value_) | |
222 | { | |
223 | other.value_ = NULL; | |
224 | } | |
225 | ||
226 | CYJavaLocal(CYJavaLocal &&other) : | |
227 | CYJavaLocal(static_cast<CYJavaRef<Value_> &&>(other)) | |
fc664744 | 228 | { |
fc664744 JF |
229 | } |
230 | ||
4e2cc9d3 JF |
231 | template <typename Other_> |
232 | CYJavaLocal &operator =(CYJavaLocal<Other_> &&other) { | |
233 | this->clear(); | |
234 | this->jni_ = other.jni_; | |
235 | this->value_ = other.value_; | |
236 | other.value_ = NULL; | |
fc664744 JF |
237 | return *this; |
238 | } | |
239 | }; | |
240 | // }}} | |
241 | // Java Strings {{{ | |
242 | static CYJavaLocal<jstring> CYCastJavaString(const CYJavaRef<jobject> &value); | |
dbf05bfd JF |
243 | |
244 | class CYJavaUTF8String : | |
245 | public CYUTF8String | |
246 | { | |
247 | private: | |
fc664744 | 248 | const CYJavaRef<jstring> *value_; |
dbf05bfd JF |
249 | |
250 | public: | |
fc664744 JF |
251 | CYJavaUTF8String(const CYJavaRef<jstring> &value) : |
252 | value_(&value) | |
253 | { | |
254 | _assert(value); | |
477549f5 | 255 | JNIEnv *jni(value.jni()); |
fc664744 JF |
256 | size = jni->GetStringUTFLength(value); |
257 | data = jni->GetStringUTFChars(value, NULL); | |
258 | } | |
259 | ||
260 | CYJavaUTF8String(const CYJavaRef<jobject> &value) : | |
261 | CYJavaUTF8String(CYCastJavaString(value)) | |
dbf05bfd | 262 | { |
dbf05bfd JF |
263 | } |
264 | ||
265 | ~CYJavaUTF8String() { | |
fc664744 | 266 | if (value_ != NULL) { |
477549f5 | 267 | JNIEnv *jni(value_->jni()); |
fc664744 JF |
268 | jni->ReleaseStringUTFChars(*value_, data); |
269 | } | |
dbf05bfd JF |
270 | } |
271 | ||
272 | CYJavaUTF8String(const CYJavaUTF8String &) = delete; | |
273 | ||
274 | CYJavaUTF8String(CYJavaUTF8String &&rhs) : | |
dbf05bfd JF |
275 | value_(rhs.value_) |
276 | { | |
277 | rhs.value_ = NULL; | |
278 | } | |
279 | }; | |
280 | ||
fc664744 JF |
281 | CYJavaUTF8String CYCastUTF8String(const CYJavaRef<jstring> &value) { |
282 | return CYJavaUTF8String(value); | |
dbf05bfd JF |
283 | } |
284 | ||
fc664744 JF |
285 | JSStringRef CYCopyJSString(const CYJavaRef<jstring> &value) { |
286 | return CYCopyJSString(CYCastUTF8String(value)); | |
dbf05bfd | 287 | } |
fc664744 JF |
288 | // }}} |
289 | // Java Error {{{ | |
290 | struct CYJavaError : | |
291 | CYException | |
292 | { | |
293 | CYJavaGlobal<jthrowable> value_; | |
dbf05bfd | 294 | |
fc664744 JF |
295 | CYJavaError(const CYJavaRef<jthrowable> &value) : |
296 | value_(value) | |
297 | { | |
298 | } | |
299 | ||
300 | virtual const char *PoolCString(CYPool &pool) const { | |
301 | return CYPoolCString(pool, CYJavaUTF8String(value_.cast<jobject>())); | |
302 | } | |
303 | ||
477549f5 | 304 | virtual JSValueRef CastJSValue(JSContextRef context, const char *name) const; |
fc664744 JF |
305 | }; |
306 | // }}} | |
307 | ||
308 | struct CYJavaFrame { | |
dbf05bfd | 309 | JNIEnv *jni_; |
dbf05bfd | 310 | |
fc664744 JF |
311 | CYJavaFrame(JNIEnv *jni, jint capacity) : |
312 | jni_(jni) | |
dbf05bfd | 313 | { |
fc664744 | 314 | _assert(jni->PushLocalFrame(capacity) == 0); |
dbf05bfd JF |
315 | } |
316 | ||
fc664744 JF |
317 | ~CYJavaFrame() { |
318 | operator ()(NULL); | |
dbf05bfd JF |
319 | } |
320 | ||
4e2cc9d3 JF |
321 | operator JNIEnv *() const { |
322 | return jni_; | |
323 | } | |
324 | ||
fc664744 JF |
325 | jobject operator ()(jobject object) { |
326 | JNIEnv *jni(jni_); | |
327 | jni_ = NULL; | |
328 | return jni->PopLocalFrame(object); | |
dbf05bfd | 329 | } |
fc664744 | 330 | }; |
dbf05bfd | 331 | |
fc664744 JF |
332 | struct CYJavaEnv { |
333 | private: | |
334 | JNIEnv *jni; | |
335 | ||
336 | public: | |
337 | CYJavaEnv(JNIEnv *jni) : | |
338 | jni(jni) | |
dbf05bfd | 339 | { |
dbf05bfd JF |
340 | } |
341 | ||
477549f5 JF |
342 | template <typename Other_> |
343 | CYJavaEnv(const CYJavaRef<Other_> &value) : | |
344 | jni(value.jni()) | |
345 | { | |
346 | } | |
347 | ||
fc664744 JF |
348 | operator JNIEnv *() const { |
349 | return jni; | |
dbf05bfd JF |
350 | } |
351 | ||
fc664744 JF |
352 | JNIEnv *operator ->() const { |
353 | return jni; | |
dbf05bfd JF |
354 | } |
355 | ||
fc664744 JF |
356 | CYJavaLocal<jclass> FindClass(const char *name) const { |
357 | return {jni, _envcall(jni, FindClass(name))}; | |
dbf05bfd JF |
358 | } |
359 | ||
fc664744 JF |
360 | CYJavaLocal<jclass> GetObjectClass(jobject object) const { |
361 | return {jni, _envcall(jni, GetObjectClass(object))}; | |
362 | } | |
363 | ||
364 | CYJavaLocal<jclass> GetSuperclass(jclass _class) const { | |
365 | return {jni, _envcall(jni, GetSuperclass(_class))}; | |
366 | } | |
367 | ||
368 | CYJavaLocal<jobject> NewObject(jclass _class, jmethodID method, ...) const { | |
369 | va_list args; | |
370 | va_start(args, method); | |
371 | jobject object(_envcall(jni, NewObjectV(_class, method, args))); | |
372 | va_end(args); | |
373 | return {jni, object}; | |
374 | } | |
375 | ||
376 | CYJavaLocal<jobject> NewObjectA(jclass _class, jmethodID method, jvalue *args) const { | |
377 | return {jni, _envcall(jni, NewObjectA(_class, method, args))}; | |
dbf05bfd | 378 | } |
fc664744 JF |
379 | |
380 | CYJavaLocal<jstring> NewString(const jchar *data, jsize size) const { | |
381 | return {jni, _envcall(jni, NewString(data, size))}; | |
382 | } | |
383 | ||
384 | #define CYJavaEnv_(Code) \ | |
385 | template <typename... Args_> \ | |
386 | void Code(Args_ &&... args) const { \ | |
387 | _envcallv(jni, Code(cy::Forward<Args_>(args)...)); \ | |
388 | } | |
389 | ||
390 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ | |
391 | CYJavaEnv_(Get ## Typ ## ArrayRegion) \ | |
392 | CYJavaEnv_(Set ## Typ ## Field) \ | |
393 | CYJavaEnv_(SetStatic ## Typ ## Field) | |
394 | CYJavaForEachPrimitive | |
395 | #undef CYJavaForEachPrimitive_ | |
396 | ||
397 | CYJavaEnv_(CallVoidMethod) | |
398 | CYJavaEnv_(CallStaticVoidMethod) | |
399 | CYJavaEnv_(CallVoidMethodA) | |
400 | CYJavaEnv_(CallStaticVoidMethodA) | |
401 | CYJavaEnv_(SetObjectArrayElement) | |
402 | CYJavaEnv_(SetObjectField) | |
403 | CYJavaEnv_(SetStaticObjectField) | |
404 | #undef CYJavaEnv_ | |
405 | ||
406 | #define CYJavaEnv_(Code) \ | |
407 | template <typename... Args_> \ | |
0e832de4 | 408 | auto Code(Args_ &&... args) const -> decltype(jni->Code(cy::Forward<Args_>(args)...)) { \ |
fc664744 JF |
409 | return _envcall(jni, Code(cy::Forward<Args_>(args)...)); \ |
410 | } | |
411 | ||
412 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ | |
413 | CYJavaEnv_(Call ## Typ ## Method) \ | |
414 | CYJavaEnv_(CallStatic ## Typ ## Method) \ | |
415 | CYJavaEnv_(Call ## Typ ## MethodA) \ | |
416 | CYJavaEnv_(CallStatic ## Typ ## MethodA) \ | |
417 | CYJavaEnv_(Get ## Typ ## Field) \ | |
418 | CYJavaEnv_(GetStatic ## Typ ## Field) | |
419 | CYJavaForEachPrimitive | |
420 | #undef CYJavaForEachPrimitive_ | |
421 | ||
422 | CYJavaEnv_(FromReflectedField) | |
423 | CYJavaEnv_(FromReflectedMethod) | |
424 | CYJavaEnv_(GetArrayLength) | |
425 | CYJavaEnv_(GetMethodID) | |
426 | CYJavaEnv_(GetStaticMethodID) | |
427 | CYJavaEnv_(IsSameObject) | |
f2c357f9 | 428 | CYJavaEnv_(RegisterNatives) |
fc664744 JF |
429 | #undef CYJavaEnv_ |
430 | ||
431 | #define CYJavaEnv_(Code) \ | |
432 | template <typename Other_, typename... Args_> \ | |
433 | auto Code(Args_ &&... args) const -> CYJavaLocal<Other_> { \ | |
434 | return {jni, static_cast<Other_>(_envcall(jni, Code(cy::Forward<Args_>(args)...)))}; \ | |
435 | } | |
436 | ||
437 | CYJavaEnv_(CallObjectMethod) | |
438 | CYJavaEnv_(CallStaticObjectMethod) | |
439 | CYJavaEnv_(CallObjectMethodA) | |
440 | CYJavaEnv_(CallStaticObjectMethodA) | |
441 | CYJavaEnv_(GetObjectArrayElement) | |
442 | CYJavaEnv_(GetObjectField) | |
443 | CYJavaEnv_(GetStaticObjectField) | |
444 | #undef CYJavaEnv_ | |
dbf05bfd JF |
445 | }; |
446 | ||
fc664744 JF |
447 | static CYJavaLocal<jstring> CYCastJavaString(const CYJavaRef<jobject> &value) { |
448 | CYJavaEnv jni(value); | |
449 | auto Object$(jni.FindClass("java/lang/Object")); | |
450 | auto Object$toString(jni.GetMethodID(Object$, "toString", "()Ljava/lang/String;")); | |
451 | return jni.CallObjectMethod<jstring>(value, Object$toString); | |
452 | } | |
453 | ||
dbf05bfd JF |
454 | template <typename Internal_, typename Value_> |
455 | struct CYJavaValue : | |
456 | CYPrivate<Internal_> | |
457 | { | |
458 | CYJavaGlobal<Value_> value_; | |
459 | ||
fc664744 JF |
460 | CYJavaValue(const CYJavaRef<Value_> &value) : |
461 | value_(value) | |
dbf05bfd JF |
462 | { |
463 | } | |
464 | ||
465 | CYJavaValue(const CYJavaValue &) = delete; | |
466 | }; | |
467 | ||
477549f5 JF |
468 | static JSValueRef CYCastJSValue(JSContextRef context, const CYJavaRef<jobject> &value); |
469 | ||
470 | template <typename Other_> | |
471 | static _finline JSValueRef CYCastJSValue(JSContextRef context, const CYJavaRef<Other_> &value) { | |
472 | return CYCastJSValue(context, value.template cast<jobject>()); | |
473 | } | |
474 | ||
dbf05bfd JF |
475 | template <typename Type_> |
476 | static _finline JSValueRef CYJavaCastJSValue(JSContextRef context, Type_ value) { | |
477 | return CYCastJSValue(context, value); | |
478 | } | |
479 | ||
480 | static _finline JSValueRef CYJavaCastJSValue(JSContextRef context, jboolean value) { | |
481 | return CYCastJSValue(context, static_cast<bool>(value)); | |
482 | } | |
483 | ||
477549f5 JF |
484 | JSValueRef CYJavaError::CastJSValue(JSContextRef context, const char *name) const { |
485 | return CYCastJSValue(context, value_); | |
486 | } | |
487 | ||
dbf05bfd JF |
488 | static std::map<std::string, CYJavaPrimitive> Primitives_; |
489 | ||
fc664744 JF |
490 | static CYJavaPrimitive CYJavaGetPrimitive(JSContextRef context, const CYJavaRef<jclass> &type, jmethodID Class$get$$Name) { |
491 | CYJavaEnv jni(type); | |
492 | auto string(jni.CallObjectMethod<jstring>(type, Class$get$$Name)); | |
493 | _assert(string); | |
494 | ||
495 | CYJavaUTF8String name(string); | |
dbf05bfd JF |
496 | auto primitive(Primitives_.find(name)); |
497 | return primitive != Primitives_.end() ? primitive->second : CYJavaPrimitiveObject; | |
498 | } | |
499 | ||
500 | typedef std::vector<CYJavaPrimitive> CYJavaShorty; | |
501 | ||
fc664744 JF |
502 | static CYJavaShorty CYJavaGetShorty(JSContextRef context, const CYJavaRef<jobjectArray> &types, jmethodID Class$get$$Name) { |
503 | CYJavaEnv jni(types); | |
504 | size_t count(jni.GetArrayLength(types)); | |
dbf05bfd JF |
505 | CYJavaShorty shorty(count); |
506 | for (size_t index(0); index != count; ++index) | |
fc664744 | 507 | shorty[index] = CYJavaGetPrimitive(context, jni.GetObjectArrayElement<jclass>(types, index), Class$get$$Name); |
dbf05bfd JF |
508 | return shorty; |
509 | } | |
510 | ||
511 | struct CYJavaField { | |
512 | jfieldID field_; | |
513 | CYJavaPrimitive primitive_; | |
514 | }; | |
515 | ||
516 | typedef std::map<std::string, CYJavaField> CYJavaFieldMap; | |
517 | ||
518 | struct CYJavaSignature { | |
4b645e23 | 519 | CYJavaGlobal<jobject> reflected_; |
fc664744 | 520 | jmethodID method_; |
4b645e23 JF |
521 | CYJavaPrimitive primitive_; |
522 | CYJavaShorty shorty_; | |
523 | ||
fc664744 JF |
524 | CYJavaSignature(const CYJavaRef<jobject> &reflected, jmethodID method, CYJavaPrimitive primitive, const CYJavaShorty &shorty) : |
525 | reflected_(reflected), | |
4b645e23 | 526 | method_(method), |
4b645e23 JF |
527 | primitive_(primitive), |
528 | shorty_(shorty) | |
dbf05bfd JF |
529 | { |
530 | } | |
531 | ||
532 | CYJavaSignature(unsigned count) : | |
4b645e23 | 533 | shorty_(count) |
dbf05bfd JF |
534 | { |
535 | } | |
536 | ||
537 | bool operator <(const CYJavaSignature &rhs) const { | |
4b645e23 | 538 | return shorty_.size() < rhs.shorty_.size(); |
dbf05bfd JF |
539 | } |
540 | }; | |
541 | ||
542 | typedef std::multiset<CYJavaSignature> CYJavaOverload; | |
543 | ||
544 | struct CYJavaMethod : | |
545 | CYPrivate<CYJavaMethod> | |
546 | { | |
dbf05bfd JF |
547 | CYJavaOverload overload_; |
548 | ||
c9c16dde JF |
549 | CYJavaMethod(const CYJavaOverload &overload) : |
550 | overload_(overload) | |
551 | { | |
552 | } | |
553 | }; | |
554 | ||
555 | struct CYJavaStaticMethod : | |
556 | CYPrivate<CYJavaStaticMethod> | |
557 | { | |
558 | CYJavaOverload overload_; | |
559 | ||
560 | CYJavaStaticMethod(const CYJavaOverload &overload) : | |
dbf05bfd JF |
561 | overload_(overload) |
562 | { | |
563 | } | |
564 | }; | |
565 | ||
566 | struct CYJavaClass : | |
567 | CYJavaValue<CYJavaClass, jclass> | |
568 | { | |
c9c16dde JF |
569 | bool interface_; |
570 | ||
dbf05bfd JF |
571 | CYJavaFieldMap static_; |
572 | CYJavaFieldMap instance_; | |
573 | CYJavaOverload overload_; | |
574 | ||
fc664744 JF |
575 | CYJavaClass(const CYJavaRef<jclass> &value, bool interface) : |
576 | CYJavaValue(value), | |
c9c16dde | 577 | interface_(interface) |
dbf05bfd JF |
578 | { |
579 | } | |
580 | }; | |
581 | ||
fc664744 | 582 | static JSObjectRef CYGetJavaClass(JSContextRef context, const CYJavaRef<jclass> &_class); |
8effd381 | 583 | |
dbf05bfd JF |
584 | struct CYJavaObject : |
585 | CYJavaValue<CYJavaObject, jobject> | |
586 | { | |
8effd381 JF |
587 | CYJavaClass *table_; |
588 | ||
fc664744 JF |
589 | CYJavaObject(const CYJavaRef<jobject> &value, CYJavaClass *table) : |
590 | CYJavaValue(value), | |
4b645e23 | 591 | table_(table) |
dbf05bfd JF |
592 | { |
593 | } | |
594 | ||
595 | JSValueRef GetPrototype(JSContextRef context) const; | |
596 | }; | |
597 | ||
8effd381 JF |
598 | struct CYJavaInterior : |
599 | CYJavaValue<CYJavaInterior, jobject> | |
600 | { | |
601 | CYJavaClass *table_; | |
602 | ||
fc664744 JF |
603 | CYJavaInterior(const CYJavaRef<jobject> &value, CYJavaClass *table) : |
604 | CYJavaValue(value), | |
8effd381 JF |
605 | table_(table) |
606 | { | |
607 | } | |
608 | }; | |
609 | ||
c9c16dde | 610 | struct CYJavaStaticInterior : |
fc664744 | 611 | CYJavaValue<CYJavaStaticInterior, jclass> |
4b645e23 JF |
612 | { |
613 | CYJavaClass *table_; | |
614 | ||
fc664744 JF |
615 | CYJavaStaticInterior(const CYJavaRef<jclass> &value, CYJavaClass *table) : |
616 | CYJavaValue(value), | |
4b645e23 JF |
617 | table_(table) |
618 | { | |
619 | } | |
620 | }; | |
621 | ||
622 | struct CYJavaArray : | |
623 | CYJavaValue<CYJavaArray, jarray> | |
624 | { | |
625 | CYJavaPrimitive primitive_; | |
626 | ||
fc664744 JF |
627 | CYJavaArray(const CYJavaRef<jarray> &value, CYJavaPrimitive primitive) : |
628 | CYJavaValue(value), | |
4b645e23 JF |
629 | primitive_(primitive) |
630 | { | |
631 | } | |
632 | ||
633 | JSValueRef GetPrototype(JSContextRef context) const; | |
634 | }; | |
635 | ||
dbf05bfd JF |
636 | struct CYJavaPackage : |
637 | CYPrivate<CYJavaPackage> | |
638 | { | |
639 | typedef std::vector<std::string> Path; | |
640 | Path package_; | |
641 | ||
642 | _finline CYJavaPackage(const Path &package) : | |
643 | package_(package) | |
644 | { | |
645 | } | |
646 | }; | |
647 | ||
dbf05bfd | 648 | JSValueRef CYJavaObject::GetPrototype(JSContextRef context) const { |
fc664744 JF |
649 | CYJavaEnv jni(value_); |
650 | return CYGetProperty(context, CYGetJavaClass(context, jni.GetObjectClass(value_)), prototype_s); | |
dbf05bfd JF |
651 | } |
652 | ||
4b645e23 JF |
653 | JSValueRef CYJavaArray::GetPrototype(JSContextRef context) const { |
654 | return CYGetCachedObject(context, CYJSString("Array_prototype")); | |
655 | } | |
656 | ||
fc664744 JF |
657 | static JSValueRef CYCastJSValue(JSContextRef context, const CYJavaRef<jobject> &value) { |
658 | if (!value) | |
dbf05bfd | 659 | return CYJSNull(context); |
fc664744 JF |
660 | CYJavaEnv jni(value); |
661 | ||
662 | auto _class(jni.GetObjectClass(value)); | |
663 | if (jni.IsSameObject(_class, jni.FindClass("java/lang/String"))) | |
664 | return CYCastJSValue(context, CYJSString(value.cast<jstring>())); | |
665 | ||
666 | auto Class$(jni.FindClass("java/lang/Class")); | |
667 | auto Class$isArray(jni.GetMethodID(Class$, "isArray", "()Z")); | |
668 | if (jni.CallBooleanMethod(_class, Class$isArray)) { | |
669 | auto Class$getComponentType(jni.GetMethodID(Class$, "getComponentType", "()Ljava/lang/Class;")); | |
670 | auto component(jni.CallObjectMethod<jclass>(_class, Class$getComponentType)); | |
671 | auto Class$getName(jni.GetMethodID(Class$, "getName", "()Ljava/lang/String;")); | |
672 | return CYJavaArray::Make(context, value.cast<jarray>(), CYJavaGetPrimitive(context, component, Class$getName)); | |
c9c16dde JF |
673 | } |
674 | ||
fc664744 JF |
675 | auto Wrapper$(jni.FindClass("Cycript$Wrapper")); |
676 | if (jni.IsSameObject(_class, Wrapper$)) { | |
677 | auto Wrapper$getProtect(jni.GetMethodID(Wrapper$, "getProtect", "()J")); | |
678 | auto &protect(*reinterpret_cast<CYProtect *>(jni.CallLongMethod(value, Wrapper$getProtect))); | |
c9c16dde | 679 | return protect; |
4b645e23 JF |
680 | } |
681 | ||
fc664744 JF |
682 | CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(CYGetJavaClass(context, _class)))); |
683 | return CYJavaObject::Make(context, value, table); | |
dbf05bfd JF |
684 | } |
685 | ||
fc664744 JF |
686 | static _finline JSObjectRef CYCastJSObject(JSContextRef context, const CYJavaRef<jobject> &value) { |
687 | return CYCastJSObject(context, CYCastJSValue(context, value)); | |
c9c16dde JF |
688 | } |
689 | ||
fc664744 JF |
690 | static CYJavaLocal<jstring> CYCastJavaString(const CYJavaEnv &jni, JSContextRef context, CYUTF16String value) { |
691 | return jni.NewString(value.data, value.size); | |
dbf05bfd JF |
692 | } |
693 | ||
fc664744 | 694 | static CYJavaLocal<jstring> CYCastJavaString(const CYJavaEnv &jni, JSContextRef context, JSStringRef value) { |
c9c16dde | 695 | return CYCastJavaString(jni, context, CYCastUTF16String(value)); |
dbf05bfd JF |
696 | } |
697 | ||
698 | #define CYCastJava$(T, Type, jtype, Cast) \ | |
fc664744 JF |
699 | _disused static CYJavaLocal<jobject> CYCastJava ## Type(const CYJavaEnv &jni, JSContextRef context, JSValueRef value) { \ |
700 | auto Type$(jni.FindClass("java/lang/" #Type)); \ | |
701 | auto Type$init$(jni.GetMethodID(Type$, "<init>", "(" #T ")V")); \ | |
702 | return jni.NewObject(Type$, Type$init$, static_cast<jtype>(Cast(context, value))); \ | |
dbf05bfd JF |
703 | } |
704 | ||
705 | CYCastJava$(Z, Boolean, jboolean, CYCastBool) | |
706 | CYCastJava$(B, Byte, jbyte, CYCastDouble) | |
707 | CYCastJava$(C, Character, jchar, CYCastDouble) | |
708 | CYCastJava$(S, Short, jshort, CYCastDouble) | |
709 | CYCastJava$(I, Integer, jint, CYCastDouble) | |
710 | CYCastJava$(J, Long, jlong, CYCastDouble) | |
711 | CYCastJava$(F, Float, jfloat, CYCastDouble) | |
712 | CYCastJava$(D, Double, jdouble, CYCastDouble) | |
713 | ||
c9c16dde JF |
714 | static CYJavaClass *CYGetJavaTable(JSContextRef context, JSObjectRef object) { |
715 | if (!JSValueIsObjectOfClass(context, object, CYJavaClass::Class_)) | |
716 | return NULL; | |
717 | return reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)); | |
718 | } | |
719 | ||
720 | static CYJavaObject *CYGetJavaObject(JSContextRef context, JSObjectRef object) { | |
721 | if (!JSValueIsObjectOfClass(context, object, CYJavaObject::Class_)) | |
722 | return NULL; | |
723 | return reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)); | |
724 | } | |
725 | ||
fc664744 | 726 | static CYJavaLocal<jobject> CYCastJavaObject(const CYJavaEnv &jni, JSContextRef context, JSObjectRef value) { |
c9c16dde | 727 | if (CYJavaObject *internal = CYGetJavaObject(context, value)) |
dbf05bfd | 728 | return internal->value_; |
dbf05bfd | 729 | |
fc664744 JF |
730 | auto Wrapper$(jni.FindClass("Cycript$Wrapper")); |
731 | auto Wrapper$$init$(jni.GetMethodID(Wrapper$, "<init>", "(J)V")); | |
c9c16dde | 732 | CYProtect *protect(new CYProtect(context, value)); |
fc664744 | 733 | return jni.NewObject(Wrapper$, Wrapper$$init$, reinterpret_cast<jlong>(protect)); |
dbf05bfd JF |
734 | } |
735 | ||
fc664744 | 736 | static CYJavaLocal<jobject> CYCastJavaObject(const CYJavaEnv &jni, JSContextRef context, JSValueRef value) { |
dbf05bfd JF |
737 | switch (JSValueGetType(context, value)) { |
738 | case kJSTypeNull: | |
fc664744 | 739 | return {jni, NULL}; |
dbf05bfd JF |
740 | case kJSTypeBoolean: |
741 | return CYCastJavaBoolean(jni, context, value); | |
742 | case kJSTypeNumber: | |
743 | return CYCastJavaDouble(jni, context, value); | |
744 | case kJSTypeString: | |
c9c16dde | 745 | return CYCastJavaString(jni, context, CYJSString(context, value)); |
dbf05bfd JF |
746 | case kJSTypeObject: |
747 | return CYCastJavaObject(jni, context, CYCastJSObject(context, value)); | |
748 | ||
749 | case kJSTypeUndefined: | |
c9c16dde | 750 | // XXX: I am currently relying on this for dynamic proxy of void method |
fc664744 | 751 | return {jni, NULL}; |
dbf05bfd JF |
752 | default: |
753 | _assert(false); | |
754 | } | |
755 | } | |
756 | ||
fc664744 JF |
757 | static JSObjectRef CYGetJavaClass(JSContextRef context, const CYJavaRef<jclass> &value) { |
758 | CYJavaEnv jni(value); | |
759 | CYJavaFrame frame(jni, 64); | |
760 | ||
dbf05bfd JF |
761 | JSObjectRef global(CYGetGlobalObject(context)); |
762 | JSObjectRef cy(CYCastJSObject(context, CYGetProperty(context, global, cy_s))); | |
763 | ||
fc664744 JF |
764 | auto Class$(jni.FindClass("java/lang/Class")); |
765 | auto Class$getName(jni.GetMethodID(Class$, "getName", "()Ljava/lang/String;")); | |
dbf05bfd | 766 | |
fc664744 | 767 | CYJSString name(jni.CallObjectMethod<jstring>(value, Class$getName)); |
dbf05bfd JF |
768 | JSValueRef cached(CYGetProperty(context, cy, name)); |
769 | if (!JSValueIsUndefined(context, cached)) | |
770 | return CYCastJSObject(context, cached); | |
771 | ||
fc664744 JF |
772 | JSObjectRef constructor; |
773 | JSObjectRef prototype; | |
c9c16dde | 774 | |
fc664744 | 775 | { |
dbf05bfd | 776 | |
fc664744 | 777 | auto Class$isInterface(jni.GetMethodID(Class$, "isInterface", "()Z")); |
dbf05bfd | 778 | |
fc664744 JF |
779 | auto Class$getDeclaredConstructors(jni.GetMethodID(Class$, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;")); |
780 | auto Class$getDeclaredFields(jni.GetMethodID(Class$, "getDeclaredFields", "()[Ljava/lang/reflect/Field;")); | |
781 | auto Class$getDeclaredMethods(jni.GetMethodID(Class$, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;")); | |
dbf05bfd | 782 | |
fc664744 JF |
783 | auto Constructor$(jni.FindClass("java/lang/reflect/Constructor")); |
784 | //auto Constructor$getModifiers(jni.GetMethodID(Constructor$, "getModifiers", "()I")); | |
785 | auto Constructor$getParameterTypes(jni.GetMethodID(Constructor$, "getParameterTypes", "()[Ljava/lang/Class;")); | |
dbf05bfd | 786 | |
fc664744 JF |
787 | auto Field$(jni.FindClass("java/lang/reflect/Field")); |
788 | auto Field$getModifiers(jni.GetMethodID(Field$, "getModifiers", "()I")); | |
789 | auto Field$getName(jni.GetMethodID(Field$, "getName", "()Ljava/lang/String;")); | |
790 | auto Field$getType(jni.GetMethodID(Field$, "getType", "()Ljava/lang/Class;")); | |
dbf05bfd | 791 | |
fc664744 JF |
792 | auto Method$(jni.FindClass("java/lang/reflect/Method")); |
793 | auto Method$getModifiers(jni.GetMethodID(Method$, "getModifiers", "()I")); | |
794 | auto Method$getName(jni.GetMethodID(Method$, "getName", "()Ljava/lang/String;")); | |
795 | auto Method$getParameterTypes(jni.GetMethodID(Method$, "getParameterTypes", "()[Ljava/lang/Class;")); | |
796 | auto Method$getReturnType(jni.GetMethodID(Method$, "getReturnType", "()Ljava/lang/Class;")); | |
dbf05bfd | 797 | |
fc664744 JF |
798 | auto Modifier$(jni.FindClass("java/lang/reflect/Modifier")); |
799 | auto Modifier$isStatic(jni.GetStaticMethodID(Modifier$, "isStatic", "(I)Z")); | |
dbf05bfd | 800 | |
fc664744 JF |
801 | auto interface(jni.CallBooleanMethod(value, Class$isInterface)); |
802 | auto table(new CYJavaClass(value, interface)); | |
803 | ||
804 | for (CYJavaLocal<jclass> prototype(value); prototype; prototype = jni.GetSuperclass(prototype)) { | |
805 | auto fields(jni.CallObjectMethod<jobjectArray>(prototype, Class$getDeclaredFields)); | |
806 | ||
807 | for (jsize i(0), e(jni.GetArrayLength(fields)); i != e; ++i) { | |
808 | auto field(jni.GetObjectArrayElement<jobject>(fields, e - i - 1)); | |
809 | auto modifiers(jni.CallIntMethod(field, Field$getModifiers)); | |
810 | auto instance(!jni.CallStaticBooleanMethod(Modifier$, Modifier$isStatic, modifiers)); | |
dbf05bfd | 811 | auto &map(instance ? table->instance_ : table->static_); |
fc664744 JF |
812 | CYJavaUTF8String name(jni.CallObjectMethod<jstring>(field, Field$getName)); |
813 | auto id(jni.FromReflectedField(field)); | |
814 | auto type(jni.CallObjectMethod<jclass>(field, Field$getType)); | |
815 | map.insert(std::make_pair(std::string(name), CYJavaField{id, CYJavaGetPrimitive(context, type, Class$getName)})); | |
dbf05bfd JF |
816 | } |
817 | } | |
818 | ||
fc664744 | 819 | constructor = JSObjectMake(context, CYJavaClass::Class_, table); |
dbf05bfd | 820 | |
fc664744 | 821 | prototype = JSObjectMake(context, NULL, NULL); |
dbf05bfd JF |
822 | CYSetProperty(context, constructor, prototype_s, prototype, kJSPropertyAttributeDontEnum); |
823 | ||
fc664744 | 824 | auto constructors(jni.CallObjectMethod<jobjectArray>(value, Class$getDeclaredConstructors)); |
dbf05bfd | 825 | |
fc664744 JF |
826 | for (jsize i(0), e(jni.GetArrayLength(constructors)); i != e; ++i) { |
827 | auto constructor(jni.GetObjectArrayElement<jobject>(constructors, i)); | |
828 | auto parameters(jni.CallObjectMethod<jobjectArray>(constructor, Constructor$getParameterTypes)); | |
829 | CYJavaShorty shorty(CYJavaGetShorty(context, parameters, Class$getName)); | |
830 | auto id(jni.FromReflectedMethod(constructor)); | |
831 | table->overload_.insert(CYJavaSignature(constructor, id, CYJavaPrimitiveObject, shorty)); | |
dbf05bfd JF |
832 | } |
833 | ||
fc664744 | 834 | auto methods(jni.CallObjectMethod<jobjectArray>(value, Class$getDeclaredMethods)); |
dbf05bfd JF |
835 | |
836 | std::map<std::pair<bool, std::string>, CYJavaOverload> entries; | |
837 | ||
fc664744 JF |
838 | for (jsize i(0), e(jni.GetArrayLength(methods)); i != e; ++i) { |
839 | auto method(jni.GetObjectArrayElement<jobject>(methods, i)); | |
840 | auto modifiers(jni.CallIntMethod(method, Method$getModifiers)); | |
841 | auto instance(!jni.CallStaticBooleanMethod(Modifier$, Modifier$isStatic, modifiers)); | |
842 | CYJavaUTF8String name(jni.CallObjectMethod<jstring>(method, Method$getName)); | |
843 | auto parameters(jni.CallObjectMethod<jobjectArray>(method, Method$getParameterTypes)); | |
844 | CYJavaShorty shorty(CYJavaGetShorty(context, parameters, Class$getName)); | |
845 | auto type(jni.CallObjectMethod<jclass>(method, Method$getReturnType)); | |
846 | auto primitive(CYJavaGetPrimitive(context, type, Class$getName)); | |
847 | auto id(jni.FromReflectedMethod(method)); | |
848 | entries[std::make_pair(instance, std::string(name))].insert(CYJavaSignature(method, id, primitive, shorty)); | |
dbf05bfd JF |
849 | } |
850 | ||
851 | for (const auto &entry : entries) { | |
852 | bool instance(entry.first.first); | |
853 | CYJSString name(entry.first.second); | |
854 | auto &overload(entry.second); | |
c9c16dde JF |
855 | if (instance) |
856 | CYSetProperty(context, prototype, name, CYJavaMethod::Make(context, overload), kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete); | |
857 | else | |
858 | CYSetProperty(context, constructor, name, CYJavaStaticMethod::Make(context, overload), kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete); | |
dbf05bfd JF |
859 | } |
860 | ||
fc664744 JF |
861 | } |
862 | ||
dbf05bfd JF |
863 | // XXX: for some reason kJSPropertyAttributeDontEnum doesn't work if there's already a property with the same name |
864 | // by not linking the prototypes until after we set the properties, we hide the parent property from this issue :( | |
865 | ||
fc664744 JF |
866 | if (auto super = jni.GetSuperclass(value)) { |
867 | JSObjectRef parent(CYGetJavaClass(context, super)); | |
4b645e23 | 868 | CYSetPrototype(context, constructor, parent); |
dbf05bfd JF |
869 | CYSetPrototype(context, prototype, CYGetProperty(context, parent, prototype_s)); |
870 | } | |
871 | ||
872 | CYSetProperty(context, cy, name, constructor); | |
873 | return constructor; | |
874 | } | |
875 | ||
4b645e23 JF |
876 | static void CYCastJavaNumeric(jvalue &value, CYJavaPrimitive primitive, JSContextRef context, JSValueRef argument) { |
877 | switch (primitive) { | |
878 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ | |
879 | case CYJavaPrimitive ## Type: \ | |
880 | value.t = static_cast<j ## type>(CYCastDouble(context, argument)); \ | |
881 | break; | |
dbf05bfd JF |
882 | CYJavaForEachPrimitive |
883 | #undef CYJavaForEachPrimitive_ | |
4b645e23 JF |
884 | default: |
885 | _assert(false); | |
886 | } | |
887 | } | |
888 | ||
4e2cc9d3 JF |
889 | static bool CYCastJavaArguments(const CYJavaFrame &frame, const CYJavaShorty &shorty, JSContextRef context, const JSValueRef arguments[], jvalue *array) { |
890 | CYJavaEnv jni(frame); | |
891 | ||
4b645e23 JF |
892 | for (size_t index(0); index != shorty.size(); ++index) { |
893 | JSValueRef argument(arguments[index]); | |
894 | JSType type(JSValueGetType(context, argument)); | |
895 | jvalue &value(array[index]); | |
896 | ||
897 | switch (CYJavaPrimitive primitive = shorty[index]) { | |
898 | case CYJavaPrimitiveObject: | |
4e2cc9d3 | 899 | // XXX: figure out a way to tie this in to the CYJavaFrame |
fc664744 | 900 | value.l = CYCastJavaObject(jni, context, argument).leak(); |
4b645e23 JF |
901 | break; |
902 | ||
903 | case CYJavaPrimitiveBoolean: | |
904 | if (type != kJSTypeBoolean) | |
905 | return false; | |
906 | value.z = CYCastBool(context, argument); | |
907 | break; | |
908 | ||
909 | case CYJavaPrimitiveCharacter: | |
910 | if (type == kJSTypeNumber) | |
911 | CYCastJavaNumeric(value, primitive, context, argument); | |
912 | else if (type != kJSTypeString) | |
913 | return false; | |
914 | else { | |
915 | CYJSString string(context, argument); | |
916 | if (JSStringGetLength(string) != 1) | |
917 | return false; | |
918 | else | |
919 | value.c = JSStringGetCharactersPtr(string)[0]; | |
920 | } | |
921 | break; | |
922 | ||
923 | case CYJavaPrimitiveByte: | |
924 | case CYJavaPrimitiveShort: | |
925 | case CYJavaPrimitiveInteger: | |
926 | case CYJavaPrimitiveLong: | |
927 | case CYJavaPrimitiveFloat: | |
928 | case CYJavaPrimitiveDouble: | |
929 | if (type != kJSTypeNumber) | |
930 | return false; | |
931 | CYCastJavaNumeric(value, primitive, context, argument); | |
932 | break; | |
933 | ||
dbf05bfd JF |
934 | default: |
935 | _assert(false); | |
936 | } | |
dbf05bfd JF |
937 | } |
938 | ||
4b645e23 | 939 | return true; |
dbf05bfd JF |
940 | } |
941 | ||
942 | static JSValueRef JavaMethod_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { | |
0b5f88f6 | 943 | auto internal(CYJavaMethod::Get(context, object)); |
c9c16dde | 944 | CYJavaObject *self(CYGetJavaObject(context, _this)); |
fc664744 | 945 | CYJavaEnv jni(self->value_); |
c9c16dde JF |
946 | |
947 | CYJavaSignature bound(count); | |
948 | for (auto overload(internal->overload_.lower_bound(bound)), e(internal->overload_.upper_bound(bound)); overload != e; ++overload) { | |
4e2cc9d3 | 949 | CYJavaFrame frame(jni, count + 16); |
c9c16dde | 950 | jvalue array[count]; |
4e2cc9d3 | 951 | if (!CYCastJavaArguments(frame, overload->shorty_, context, arguments, array)) |
c9c16dde | 952 | continue; |
fc664744 | 953 | jvalue *values(array); |
c9c16dde JF |
954 | switch (overload->primitive_) { |
955 | case CYJavaPrimitiveObject: | |
fc664744 | 956 | return CYCastJSValue(context, jni.CallObjectMethodA<jobject>(self->value_, overload->method_, values)); |
c9c16dde | 957 | case CYJavaPrimitiveVoid: |
fc664744 | 958 | jni.CallVoidMethodA(self->value_, overload->method_, values); |
c9c16dde JF |
959 | return CYJSUndefined(context); |
960 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ | |
961 | case CYJavaPrimitive ## Type: \ | |
fc664744 | 962 | return CYJavaCastJSValue(context, jni.Call ## Typ ## MethodA(self->value_, overload->method_, values)); |
c9c16dde JF |
963 | CYJavaForEachPrimitive |
964 | #undef CYJavaForEachPrimitive_ | |
965 | default: _assert(false); | |
966 | } | |
967 | } | |
968 | ||
969 | CYThrow("invalid method call"); | |
970 | } CYCatch(NULL) } | |
971 | ||
972 | static JSValueRef JavaStaticMethod_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { | |
0b5f88f6 | 973 | auto internal(CYJavaStaticMethod::Get(context, object)); |
c9c16dde | 974 | CYJavaClass *table(CYGetJavaTable(context, _this)); |
fc664744 | 975 | CYJavaEnv jni(table->value_); |
dbf05bfd JF |
976 | |
977 | CYJavaSignature bound(count); | |
978 | for (auto overload(internal->overload_.lower_bound(bound)), e(internal->overload_.upper_bound(bound)); overload != e; ++overload) { | |
4e2cc9d3 | 979 | CYJavaFrame frame(jni, count + 16); |
4b645e23 | 980 | jvalue array[count]; |
4e2cc9d3 | 981 | if (!CYCastJavaArguments(frame, overload->shorty_, context, arguments, array)) |
4b645e23 | 982 | continue; |
fc664744 | 983 | jvalue *values(array); |
4b645e23 JF |
984 | switch (overload->primitive_) { |
985 | case CYJavaPrimitiveObject: | |
fc664744 | 986 | return CYCastJSValue(context, jni.CallStaticObjectMethodA<jobject>(table->value_, overload->method_, values)); |
c9c16dde | 987 | case CYJavaPrimitiveVoid: |
fc664744 | 988 | jni.CallStaticVoidMethodA(table->value_, overload->method_, values); |
c9c16dde | 989 | return CYJSUndefined(context); |
4b645e23 JF |
990 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
991 | case CYJavaPrimitive ## Type: \ | |
fc664744 | 992 | return CYJavaCastJSValue(context, jni.CallStatic ## Typ ## MethodA(table->value_, overload->method_, values)); |
4b645e23 JF |
993 | CYJavaForEachPrimitive |
994 | #undef CYJavaForEachPrimitive_ | |
995 | default: _assert(false); | |
996 | } | |
dbf05bfd JF |
997 | } |
998 | ||
999 | CYThrow("invalid method call"); | |
1000 | } CYCatch(NULL) } | |
1001 | ||
1002 | static JSObjectRef JavaClass_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { | |
0b5f88f6 | 1003 | auto table(CYJavaClass::Get(context, object)); |
fc664744 | 1004 | CYJavaEnv jni(table->value_); |
c9c16dde JF |
1005 | jclass _class(table->value_); |
1006 | ||
1007 | if (table->interface_ && count == 1) { | |
fc664744 | 1008 | auto Cycript$(jni.FindClass("Cycript")); |
0e832de4 JF |
1009 | auto Cycript$Make(jni.GetStaticMethodID(Cycript$, "proxy", "(Ljava/lang/Class;LCycript$Wrapper;)Ljava/lang/Object;")); |
1010 | return CYCastJSObject(context, jni.CallObjectMethod<jobject>(Cycript$, Cycript$Make, _class, CYCastJavaObject(jni, context, CYCastJSObject(context, arguments[0])).get())); | |
c9c16dde | 1011 | } |
dbf05bfd | 1012 | |
dbf05bfd JF |
1013 | CYJavaSignature bound(count); |
1014 | for (auto overload(table->overload_.lower_bound(bound)), e(table->overload_.upper_bound(bound)); overload != e; ++overload) { | |
4e2cc9d3 | 1015 | CYJavaFrame frame(jni, count + 16); |
4b645e23 | 1016 | jvalue array[count]; |
4e2cc9d3 | 1017 | if (!CYCastJavaArguments(frame, overload->shorty_, context, arguments, array)) |
4b645e23 | 1018 | continue; |
fc664744 JF |
1019 | jvalue *values(array); |
1020 | auto object(jni.NewObjectA(_class, overload->method_, values)); | |
1021 | return CYCastJSObject(context, object); | |
dbf05bfd JF |
1022 | } |
1023 | ||
1024 | CYThrow("invalid constructor call"); | |
1025 | } CYCatch(NULL) } | |
1026 | ||
c9c16dde | 1027 | static bool JavaStaticInterior_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) { |
0b5f88f6 | 1028 | auto internal(CYJavaStaticInterior::Get(context, object)); |
4b645e23 | 1029 | CYJavaClass *table(internal->table_); |
dbf05bfd JF |
1030 | CYPool pool; |
1031 | auto name(CYPoolUTF8String(pool, context, property)); | |
1032 | auto field(table->static_.find(name)); | |
1033 | if (field == table->static_.end()) | |
1034 | return false; | |
1035 | return true; | |
1036 | } | |
1037 | ||
c9c16dde | 1038 | static JSValueRef JavaStaticInterior_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { |
0b5f88f6 | 1039 | auto internal(CYJavaStaticInterior::Get(context, object)); |
4b645e23 | 1040 | CYJavaClass *table(internal->table_); |
fc664744 | 1041 | CYJavaEnv jni(table->value_); |
dbf05bfd JF |
1042 | CYPool pool; |
1043 | auto name(CYPoolUTF8String(pool, context, property)); | |
1044 | auto field(table->static_.find(name)); | |
1045 | if (field == table->static_.end()) | |
1046 | return NULL; | |
1047 | ||
1048 | switch (field->second.primitive_) { | |
1049 | case CYJavaPrimitiveObject: | |
fc664744 | 1050 | return CYCastJSValue(context, jni.GetStaticObjectField<jobject>(table->value_, field->second.field_)); |
4b645e23 | 1051 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
dbf05bfd | 1052 | case CYJavaPrimitive ## Type: \ |
fc664744 | 1053 | return CYJavaCastJSValue(context, jni.GetStatic ## Typ ## Field(table->value_, field->second.field_)); |
dbf05bfd JF |
1054 | CYJavaForEachPrimitive |
1055 | #undef CYJavaForEachPrimitive_ | |
1056 | default: _assert(false); | |
1057 | } | |
1058 | } CYCatch(NULL) } | |
1059 | ||
c9c16dde | 1060 | static bool JavaStaticInterior_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry { |
0b5f88f6 | 1061 | auto internal(CYJavaStaticInterior::Get(context, object)); |
4b645e23 | 1062 | CYJavaClass *table(internal->table_); |
fc664744 | 1063 | CYJavaEnv jni(table->value_); |
dbf05bfd JF |
1064 | CYPool pool; |
1065 | auto name(CYPoolUTF8String(pool, context, property)); | |
1066 | auto field(table->static_.find(name)); | |
1067 | if (field == table->static_.end()) | |
1068 | return false; | |
1069 | ||
1070 | switch (field->second.primitive_) { | |
1071 | case CYJavaPrimitiveObject: | |
fc664744 | 1072 | jni.SetStaticObjectField(table->value_, field->second.field_, CYCastJavaObject(jni, context, value)); |
4b645e23 | 1073 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
dbf05bfd | 1074 | case CYJavaPrimitive ## Type: \ |
fc664744 | 1075 | jni.SetStatic ## Typ ## Field(table->value_, field->second.field_, CYCastDouble(context, value)); \ |
dbf05bfd JF |
1076 | break; |
1077 | CYJavaForEachPrimitive | |
1078 | #undef CYJavaForEachPrimitive_ | |
1079 | default: _assert(false); | |
1080 | } | |
1081 | ||
1082 | return true; | |
1083 | } CYCatch(false) } | |
1084 | ||
c9c16dde | 1085 | static void JavaStaticInterior_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) { |
0b5f88f6 | 1086 | auto internal(CYJavaStaticInterior::Get(context, object)); |
4b645e23 | 1087 | CYJavaClass *table(internal->table_); |
dbf05bfd JF |
1088 | for (const auto &field : table->static_) |
1089 | JSPropertyNameAccumulatorAddName(names, CYJSString(field.first)); | |
1090 | } | |
1091 | ||
1092 | static JSValueRef JavaClass_getProperty_class(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { | |
0b5f88f6 | 1093 | auto table(CYJavaClass::Get(context, object)); |
fc664744 | 1094 | return CYCastJSValue(context, table->value_); |
dbf05bfd JF |
1095 | } CYCatch(NULL) } |
1096 | ||
8effd381 | 1097 | static bool JavaInterior_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) { |
0b5f88f6 | 1098 | auto internal(CYJavaInterior::Get(context, object)); |
8effd381 | 1099 | CYJavaClass *table(internal->table_); |
dbf05bfd JF |
1100 | CYPool pool; |
1101 | auto name(CYPoolUTF8String(pool, context, property)); | |
1102 | auto field(table->instance_.find(name)); | |
1103 | if (field == table->instance_.end()) | |
1104 | return false; | |
1105 | return true; | |
1106 | } | |
1107 | ||
8effd381 | 1108 | static JSValueRef JavaInterior_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { |
0b5f88f6 | 1109 | auto internal(CYJavaInterior::Get(context, object)); |
fc664744 | 1110 | CYJavaEnv jni(internal->value_); |
8effd381 | 1111 | CYJavaClass *table(internal->table_); |
dbf05bfd JF |
1112 | CYPool pool; |
1113 | auto name(CYPoolUTF8String(pool, context, property)); | |
1114 | auto field(table->instance_.find(name)); | |
1115 | if (field == table->instance_.end()) | |
1116 | return NULL; | |
1117 | ||
1118 | switch (field->second.primitive_) { | |
1119 | case CYJavaPrimitiveObject: | |
fc664744 | 1120 | return CYCastJSValue(context, jni.GetObjectField<jobject>(internal->value_, field->second.field_)); |
4b645e23 | 1121 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
dbf05bfd | 1122 | case CYJavaPrimitive ## Type: \ |
fc664744 | 1123 | return CYJavaCastJSValue(context, jni.Get ## Typ ## Field(internal->value_, field->second.field_)); |
dbf05bfd JF |
1124 | CYJavaForEachPrimitive |
1125 | #undef CYJavaForEachPrimitive_ | |
1126 | default: _assert(false); | |
1127 | } | |
1128 | } CYCatch(NULL) } | |
1129 | ||
8effd381 | 1130 | static bool JavaInterior_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry { |
0b5f88f6 | 1131 | auto internal(CYJavaInterior::Get(context, object)); |
fc664744 | 1132 | CYJavaEnv jni(internal->value_); |
8effd381 | 1133 | CYJavaClass *table(internal->table_); |
dbf05bfd JF |
1134 | CYPool pool; |
1135 | auto name(CYPoolUTF8String(pool, context, property)); | |
1136 | auto field(table->instance_.find(name)); | |
1137 | if (field == table->instance_.end()) | |
1138 | return false; | |
1139 | ||
1140 | switch (field->second.primitive_) { | |
1141 | case CYJavaPrimitiveObject: | |
fc664744 | 1142 | jni.SetObjectField(table->value_, field->second.field_, CYCastJavaObject(jni, context, value)); |
4b645e23 | 1143 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
dbf05bfd | 1144 | case CYJavaPrimitive ## Type: \ |
fc664744 | 1145 | jni.Set ## Typ ## Field(table->value_, field->second.field_, CYCastDouble(context, value)); \ |
dbf05bfd JF |
1146 | break; |
1147 | CYJavaForEachPrimitive | |
1148 | #undef CYJavaForEachPrimitive_ | |
1149 | default: _assert(false); | |
1150 | } | |
1151 | ||
1152 | return true; | |
1153 | } CYCatch(false) } | |
1154 | ||
8effd381 | 1155 | static void JavaInterior_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) { |
0b5f88f6 | 1156 | auto internal(CYJavaInterior::Get(context, object)); |
8effd381 | 1157 | CYJavaClass *table(internal->table_); |
dbf05bfd JF |
1158 | for (const auto &field : table->instance_) |
1159 | JSPropertyNameAccumulatorAddName(names, CYJSString(field.first)); | |
1160 | } | |
1161 | ||
1162 | static JSValueRef JavaObject_getProperty_constructor(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { | |
0b5f88f6 | 1163 | auto internal(CYJavaObject::Get(context, object)); |
fc664744 JF |
1164 | CYJavaEnv jni(internal->value_); |
1165 | return CYGetJavaClass(context, jni.GetObjectClass(internal->value_)); | |
dbf05bfd JF |
1166 | } CYCatch(NULL) } |
1167 | ||
4b645e23 | 1168 | static JSValueRef JavaClass_getProperty_$cyi(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { |
0b5f88f6 | 1169 | auto internal(CYJavaClass::Get(context, object)); |
fc664744 | 1170 | return CYJavaStaticInterior::Make(context, internal->value_, internal); |
4b645e23 JF |
1171 | } CYCatch(NULL) } |
1172 | ||
8effd381 | 1173 | static JSValueRef JavaObject_getProperty_$cyi(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { |
0b5f88f6 | 1174 | auto internal(CYJavaObject::Get(context, object)); |
fc664744 | 1175 | return CYJavaInterior::Make(context, internal->value_, internal->table_); |
8effd381 JF |
1176 | } CYCatch(NULL) } |
1177 | ||
4b645e23 | 1178 | static JSValueRef JavaClass_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { |
0b5f88f6 | 1179 | auto internal(CYJavaClass::Get(context, _this)); |
fc664744 JF |
1180 | CYJavaEnv jni(internal->value_); |
1181 | auto Class$(jni.FindClass("java/lang/Class")); | |
1182 | auto Class$getCanonicalName(jni.GetMethodID(Class$, "getCanonicalName", "()Ljava/lang/String;")); | |
1183 | return CYCastJSValue(context, CYJSString(jni.CallObjectMethod<jstring>(internal->value_, Class$getCanonicalName))); | |
4b645e23 JF |
1184 | } CYCatch(NULL) } |
1185 | ||
1186 | static JSValueRef JavaMethod_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { | |
1187 | std::ostringstream cyon; | |
1188 | return CYCastJSValue(context, CYJSString(cyon.str())); | |
1189 | } CYCatch(NULL) } | |
1190 | ||
c9c16dde JF |
1191 | static JSValueRef JavaStaticMethod_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { |
1192 | std::ostringstream cyon; | |
1193 | return CYCastJSValue(context, CYJSString(cyon.str())); | |
1194 | } CYCatch(NULL) } | |
1195 | ||
4b645e23 | 1196 | static JSValueRef JavaArray_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { |
0b5f88f6 | 1197 | auto internal(CYJavaArray::Get(context, object)); |
fc664744 | 1198 | CYJavaEnv jni(internal->value_); |
4b645e23 | 1199 | if (JSStringIsEqual(property, length_s)) |
fc664744 | 1200 | return CYCastJSValue(context, jni.GetArrayLength(internal->value_)); |
4b645e23 JF |
1201 | |
1202 | CYPool pool; | |
1203 | ssize_t offset; | |
1204 | if (!CYGetOffset(pool, context, property, offset)) | |
1205 | return NULL; | |
1206 | ||
1207 | if (internal->primitive_ == CYJavaPrimitiveObject) | |
fc664744 | 1208 | return CYCastJSValue(context, jni.GetObjectArrayElement<jobject>(static_cast<jobjectArray>(internal->value_.value_), offset)); |
4b645e23 JF |
1209 | else switch (internal->primitive_) { |
1210 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ | |
1211 | case CYJavaPrimitive ## Type: { \ | |
1212 | j ## type element; \ | |
fc664744 | 1213 | jni.Get ## Typ ## ArrayRegion(static_cast<j ## type ## Array>(internal->value_.value_), offset, 1, &element); \ |
4b645e23 JF |
1214 | return CYJavaCastJSValue(context, element); \ |
1215 | } break; | |
1216 | CYJavaForEachPrimitive | |
1217 | #undef CYJavaForEachPrimitive_ | |
1218 | default: _assert(false); | |
1219 | } | |
1220 | } CYCatch(NULL) } | |
1221 | ||
1222 | static bool JavaArray_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry { | |
0b5f88f6 | 1223 | auto internal(CYJavaArray::Get(context, object)); |
fc664744 | 1224 | CYJavaEnv jni(internal->value_); |
4b645e23 JF |
1225 | |
1226 | CYPool pool; | |
1227 | ssize_t offset; | |
1228 | if (!CYGetOffset(pool, context, property, offset)) | |
1229 | return false; | |
1230 | ||
1231 | if (internal->primitive_ == CYJavaPrimitiveObject) | |
fc664744 | 1232 | jni.SetObjectArrayElement(static_cast<jobjectArray>(internal->value_.value_), offset, CYCastJavaObject(jni, context, value)); |
4b645e23 JF |
1233 | else switch (internal->primitive_) { |
1234 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ | |
1235 | case CYJavaPrimitive ## Type: { \ | |
1236 | j ## type element; \ | |
fc664744 | 1237 | jni.Get ## Typ ## ArrayRegion(static_cast<j ## type ## Array>(internal->value_.value_), offset, 1, &element); \ |
4b645e23 JF |
1238 | return CYJavaCastJSValue(context, element); \ |
1239 | } break; | |
1240 | CYJavaForEachPrimitive | |
1241 | #undef CYJavaForEachPrimitive_ | |
1242 | default: _assert(false); | |
1243 | } | |
1244 | ||
1245 | return true; | |
1246 | } CYCatch(false) } | |
1247 | ||
1248 | static JSValueRef JavaPackage_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { | |
0b5f88f6 | 1249 | auto internal(CYJavaPackage::Get(context, _this)); |
4b645e23 JF |
1250 | std::ostringstream name; |
1251 | for (auto &package : internal->package_) | |
1252 | name << package << '.'; | |
1253 | name << '*'; | |
1254 | return CYCastJSValue(context, CYJSString(name.str())); | |
1255 | } CYCatch(NULL) } | |
1256 | ||
dbf05bfd JF |
1257 | static bool CYJavaPackage_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) { |
1258 | return true; | |
1259 | } | |
1260 | ||
1261 | static JSValueRef CYJavaPackage_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { | |
0b5f88f6 | 1262 | auto internal(CYJavaPackage::Get(context, object)); |
dbf05bfd JF |
1263 | CYJavaPackage::Path package(internal->package_); |
1264 | ||
1265 | CYPool pool; | |
1266 | const char *next(CYPoolCString(pool, context, property)); | |
1267 | ||
1268 | std::ostringstream name; | |
1269 | for (auto &package : internal->package_) | |
1270 | name << package << '/'; | |
1271 | name << next; | |
1272 | ||
c9c16dde | 1273 | JNIEnv *jni(GetJNI(context)); |
fc664744 JF |
1274 | if (auto _class = jni->FindClass(name.str().c_str())) |
1275 | return CYGetJavaClass(context, CYJavaLocal<jclass>(jni, _class)); | |
dbf05bfd JF |
1276 | jni->ExceptionClear(); |
1277 | ||
1278 | package.push_back(next); | |
1279 | return CYJavaPackage::Make(context, package); | |
1280 | } CYCatch(NULL) } | |
1281 | ||
fc664744 | 1282 | static void Cycript_delete(JNIEnv *env, jclass api, jlong jprotect) { CYJavaTry { |
824bc1ec | 1283 | delete &protect; |
c9c16dde JF |
1284 | } CYJavaCatch() } |
1285 | ||
fc664744 JF |
1286 | static jobject Cycript_handle(JNIEnv *env, jclass api, jlong jprotect, jstring property, jobjectArray jarguments) { CYJavaTry { |
1287 | JSValueRef function(CYGetProperty(context, object, CYJSString(CYJavaRef<jstring>(jni, property)))); | |
c9c16dde JF |
1288 | if (JSValueIsUndefined(context, function)) |
1289 | return NULL; | |
1290 | ||
fc664744 | 1291 | size_t count(jarguments == NULL ? 0 : jni.GetArrayLength(jarguments)); |
c9c16dde | 1292 | JSValueRef arguments[count]; |
4e2cc9d3 | 1293 | for (size_t index(0); index != count; ++index) |
fc664744 | 1294 | arguments[index] = CYCastJSValue(context, jni.GetObjectArrayElement<jobject>(jarguments, index)); |
c9c16dde | 1295 | |
4e2cc9d3 | 1296 | return CYCastJavaObject(jni, context, CYCallAsFunction(context, CYCastJSObject(context, function), object, count, arguments)).leak(); |
c9c16dde JF |
1297 | } CYJavaCatch(NULL) } |
1298 | ||
1299 | static JNINativeMethod Cycript_[] = { | |
1300 | {(char *) "delete", (char *) "(J)V", (void *) &Cycript_delete}, | |
1301 | {(char *) "handle", (char *) "(JLjava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;", (void *) &Cycript_handle}, | |
1302 | }; | |
1303 | ||
f2c357f9 JF |
1304 | template <typename Type_> |
1305 | static _finline void dlset(Type_ &function, const char *name, void *handle) { | |
1306 | function = reinterpret_cast<Type_>(dlsym(handle, name)); | |
1307 | } | |
1308 | ||
1309 | jint CYJavaVersion(JNI_VERSION_1_4); | |
1310 | ||
1311 | static JNIEnv *CYGetCreatedJava(jint (*$JNI_GetCreatedJavaVMs)(JavaVM **, jsize, jsize *)) { | |
1312 | jsize capacity(16); | |
1313 | JavaVM *jvms[capacity]; | |
1314 | jsize size; | |
1315 | _jnicall($JNI_GetCreatedJavaVMs(jvms, capacity, &size)); | |
1316 | if (size == 0) | |
1317 | return NULL; | |
1318 | JavaVM *jvm(jvms[0]); | |
1319 | JNIEnv *jni; | |
1320 | _jnicall(jvm->GetEnv(reinterpret_cast<void **>(&jni), CYJavaVersion)); | |
1321 | return jni; | |
1322 | } | |
1323 | ||
1324 | static JNIEnv *GetJNI_(JSContextRef context) { | |
fc664744 JF |
1325 | static JavaVM *jvm(NULL); |
1326 | static JNIEnv *jni(NULL); | |
1327 | ||
1328 | if (jni != NULL) | |
1329 | return jni; | |
fc664744 | 1330 | |
f2c357f9 JF |
1331 | CYPool pool; |
1332 | void *handle(RTLD_DEFAULT); | |
1333 | std::string library; | |
fc664744 | 1334 | |
f2c357f9 JF |
1335 | jint (*$JNI_GetCreatedJavaVMs)(JavaVM **jvms, jsize capacity, jsize *size); |
1336 | dlset($JNI_GetCreatedJavaVMs, "JNI_GetCreatedJavaVMs", handle); | |
1337 | ||
1338 | if ($JNI_GetCreatedJavaVMs != NULL) { | |
1339 | if (JNIEnv *jni = CYGetCreatedJava($JNI_GetCreatedJavaVMs)) | |
1340 | return jni; | |
fc664744 | 1341 | } else { |
f2c357f9 JF |
1342 | std::vector<const char *> guesses; |
1343 | ||
1344 | #ifdef __ANDROID__ | |
1345 | char android[PROP_VALUE_MAX]; | |
1346 | if (__system_property_get("persist.sys.dalvik.vm.lib", android) != 0) | |
1347 | guesses.push_back(android); | |
1348 | #endif | |
1349 | ||
1350 | guesses.push_back("libart.so"); | |
1351 | guesses.push_back("libdvm.so"); | |
1352 | guesses.push_back("libjvm.so"); | |
1353 | ||
1354 | for (const char *guess : guesses) { | |
1355 | handle = dlopen(guess, RTLD_LAZY | RTLD_GLOBAL); | |
1356 | if (handle != NULL) { | |
1357 | library = guess; | |
1358 | break; | |
1359 | } | |
fc664744 JF |
1360 | } |
1361 | ||
f2c357f9 JF |
1362 | _assert(library.size() != 0); |
1363 | ||
1364 | dlset($JNI_GetCreatedJavaVMs, "JNI_GetCreatedJavaVMs", handle); | |
1365 | if (JNIEnv *jni = CYGetCreatedJava($JNI_GetCreatedJavaVMs)) | |
1366 | return jni; | |
fc664744 JF |
1367 | } |
1368 | ||
f2c357f9 JF |
1369 | std::vector<JavaVMOption> options; |
1370 | ||
1371 | { | |
1372 | std::ostringstream option; | |
1373 | option << "-Djava.class.path="; | |
1374 | option << CYPoolLibraryPath(pool) << "/libcycript.jar"; | |
1375 | if (const char *classpath = getenv("CLASSPATH")) | |
1376 | option << ':' << classpath; | |
1377 | options.push_back(JavaVMOption{pool.strdup(option.str().c_str()), NULL}); | |
1378 | } | |
1379 | ||
1380 | // To use libnativehelper to access JNI_GetCreatedJavaVMs, you need JniInvocation. | |
1381 | // ...but there can only be one JniInvocation, and assuradely the other VM has it. | |
1382 | // Essentially, this API makes no sense. We need it for AndroidRuntime, though :/. | |
1383 | ||
1384 | if (void *libnativehelper = dlopen("libnativehelper.so", RTLD_LAZY | RTLD_GLOBAL)) { | |
1385 | class JniInvocation$; | |
1386 | JniInvocation$ *(*JniInvocation$$init$)(JniInvocation$ *self)(NULL); | |
1387 | bool (*JniInvocation$Init)(JniInvocation$ *self, const char *library)(NULL); | |
1388 | JniInvocation$ *(*JniInvocation$finalize)(JniInvocation$ *self)(NULL); | |
1389 | ||
1390 | dlset(JniInvocation$$init$, "_ZN13JniInvocationC1Ev", libnativehelper); | |
1391 | dlset(JniInvocation$Init, "_ZN13JniInvocation4InitEPKc", libnativehelper); | |
1392 | dlset(JniInvocation$finalize, "_ZN13JniInvocationD1Ev", libnativehelper); | |
1393 | ||
95be1645 JF |
1394 | if (JniInvocation$$init$ == NULL) |
1395 | dlclose(libnativehelper); | |
1396 | else { | |
1397 | // XXX: we should attach a pool to the VM itself and deallocate this there | |
1398 | //auto invocation(pool.calloc<JniInvocation$>(1, 1024)); | |
1399 | //_assert(JniInvocation$finalize != NULL); | |
1400 | //pool.atexit(reinterpret_cast<void (*)(void *)>(JniInvocation$finalize), invocation); | |
1401 | ||
1402 | auto invocation(static_cast<JniInvocation$ *>(calloc(1, 1024))); | |
1403 | JniInvocation$$init$(invocation); | |
1404 | ||
1405 | _assert(JniInvocation$Init != NULL); | |
1406 | JniInvocation$Init(invocation, NULL); | |
1407 | ||
1408 | dlset($JNI_GetCreatedJavaVMs, "JNI_GetCreatedJavaVMs", libnativehelper); | |
1409 | if (JNIEnv *jni = CYGetCreatedJava($JNI_GetCreatedJavaVMs)) | |
1410 | return jni; | |
1411 | } | |
f2c357f9 JF |
1412 | } |
1413 | ||
1414 | if (void *libandroid_runtime = dlopen("libandroid_runtime.so", RTLD_LAZY | RTLD_GLOBAL)) { | |
1415 | class AndroidRuntime$; | |
1416 | AndroidRuntime$ *(*AndroidRuntime$$init$)(AndroidRuntime$ *self, char *args, unsigned int size)(NULL); | |
1417 | int (*AndroidRuntime$startVm)(AndroidRuntime$ *self, JavaVM **jvm, JNIEnv **jni)(NULL); | |
1418 | int (*AndroidRuntime$startReg)(JNIEnv *jni)(NULL); | |
1419 | int (*AndroidRuntime$addOption)(AndroidRuntime$ *self, const char *option, void *extra)(NULL); | |
1420 | int (*AndroidRuntime$addVmArguments)(AndroidRuntime$ *self, int, const char *const argv[])(NULL); | |
1421 | AndroidRuntime$ *(*AndroidRuntime$finalize)(AndroidRuntime$ *self)(NULL); | |
1422 | ||
1423 | dlset(AndroidRuntime$$init$, "_ZN7android14AndroidRuntimeC1EPcj", libandroid_runtime); | |
1424 | dlset(AndroidRuntime$startVm, "_ZN7android14AndroidRuntime7startVmEPP7_JavaVMPP7_JNIEnv", libandroid_runtime); | |
1425 | dlset(AndroidRuntime$startReg, "_ZN7android14AndroidRuntime8startRegEP7_JNIEnv", libandroid_runtime); | |
1426 | dlset(AndroidRuntime$addOption, "_ZN7android14AndroidRuntime9addOptionEPKcPv", libandroid_runtime); | |
1427 | dlset(AndroidRuntime$addVmArguments, "_ZN7android14AndroidRuntime14addVmArgumentsEiPKPKc", libandroid_runtime); | |
1428 | dlset(AndroidRuntime$finalize, "_ZN7android14AndroidRuntimeD1Ev", libandroid_runtime); | |
1429 | ||
1430 | // XXX: it would also be interesting to attach this to a global pool | |
1431 | AndroidRuntime$ *runtime(pool.calloc<AndroidRuntime$>(1, 1024)); | |
1432 | ||
1433 | _assert(AndroidRuntime$$init$ != NULL); | |
1434 | AndroidRuntime$$init$(runtime, NULL, 0); | |
1435 | ||
1436 | if (AndroidRuntime$addOption == NULL) { | |
1437 | _assert(AndroidRuntime$addVmArguments != NULL); | |
1438 | std::vector<const char *> arguments; | |
1439 | for (const auto &option : options) | |
1440 | arguments.push_back(option.optionString); | |
1441 | AndroidRuntime$addVmArguments(runtime, arguments.size(), arguments.data()); | |
1442 | } else for (const auto &option : options) | |
1443 | AndroidRuntime$addOption(runtime, option.optionString, option.extraInfo); | |
1444 | ||
1445 | int failure; | |
1446 | ||
1447 | _assert(AndroidRuntime$startVm != NULL); | |
1448 | failure = AndroidRuntime$startVm(runtime, &jvm, &jni); | |
1449 | _assert(failure == 0); | |
1450 | ||
1451 | _assert(AndroidRuntime$startReg != NULL); | |
1452 | failure = AndroidRuntime$startReg(jni); | |
1453 | _assert(failure == 0); | |
1454 | ||
1455 | return jni; | |
1456 | } | |
1457 | ||
1458 | jint (*$JNI_CreateJavaVM)(JavaVM **jvm, void **, void *); | |
1459 | dlset($JNI_CreateJavaVM, "JNI_CreateJavaVM", handle); | |
1460 | ||
1461 | JavaVMInitArgs args; | |
1462 | memset(&args, 0, sizeof(args)); | |
1463 | args.version = CYJavaVersion; | |
1464 | args.nOptions = options.size(); | |
1465 | args.options = options.data(); | |
1466 | _jnicall($JNI_CreateJavaVM(&jvm, reinterpret_cast<void **>(&jni), &args)); | |
1467 | return jni; | |
1468 | } | |
fc664744 | 1469 | |
f2c357f9 JF |
1470 | static JNIEnv *GetJNI(JSContextRef context) { |
1471 | CYJavaEnv jni(GetJNI_(context)); | |
1472 | auto Cycript$(jni.FindClass("Cycript")); | |
1473 | jni.RegisterNatives(Cycript$, Cycript_, sizeof(Cycript_) / sizeof(Cycript_[0])); | |
fc664744 | 1474 | return jni; |
c9c16dde JF |
1475 | } |
1476 | ||
4b645e23 | 1477 | static JSStaticValue JavaClass_staticValues[3] = { |
dbf05bfd | 1478 | {"class", &JavaClass_getProperty_class, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, |
4b645e23 | 1479 | {"$cyi", &JavaClass_getProperty_$cyi, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, |
dbf05bfd JF |
1480 | {NULL, NULL, NULL, 0} |
1481 | }; | |
1482 | ||
4b645e23 JF |
1483 | static JSStaticFunction JavaClass_staticFunctions[2] = { |
1484 | {"toCYON", &JavaClass_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, | |
1485 | {NULL, NULL, 0} | |
1486 | }; | |
1487 | ||
8effd381 | 1488 | static JSStaticValue JavaObject_staticValues[3] = { |
dbf05bfd | 1489 | {"constructor", &JavaObject_getProperty_constructor, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, |
8effd381 | 1490 | {"$cyi", &JavaObject_getProperty_$cyi, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, |
dbf05bfd JF |
1491 | {NULL, NULL, NULL, 0} |
1492 | }; | |
1493 | ||
4b645e23 JF |
1494 | static JSStaticFunction JavaMethod_staticFunctions[2] = { |
1495 | {"toCYON", &JavaMethod_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, | |
1496 | {NULL, NULL, 0} | |
1497 | }; | |
1498 | ||
c9c16dde JF |
1499 | static JSStaticFunction JavaStaticMethod_staticFunctions[2] = { |
1500 | {"toCYON", &JavaStaticMethod_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, | |
1501 | {NULL, NULL, 0} | |
1502 | }; | |
1503 | ||
4b645e23 JF |
1504 | static JSStaticFunction JavaPackage_staticFunctions[2] = { |
1505 | {"toCYON", &JavaPackage_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, | |
dbf05bfd JF |
1506 | {NULL, NULL, 0} |
1507 | }; | |
1508 | ||
1509 | void CYJava_Initialize() { | |
1510 | Primitives_.insert(std::make_pair("void", CYJavaPrimitiveVoid)); | |
4b645e23 | 1511 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
42619b59 JF |
1512 | Primitives_.insert(std::make_pair(#type, CYJavaPrimitive ## Type)); |
1513 | CYJavaForEachPrimitive | |
1514 | #undef CYJavaForEachPrimitive_ | |
dbf05bfd JF |
1515 | |
1516 | JSClassDefinition definition; | |
1517 | ||
1518 | definition = kJSClassDefinitionEmpty; | |
1519 | definition.className = "JavaClass"; | |
1520 | definition.staticValues = JavaClass_staticValues; | |
4b645e23 | 1521 | definition.staticFunctions = JavaClass_staticFunctions; |
dbf05bfd JF |
1522 | definition.callAsConstructor = &JavaClass_callAsConstructor; |
1523 | definition.finalize = &CYFinalize; | |
1524 | CYJavaClass::Class_ = JSClassCreate(&definition); | |
1525 | ||
8effd381 JF |
1526 | definition = kJSClassDefinitionEmpty; |
1527 | definition.attributes = kJSClassAttributeNoAutomaticPrototype; | |
1528 | definition.className = "JavaInterior"; | |
1529 | definition.hasProperty = &JavaInterior_hasProperty; | |
1530 | definition.getProperty = &JavaInterior_getProperty; | |
1531 | definition.setProperty = &JavaInterior_setProperty; | |
1532 | definition.getPropertyNames = &JavaInterior_getPropertyNames; | |
1533 | definition.finalize = &CYFinalize; | |
1534 | CYJavaInterior::Class_ = JSClassCreate(&definition); | |
1535 | ||
dbf05bfd JF |
1536 | definition = kJSClassDefinitionEmpty; |
1537 | definition.className = "JavaMethod"; | |
4b645e23 | 1538 | definition.staticFunctions = JavaMethod_staticFunctions; |
dbf05bfd JF |
1539 | definition.callAsFunction = &JavaMethod_callAsFunction; |
1540 | definition.finalize = &CYFinalize; | |
1541 | CYJavaMethod::Class_ = JSClassCreate(&definition); | |
1542 | ||
c9c16dde JF |
1543 | definition = kJSClassDefinitionEmpty; |
1544 | definition.className = "JavaStaticMethod"; | |
1545 | definition.staticFunctions = JavaStaticMethod_staticFunctions; | |
1546 | definition.callAsFunction = &JavaStaticMethod_callAsFunction; | |
1547 | definition.finalize = &CYFinalize; | |
1548 | CYJavaStaticMethod::Class_ = JSClassCreate(&definition); | |
1549 | ||
dbf05bfd JF |
1550 | definition = kJSClassDefinitionEmpty; |
1551 | definition.attributes = kJSClassAttributeNoAutomaticPrototype; | |
1552 | definition.className = "JavaObject"; | |
1553 | definition.staticValues = JavaObject_staticValues; | |
dbf05bfd JF |
1554 | definition.finalize = &CYFinalize; |
1555 | CYJavaObject::Class_ = JSClassCreate(&definition); | |
1556 | ||
4b645e23 JF |
1557 | definition = kJSClassDefinitionEmpty; |
1558 | definition.className = "JavaArray"; | |
1559 | definition.getProperty = &JavaArray_getProperty; | |
1560 | definition.setProperty = &JavaArray_setProperty; | |
1561 | definition.finalize = &CYFinalize; | |
1562 | CYJavaArray::Class_ = JSClassCreate(&definition); | |
1563 | ||
dbf05bfd JF |
1564 | definition = kJSClassDefinitionEmpty; |
1565 | definition.className = "JavaPackage"; | |
1566 | definition.staticFunctions = JavaPackage_staticFunctions; | |
1567 | definition.hasProperty = &CYJavaPackage_hasProperty; | |
1568 | definition.getProperty = &CYJavaPackage_getProperty; | |
1569 | definition.finalize = &CYFinalize; | |
1570 | CYJavaPackage::Class_ = JSClassCreate(&definition); | |
4b645e23 JF |
1571 | |
1572 | definition = kJSClassDefinitionEmpty; | |
1573 | definition.attributes = kJSClassAttributeNoAutomaticPrototype; | |
c9c16dde JF |
1574 | definition.className = "JavaStaticInterior"; |
1575 | definition.hasProperty = &JavaStaticInterior_hasProperty; | |
1576 | definition.getProperty = &JavaStaticInterior_getProperty; | |
1577 | definition.setProperty = &JavaStaticInterior_setProperty; | |
1578 | definition.getPropertyNames = &JavaStaticInterior_getPropertyNames; | |
4b645e23 | 1579 | definition.finalize = &CYFinalize; |
c9c16dde | 1580 | CYJavaStaticInterior::Class_ = JSClassCreate(&definition); |
dbf05bfd JF |
1581 | } |
1582 | ||
1583 | void CYJava_SetupContext(JSContextRef context) { | |
1584 | JSObjectRef global(CYGetGlobalObject(context)); | |
1585 | //JSObjectRef cy(CYCastJSObject(context, CYGetProperty(context, global, cy_s))); | |
1586 | JSObjectRef cycript(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Cycript")))); | |
1587 | JSObjectRef all(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("all")))); | |
1588 | //JSObjectRef alls(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("alls")))); | |
1589 | ||
1590 | JSObjectRef Java(JSObjectMake(context, NULL, NULL)); | |
1591 | CYSetProperty(context, cycript, CYJSString("Java"), Java); | |
1592 | ||
1593 | JSObjectRef Packages(CYJavaPackage::Make(context, CYJavaPackage::Path())); | |
1594 | CYSetProperty(context, all, CYJSString("Packages"), Packages); | |
1595 | ||
1596 | for (auto name : (const char *[]) {"java", "javax", "android", "com", "net", "org"}) { | |
1597 | CYJSString js(name); | |
1598 | CYSetProperty(context, all, js, CYGetProperty(context, Packages, js)); | |
1599 | } | |
1600 | } | |
1601 | ||
1602 | static CYHook CYJavaHook = { | |
1603 | NULL, | |
1604 | NULL, | |
1605 | NULL, | |
1606 | &CYJava_Initialize, | |
1607 | &CYJava_SetupContext, | |
1608 | NULL, | |
1609 | }; | |
1610 | ||
1611 | CYRegisterHook CYJava(&CYJavaHook); |