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