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