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