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