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