]> git.saurik.com Git - cycript.git/blame - Java/Execute.cpp
Fix compilation errors using gcc from Android NDK.
[cycript.git] / Java / Execute.cpp
CommitLineData
7341eedb
JF
1/* Cycript - The Truly Universal Scripting Language
2 * Copyright (C) 2009-2016 Jay Freeman (saurik)
e17ad658
JF
3*/
4
f95d2598 5/* GNU Affero General Public License, Version 3 {{{ */
e17ad658 6/*
f95d2598
JF
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
e17ad658 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f95d2598
JF
15 * GNU Affero General Public License for more details.
16
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
e17ad658
JF
19**/
20/* }}} */
21
dbf05bfd
JF
22#include <map>
23#include <sstream>
24#include <vector>
25
def90846
JF
26#ifdef __APPLE__
27#include <JavaVM/jni.h>
28#else
e17ad658 29#include <jni.h>
def90846 30#endif
dbf05bfd
JF
31
32#include "cycript.hpp"
c9c16dde 33#include "Error.hpp"
dbf05bfd
JF
34#include "Execute.hpp"
35#include "Internal.hpp"
36#include "JavaScript.hpp"
37#include "Pooling.hpp"
38
39#define _jnicall(expr) ({ \
40 jint _value(expr); \
41 if (_value != JNI_OK) \
42 CYThrow("_jnicall(%s) == %d", #expr, _value); \
43})
44
45#define _envcall(jni, expr) ({ \
46 __typeof__(jni->expr) _value(jni->expr); \
47 if (jthrowable _error = jni->ExceptionOccurred()) { \
48 jni->ExceptionClear(); \
c9c16dde 49 throw CYJSError(context, CYCastJSValue(context, jni, _error)); \
dbf05bfd
JF
50 } \
51_value; })
52
53#define _envcallv(jni, expr) do { \
54 jni->expr; \
55 if (jthrowable _error = jni->ExceptionOccurred()) { \
56 jni->ExceptionClear(); \
c9c16dde 57 throw CYJSError(context, CYCastJSValue(context, jni, _error)); \
dbf05bfd
JF
58 } \
59} while (false)
60
c9c16dde 61#define CYJavaTry \
824bc1ec
JF
62 auto &protect(*reinterpret_cast<CYProtect *>(jprotect)); \
63 _disused JSContextRef context(protect); \
64 _disused JSObjectRef object(protect); \
c9c16dde
JF
65 try
66#define CYJavaCatch(value) \
67 catch (const CYException &error) { \
824bc1ec 68 jni->Throw(static_cast<jthrowable>(CYCastJavaObject(jni, context, error.CastJSValue(context, "Error")))); \
c9c16dde
JF
69 return value; \
70 }
71
dbf05bfd
JF
72extern "C" {
73 // Android's jni.h seriously doesn't declare these :/
74 jint JNI_CreateJavaVM(JavaVM **, void **, void *);
75 jint JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *);
76}
77
c9c16dde
JF
78static JSValueRef CYCastJSValue(JSContextRef context, JNIEnv *jni, jobject value);
79
80static void CYRegisterNatives(JSContextRef context, JNIEnv *jni);
81
82JNIEnv *GetJNI(JSContextRef context) {
dbf05bfd
JF
83 static JavaVM *jvm(NULL);
84 static JNIEnv *jni(NULL);
85
86 if (jni != NULL)
87 return jni;
88 jint version(JNI_VERSION_1_4);
89
90 jsize capacity(16);
91 JavaVM *jvms[capacity];
92 jsize size;
93 _jnicall(JNI_GetCreatedJavaVMs(jvms, capacity, &size));
94
95 if (size != 0) {
96 jvm = jvms[0];
97 _jnicall(jvm->GetEnv(reinterpret_cast<void **>(&jni), version));
98 } else {
c9c16dde
JF
99 CYPool pool;
100 std::vector<JavaVMOption> options;
101
102 {
103 std::ostringstream option;
104 option << "-Djava.class.path=";
105 option << CYPoolLibraryPath(pool) << "/libcycript.jar";
106 if (const char *classpath = getenv("CLASSPATH"))
107 option << ':' << classpath;
108 options.push_back(JavaVMOption{pool.strdup(option.str().c_str()), NULL});
109 }
110
dbf05bfd
JF
111 JavaVMInitArgs args;
112 memset(&args, 0, sizeof(args));
113 args.version = version;
c9c16dde
JF
114 args.nOptions = options.size();
115 args.options = options.data();
dbf05bfd
JF
116 _jnicall(JNI_CreateJavaVM(&jvm, reinterpret_cast<void **>(&jni), &args));
117 }
118
c9c16dde
JF
119 CYRegisterNatives(context, jni);
120
dbf05bfd
JF
121 return jni;
122}
123
124class CYJavaUTF8String :
125 public CYUTF8String
126{
127 private:
128 JNIEnv *jni_;
129 jstring value_;
130
131 public:
132 CYJavaUTF8String(JNIEnv *jni, jstring value) :
133 jni_(jni),
134 value_(value)
135 {
4b645e23 136 _assert(value != NULL);
dbf05bfd
JF
137 size = jni_->GetStringUTFLength(value_);
138 data = jni_->GetStringUTFChars(value_, NULL);
139 }
140
141 ~CYJavaUTF8String() {
142 if (value_ != NULL)
143 jni_->ReleaseStringUTFChars(value_, data);
144 }
145
146 CYJavaUTF8String(const CYJavaUTF8String &) = delete;
147
148 CYJavaUTF8String(CYJavaUTF8String &&rhs) :
149 jni_(rhs.jni_),
150 value_(rhs.value_)
151 {
152 rhs.value_ = NULL;
153 }
154};
155
156CYJavaUTF8String CYCastUTF8String(JNIEnv *jni, jstring value) {
157 return CYJavaUTF8String(jni, value);
158}
159
160JSStringRef CYCopyJSString(JNIEnv *jni, jstring value) {
161 return CYCopyJSString(CYCastUTF8String(jni, value));
162}
163
164template <typename Value_>
165struct CYJavaGlobal {
166 JNIEnv *jni_;
167 Value_ value_;
168
169 CYJavaGlobal() :
170 jni_(NULL),
171 value_(NULL)
172 {
173 }
174
175 CYJavaGlobal(JNIEnv *jni, Value_ value) :
176 jni_(jni),
c9c16dde 177 value_(static_cast<Value_>(jni_->NewGlobalRef(value)))
dbf05bfd
JF
178 {
179 }
180
181 CYJavaGlobal(const CYJavaGlobal &value) :
182 CYJavaGlobal(value.jni_, value.value_)
183 {
184 }
185
186 CYJavaGlobal(CYJavaGlobal &&value) :
187 jni_(value.jni_),
188 value_(value.value_)
189 {
190 value.value_ = NULL;
191 }
192
193 ~CYJavaGlobal() {
194 if (value_ != NULL)
c9c16dde 195 jni_->DeleteGlobalRef(value_);
dbf05bfd
JF
196 }
197
198 operator bool() const {
199 return value_ != NULL;
200 }
201
202 operator JNIEnv *() const {
203 return jni_;
204 }
205
206 operator Value_() const {
207 return value_;
208 }
209};
210
211template <typename Internal_, typename Value_>
212struct CYJavaValue :
213 CYPrivate<Internal_>
214{
215 CYJavaGlobal<Value_> value_;
216
217 CYJavaValue(JNIEnv *jni, Value_ value) :
218 value_(jni, value)
219 {
220 }
221
222 CYJavaValue(const CYJavaValue &) = delete;
223};
224
225#define CYJavaForEachPrimitive \
4b645e23
JF
226 CYJavaForEachPrimitive_(Z, z, Boolean, Boolean, boolean) \
227 CYJavaForEachPrimitive_(B, b, Byte, Byte, byte) \
228 CYJavaForEachPrimitive_(C, c, Char, Character, char) \
229 CYJavaForEachPrimitive_(S, s, Short, Short, short) \
230 CYJavaForEachPrimitive_(I, i, Int, Integer, int) \
231 CYJavaForEachPrimitive_(J, j, Long, Long, long) \
232 CYJavaForEachPrimitive_(F, f, Float, Float, float) \
233 CYJavaForEachPrimitive_(D, d, Double, Double, double)
dbf05bfd
JF
234
235enum CYJavaPrimitive : char {
236 CYJavaPrimitiveObject,
237 CYJavaPrimitiveVoid,
4b645e23 238#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
dbf05bfd
JF
239 CYJavaPrimitive ## Type,
240CYJavaForEachPrimitive
241#undef CYJavaForEachPrimitive_
242};
243
244template <typename Type_>
245static _finline JSValueRef CYJavaCastJSValue(JSContextRef context, Type_ value) {
246 return CYCastJSValue(context, value);
247}
248
249static _finline JSValueRef CYJavaCastJSValue(JSContextRef context, jboolean value) {
250 return CYCastJSValue(context, static_cast<bool>(value));
251}
252
253static std::map<std::string, CYJavaPrimitive> Primitives_;
254
c9c16dde 255static CYJavaPrimitive CYJavaGetPrimitive(JSContextRef context, JNIEnv *jni, jobject type, jmethodID Class$get$$Name) {
4b645e23
JF
256 jstring string(static_cast<jstring>(_envcall(jni, CallObjectMethod(type, Class$get$$Name))));
257 _assert(string != NULL);
258 CYJavaUTF8String name(jni, string);
dbf05bfd
JF
259 auto primitive(Primitives_.find(name));
260 return primitive != Primitives_.end() ? primitive->second : CYJavaPrimitiveObject;
261}
262
263typedef std::vector<CYJavaPrimitive> CYJavaShorty;
264
c9c16dde 265static CYJavaShorty CYJavaGetShorty(JSContextRef context, JNIEnv *jni, jobjectArray types, jmethodID Class$get$$Name) {
dbf05bfd
JF
266 size_t count(_envcall(jni, GetArrayLength(types)));
267 CYJavaShorty shorty(count);
268 for (size_t index(0); index != count; ++index)
c9c16dde 269 shorty[index] = CYJavaGetPrimitive(context, jni, _envcall(jni, GetObjectArrayElement(types, index)), Class$get$$Name);
dbf05bfd
JF
270 return shorty;
271}
272
273struct CYJavaField {
274 jfieldID field_;
275 CYJavaPrimitive primitive_;
276};
277
278typedef std::map<std::string, CYJavaField> CYJavaFieldMap;
279
280struct CYJavaSignature {
4b645e23
JF
281 jmethodID method_;
282 CYJavaGlobal<jobject> reflected_;
283 CYJavaPrimitive primitive_;
284 CYJavaShorty shorty_;
285
286 CYJavaSignature(JNIEnv *jni, jmethodID method, jobject reflected, CYJavaPrimitive primitive, const CYJavaShorty &shorty) :
287 method_(method),
288 reflected_(jni, reflected),
289 primitive_(primitive),
290 shorty_(shorty)
dbf05bfd
JF
291 {
292 }
293
294 CYJavaSignature(unsigned count) :
4b645e23 295 shorty_(count)
dbf05bfd
JF
296 {
297 }
298
299 bool operator <(const CYJavaSignature &rhs) const {
4b645e23 300 return shorty_.size() < rhs.shorty_.size();
dbf05bfd
JF
301 }
302};
303
304typedef std::multiset<CYJavaSignature> CYJavaOverload;
305
306struct CYJavaMethod :
307 CYPrivate<CYJavaMethod>
308{
dbf05bfd
JF
309 CYJavaOverload overload_;
310
c9c16dde
JF
311 CYJavaMethod(const CYJavaOverload &overload) :
312 overload_(overload)
313 {
314 }
315};
316
317struct CYJavaStaticMethod :
318 CYPrivate<CYJavaStaticMethod>
319{
320 CYJavaOverload overload_;
321
322 CYJavaStaticMethod(const CYJavaOverload &overload) :
dbf05bfd
JF
323 overload_(overload)
324 {
325 }
326};
327
328struct CYJavaClass :
329 CYJavaValue<CYJavaClass, jclass>
330{
c9c16dde
JF
331 bool interface_;
332
dbf05bfd
JF
333 CYJavaFieldMap static_;
334 CYJavaFieldMap instance_;
335 CYJavaOverload overload_;
336
c9c16dde
JF
337 CYJavaClass(JNIEnv *jni, jclass value, bool interface) :
338 CYJavaValue(jni, value),
339 interface_(interface)
dbf05bfd
JF
340 {
341 }
342};
343
8effd381
JF
344static JSObjectRef CYGetJavaClass(JSContextRef context, JNIEnv *jni, jclass _class);
345
dbf05bfd
JF
346struct CYJavaObject :
347 CYJavaValue<CYJavaObject, jobject>
348{
8effd381
JF
349 CYJavaClass *table_;
350
4b645e23 351 CYJavaObject(JNIEnv *jni, jobject value, CYJavaClass *table) :
8effd381 352 CYJavaValue(jni, value),
4b645e23 353 table_(table)
dbf05bfd
JF
354 {
355 }
356
357 JSValueRef GetPrototype(JSContextRef context) const;
358};
359
8effd381
JF
360struct CYJavaInterior :
361 CYJavaValue<CYJavaInterior, jobject>
362{
363 CYJavaClass *table_;
364
365 CYJavaInterior(JNIEnv *jni, jobject value, CYJavaClass *table) :
366 CYJavaValue(jni, value),
367 table_(table)
368 {
369 }
370};
371
c9c16dde
JF
372struct CYJavaStaticInterior :
373 CYJavaValue<CYJavaStaticInterior, jobject>
4b645e23
JF
374{
375 CYJavaClass *table_;
376
c9c16dde 377 CYJavaStaticInterior(JNIEnv *jni, jobject value, CYJavaClass *table) :
4b645e23
JF
378 CYJavaValue(jni, value),
379 table_(table)
380 {
381 }
382};
383
384struct CYJavaArray :
385 CYJavaValue<CYJavaArray, jarray>
386{
387 CYJavaPrimitive primitive_;
388
389 CYJavaArray(JNIEnv *jni, jarray value, CYJavaPrimitive primitive) :
390 CYJavaValue(jni, value),
391 primitive_(primitive)
392 {
393 }
394
395 JSValueRef GetPrototype(JSContextRef context) const;
396};
397
dbf05bfd
JF
398struct CYJavaPackage :
399 CYPrivate<CYJavaPackage>
400{
401 typedef std::vector<std::string> Path;
402 Path package_;
403
404 _finline CYJavaPackage(const Path &package) :
405 package_(package)
406 {
407 }
408};
409
dbf05bfd
JF
410JSValueRef CYJavaObject::GetPrototype(JSContextRef context) const {
411 JNIEnv *jni(value_);
412 return CYGetProperty(context, CYGetJavaClass(context, jni, _envcall(jni, GetObjectClass(value_))), prototype_s);
413}
414
4b645e23
JF
415JSValueRef CYJavaArray::GetPrototype(JSContextRef context) const {
416 return CYGetCachedObject(context, CYJSString("Array_prototype"));
417}
418
dbf05bfd
JF
419static JSValueRef CYCastJSValue(JSContextRef context, JNIEnv *jni, jobject value) {
420 if (value == NULL)
421 return CYJSNull(context);
4b645e23
JF
422
423 jclass _class(_envcall(jni, GetObjectClass(value)));
424 if (_envcall(jni, IsSameObject(_class, _envcall(jni, FindClass("java/lang/String")))))
c9c16dde 425 return CYCastJSValue(context, CYJSString(jni, static_cast<jstring>(value)));
4b645e23
JF
426
427 jclass Class$(_envcall(jni, FindClass("java/lang/Class")));
428 jmethodID Class$isArray(_envcall(jni, GetMethodID(Class$, "isArray", "()Z")));
429 if (_envcall(jni, CallBooleanMethod(_class, Class$isArray))) {
430 jmethodID Class$getComponentType(_envcall(jni, GetMethodID(Class$, "getComponentType", "()Ljava/lang/Class;")));
431 jclass component(static_cast<jclass>(_envcall(jni, CallObjectMethod(_class, Class$getComponentType))));
432 jmethodID Class$getName(_envcall(jni, GetMethodID(Class$, "getName", "()Ljava/lang/String;")));
c9c16dde
JF
433 return CYJavaArray::Make(context, jni, static_cast<jarray>(value), CYJavaGetPrimitive(context, jni, component, Class$getName));
434 }
435
436 jclass Wrapper$(_envcall(jni, FindClass("Cycript$Wrapper")));
437 if (_envcall(jni, IsSameObject(_class, Wrapper$))) {
438 jmethodID Wrapper$getProtect(_envcall(jni, GetMethodID(Wrapper$, "getProtect", "()J")));
439 auto &protect(*reinterpret_cast<CYProtect *>(_envcall(jni, CallLongMethod(value, Wrapper$getProtect))));
440 return protect;
4b645e23
JF
441 }
442
443 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(CYGetJavaClass(context, jni, _class))));
444 return CYJavaObject::Make(context, jni, value, table);
dbf05bfd
JF
445}
446
c9c16dde
JF
447static _finline JSObjectRef CYCastJSObject(JSContextRef context, JNIEnv *jni, jobject value) {
448 return CYCastJSObject(context, CYCastJSValue(context, jni, value));
449}
450
451static jstring CYCastJavaString(JNIEnv *jni, JSContextRef context, CYUTF16String value) {
dbf05bfd
JF
452 return _envcall(jni, NewString(value.data, value.size));
453}
454
c9c16dde
JF
455static jstring CYCastJavaString(JNIEnv *jni, JSContextRef context, JSStringRef value) {
456 return CYCastJavaString(jni, context, CYCastUTF16String(value));
dbf05bfd
JF
457}
458
459#define CYCastJava$(T, Type, jtype, Cast) \
460_disused static jobject CYCastJava ## Type(JNIEnv *jni, JSContextRef context, JSValueRef value) { \
461 jclass Type$(_envcall(jni, FindClass("java/lang/" #Type))); \
462 jmethodID Type$init$(_envcall(jni, GetMethodID(Type$, "<init>", "(" #T ")V"))); \
463 return _envcall(jni, NewObject(Type$, Type$init$, static_cast<jtype>(Cast(context, value)))); \
464}
465
466CYCastJava$(Z, Boolean, jboolean, CYCastBool)
467CYCastJava$(B, Byte, jbyte, CYCastDouble)
468CYCastJava$(C, Character, jchar, CYCastDouble)
469CYCastJava$(S, Short, jshort, CYCastDouble)
470CYCastJava$(I, Integer, jint, CYCastDouble)
471CYCastJava$(J, Long, jlong, CYCastDouble)
472CYCastJava$(F, Float, jfloat, CYCastDouble)
473CYCastJava$(D, Double, jdouble, CYCastDouble)
474
c9c16dde
JF
475static CYJavaClass *CYGetJavaTable(JSContextRef context, JSObjectRef object) {
476 if (!JSValueIsObjectOfClass(context, object, CYJavaClass::Class_))
477 return NULL;
478 return reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object));
479}
480
481static CYJavaObject *CYGetJavaObject(JSContextRef context, JSObjectRef object) {
482 if (!JSValueIsObjectOfClass(context, object, CYJavaObject::Class_))
483 return NULL;
484 return reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object));
485}
486
dbf05bfd 487static jobject CYCastJavaObject(JNIEnv *jni, JSContextRef context, JSObjectRef value) {
c9c16dde 488 if (CYJavaObject *internal = CYGetJavaObject(context, value))
dbf05bfd 489 return internal->value_;
dbf05bfd 490
c9c16dde
JF
491 jclass Wrapper$(_envcall(jni, FindClass("Cycript$Wrapper")));
492 jmethodID Wrapper$$init$(_envcall(jni, GetMethodID(Wrapper$, "<init>", "(J)V")));
493 CYProtect *protect(new CYProtect(context, value));
494 return _envcall(jni, NewObject(Wrapper$, Wrapper$$init$, reinterpret_cast<jlong>(protect)));
dbf05bfd
JF
495}
496
497static jobject CYCastJavaObject(JNIEnv *jni, JSContextRef context, JSValueRef value) {
498 switch (JSValueGetType(context, value)) {
499 case kJSTypeNull:
500 return NULL;
501 case kJSTypeBoolean:
502 return CYCastJavaBoolean(jni, context, value);
503 case kJSTypeNumber:
504 return CYCastJavaDouble(jni, context, value);
505 case kJSTypeString:
c9c16dde 506 return CYCastJavaString(jni, context, CYJSString(context, value));
dbf05bfd
JF
507 case kJSTypeObject:
508 return CYCastJavaObject(jni, context, CYCastJSObject(context, value));
509
510 case kJSTypeUndefined:
c9c16dde
JF
511 // XXX: I am currently relying on this for dynamic proxy of void method
512 return NULL;
dbf05bfd
JF
513 default:
514 _assert(false);
515 }
516}
517
dbf05bfd
JF
518static JSObjectRef CYGetJavaClass(JSContextRef context, JNIEnv *jni, jclass value) {
519 JSObjectRef global(CYGetGlobalObject(context));
520 JSObjectRef cy(CYCastJSObject(context, CYGetProperty(context, global, cy_s)));
521
522 jclass Class$(_envcall(jni, FindClass("java/lang/Class")));
4b645e23 523 jmethodID Class$getName(_envcall(jni, GetMethodID(Class$, "getName", "()Ljava/lang/String;")));
dbf05bfd 524
4b645e23 525 CYJSString name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(value, Class$getName))));
dbf05bfd
JF
526 JSValueRef cached(CYGetProperty(context, cy, name));
527 if (!JSValueIsUndefined(context, cached))
528 return CYCastJSObject(context, cached);
529
c9c16dde
JF
530 jmethodID Class$isInterface(_envcall(jni, GetMethodID(Class$, "isInterface", "()Z")));
531
dbf05bfd
JF
532 jmethodID Class$getDeclaredConstructors(_envcall(jni, GetMethodID(Class$, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;")));
533 jmethodID Class$getDeclaredFields(_envcall(jni, GetMethodID(Class$, "getDeclaredFields", "()[Ljava/lang/reflect/Field;")));
534 jmethodID Class$getDeclaredMethods(_envcall(jni, GetMethodID(Class$, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;")));
535
536 jclass Constructor$(_envcall(jni, FindClass("java/lang/reflect/Constructor")));
537 //jmethodID Constructor$getModifiers(_envcall(jni, GetMethodID(Constructor$, "getModifiers", "()I")));
538 jmethodID Constructor$getParameterTypes(_envcall(jni, GetMethodID(Constructor$, "getParameterTypes", "()[Ljava/lang/Class;")));
539
540 jclass Field$(_envcall(jni, FindClass("java/lang/reflect/Field")));
541 jmethodID Field$getModifiers(_envcall(jni, GetMethodID(Field$, "getModifiers", "()I")));
542 jmethodID Field$getName(_envcall(jni, GetMethodID(Field$, "getName", "()Ljava/lang/String;")));
543 jmethodID Field$getType(_envcall(jni, GetMethodID(Field$, "getType", "()Ljava/lang/Class;")));
544
545 jclass Method$(_envcall(jni, FindClass("java/lang/reflect/Method")));
546 jmethodID Method$getModifiers(_envcall(jni, GetMethodID(Method$, "getModifiers", "()I")));
547 jmethodID Method$getName(_envcall(jni, GetMethodID(Method$, "getName", "()Ljava/lang/String;")));
548 jmethodID Method$getParameterTypes(_envcall(jni, GetMethodID(Method$, "getParameterTypes", "()[Ljava/lang/Class;")));
549 jmethodID Method$getReturnType(_envcall(jni, GetMethodID(Method$, "getReturnType", "()Ljava/lang/Class;")));
550
551 jclass Modifier$(_envcall(jni, FindClass("java/lang/reflect/Modifier")));
552 jmethodID Modifier$isStatic(_envcall(jni, GetStaticMethodID(Modifier$, "isStatic", "(I)Z")));
553
c9c16dde
JF
554 bool interface(_envcall(jni, CallBooleanMethod(value, Class$isInterface)));
555 CYJavaClass *table(new CYJavaClass(jni, value, interface));
dbf05bfd
JF
556
557 for (jclass prototype(value); prototype != NULL; prototype = _envcall(jni, GetSuperclass(prototype))) {
558 jobjectArray fields(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(prototype, Class$getDeclaredFields))));
559
560 for (jsize i(0), e(_envcall(jni, GetArrayLength(fields))); i != e; ++i) {
561 jobject field(_envcall(jni, GetObjectArrayElement(fields, e - i - 1)));
562 jint modifiers(_envcall(jni, CallIntMethod(field, Field$getModifiers)));
563 bool instance(!_envcall(jni, CallStaticBooleanMethod(Modifier$, Modifier$isStatic, modifiers)));
564 auto &map(instance ? table->instance_ : table->static_);
565 CYJavaUTF8String name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(field, Field$getName))));
566 jfieldID id(_envcall(jni, FromReflectedField(field)));
567 jobject type(_envcall(jni, CallObjectMethod(field, Field$getType)));
c9c16dde 568 map.insert(std::make_pair(std::string(name), CYJavaField{id, CYJavaGetPrimitive(context, jni, type, Class$getName)}));
dbf05bfd
JF
569 }
570 }
571
572 JSObjectRef constructor(JSObjectMake(context, CYJavaClass::Class_, table));
dbf05bfd
JF
573
574 JSObjectRef prototype(JSObjectMake(context, NULL, NULL));
575 CYSetProperty(context, constructor, prototype_s, prototype, kJSPropertyAttributeDontEnum);
576
577 jobjectArray constructors(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(value, Class$getDeclaredConstructors))));
578
579 for (jsize i(0), e(_envcall(jni, GetArrayLength(constructors))); i != e; ++i) {
580 jobject constructor(_envcall(jni, GetObjectArrayElement(constructors, i)));
581 jobjectArray parameters(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(constructor, Constructor$getParameterTypes))));
c9c16dde 582 CYJavaShorty shorty(CYJavaGetShorty(context, jni, parameters, Class$getName));
4b645e23
JF
583 jmethodID id(_envcall(jni, FromReflectedMethod(constructor)));
584 table->overload_.insert(CYJavaSignature(jni, id, constructor, CYJavaPrimitiveObject, shorty));
dbf05bfd
JF
585 }
586
587 jobjectArray methods(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(value, Class$getDeclaredMethods))));
588
589 std::map<std::pair<bool, std::string>, CYJavaOverload> entries;
590
591 for (jsize i(0), e(_envcall(jni, GetArrayLength(methods))); i != e; ++i) {
592 jobject method(_envcall(jni, GetObjectArrayElement(methods, i)));
593 jint modifiers(_envcall(jni, CallIntMethod(method, Method$getModifiers)));
594 bool instance(!_envcall(jni, CallStaticBooleanMethod(Modifier$, Modifier$isStatic, modifiers)));
595 CYJavaUTF8String name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(method, Method$getName))));
596 jobjectArray parameters(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(method, Method$getParameterTypes))));
c9c16dde 597 CYJavaShorty shorty(CYJavaGetShorty(context, jni, parameters, Class$getName));
dbf05bfd 598 jobject type(_envcall(jni, CallObjectMethod(method, Method$getReturnType)));
c9c16dde 599 auto primitive(CYJavaGetPrimitive(context, jni, type, Class$getName));
4b645e23
JF
600 jmethodID id(_envcall(jni, FromReflectedMethod(method)));
601 entries[std::make_pair(instance, std::string(name))].insert(CYJavaSignature(jni, id, method, primitive, shorty));
dbf05bfd
JF
602 }
603
604 for (const auto &entry : entries) {
605 bool instance(entry.first.first);
606 CYJSString name(entry.first.second);
607 auto &overload(entry.second);
c9c16dde
JF
608 if (instance)
609 CYSetProperty(context, prototype, name, CYJavaMethod::Make(context, overload), kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete);
610 else
611 CYSetProperty(context, constructor, name, CYJavaStaticMethod::Make(context, overload), kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete);
dbf05bfd
JF
612 }
613
614 // XXX: for some reason kJSPropertyAttributeDontEnum doesn't work if there's already a property with the same name
615 // by not linking the prototypes until after we set the properties, we hide the parent property from this issue :(
616
617 if (jclass super = _envcall(jni, GetSuperclass(value))) {
618 JSObjectRef parent(CYGetJavaClass(context, jni, super));
4b645e23 619 CYSetPrototype(context, constructor, parent);
dbf05bfd
JF
620 CYSetPrototype(context, prototype, CYGetProperty(context, parent, prototype_s));
621 }
622
623 CYSetProperty(context, cy, name, constructor);
624 return constructor;
625}
626
4b645e23
JF
627static void CYCastJavaNumeric(jvalue &value, CYJavaPrimitive primitive, JSContextRef context, JSValueRef argument) {
628 switch (primitive) {
629#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
630 case CYJavaPrimitive ## Type: \
631 value.t = static_cast<j ## type>(CYCastDouble(context, argument)); \
632 break;
dbf05bfd
JF
633CYJavaForEachPrimitive
634#undef CYJavaForEachPrimitive_
4b645e23
JF
635 default:
636 _assert(false);
637 }
638}
639
640static bool CYCastJavaArguments(JNIEnv *jni, const CYJavaShorty &shorty, JSContextRef context, const JSValueRef arguments[], jvalue *array) {
641 for (size_t index(0); index != shorty.size(); ++index) {
642 JSValueRef argument(arguments[index]);
643 JSType type(JSValueGetType(context, argument));
644 jvalue &value(array[index]);
645
646 switch (CYJavaPrimitive primitive = shorty[index]) {
647 case CYJavaPrimitiveObject:
648 value.l = CYCastJavaObject(jni, context, argument);
649 break;
650
651 case CYJavaPrimitiveBoolean:
652 if (type != kJSTypeBoolean)
653 return false;
654 value.z = CYCastBool(context, argument);
655 break;
656
657 case CYJavaPrimitiveCharacter:
658 if (type == kJSTypeNumber)
659 CYCastJavaNumeric(value, primitive, context, argument);
660 else if (type != kJSTypeString)
661 return false;
662 else {
663 CYJSString string(context, argument);
664 if (JSStringGetLength(string) != 1)
665 return false;
666 else
667 value.c = JSStringGetCharactersPtr(string)[0];
668 }
669 break;
670
671 case CYJavaPrimitiveByte:
672 case CYJavaPrimitiveShort:
673 case CYJavaPrimitiveInteger:
674 case CYJavaPrimitiveLong:
675 case CYJavaPrimitiveFloat:
676 case CYJavaPrimitiveDouble:
677 if (type != kJSTypeNumber)
678 return false;
679 CYCastJavaNumeric(value, primitive, context, argument);
680 break;
681
dbf05bfd
JF
682 default:
683 _assert(false);
684 }
dbf05bfd
JF
685 }
686
4b645e23 687 return true;
dbf05bfd
JF
688}
689
690static JSValueRef JavaMethod_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
691 CYJavaMethod *internal(reinterpret_cast<CYJavaMethod *>(JSObjectGetPrivate(object)));
c9c16dde
JF
692 CYJavaObject *self(CYGetJavaObject(context, _this));
693 JNIEnv *jni(self->value_);
694
695 CYJavaSignature bound(count);
696 for (auto overload(internal->overload_.lower_bound(bound)), e(internal->overload_.upper_bound(bound)); overload != e; ++overload) {
697 jvalue array[count];
698 if (!CYCastJavaArguments(jni, overload->shorty_, context, arguments, array))
699 continue;
700 switch (overload->primitive_) {
701 case CYJavaPrimitiveObject:
702 return CYCastJSValue(context, jni, _envcall(jni, CallObjectMethodA(self->value_, overload->method_, array)));
703 case CYJavaPrimitiveVoid:
704 _envcallv(jni, CallVoidMethodA(self->value_, overload->method_, array));
705 return CYJSUndefined(context);
706#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
707 case CYJavaPrimitive ## Type: \
708 return CYJavaCastJSValue(context, _envcall(jni, Call ## Typ ## MethodA(self->value_, overload->method_, array)));
709CYJavaForEachPrimitive
710#undef CYJavaForEachPrimitive_
711 default: _assert(false);
712 }
713 }
714
715 CYThrow("invalid method call");
716} CYCatch(NULL) }
717
718static JSValueRef JavaStaticMethod_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
719 CYJavaMethod *internal(reinterpret_cast<CYJavaMethod *>(JSObjectGetPrivate(object)));
720 CYJavaClass *table(CYGetJavaTable(context, _this));
721 JNIEnv *jni(table->value_);
dbf05bfd
JF
722
723 CYJavaSignature bound(count);
724 for (auto overload(internal->overload_.lower_bound(bound)), e(internal->overload_.upper_bound(bound)); overload != e; ++overload) {
4b645e23
JF
725 jvalue array[count];
726 if (!CYCastJavaArguments(jni, overload->shorty_, context, arguments, array))
727 continue;
728 switch (overload->primitive_) {
729 case CYJavaPrimitiveObject:
c9c16dde
JF
730 return CYCastJSValue(context, jni, _envcall(jni, CallStaticObjectMethodA(table->value_, overload->method_, array)));
731 case CYJavaPrimitiveVoid:
732 _envcallv(jni, CallStaticVoidMethodA(table->value_, overload->method_, array));
733 return CYJSUndefined(context);
4b645e23
JF
734#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
735 case CYJavaPrimitive ## Type: \
c9c16dde 736 return CYJavaCastJSValue(context, _envcall(jni, CallStatic ## Typ ## MethodA(table->value_, overload->method_, array)));
4b645e23
JF
737CYJavaForEachPrimitive
738#undef CYJavaForEachPrimitive_
739 default: _assert(false);
740 }
dbf05bfd
JF
741 }
742
743 CYThrow("invalid method call");
744} CYCatch(NULL) }
745
746static JSObjectRef JavaClass_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
747 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
748 JNIEnv *jni(table->value_);
c9c16dde
JF
749 jclass _class(table->value_);
750
751 if (table->interface_ && count == 1) {
752 JSObjectRef target(CYCastJSObject(context, arguments[0]));
753 jclass Cycript$(_envcall(jni, FindClass("Cycript")));
754 jmethodID Cycript$Make(_envcall(jni, GetStaticMethodID(Cycript$, "proxy", "(Ljava/lang/Class;J)Ljava/lang/Object;")));
755 CYProtect *protect(new CYProtect(context, target));
756 return CYCastJSObject(context, jni, _envcall(jni, CallObjectMethod(Cycript$, Cycript$Make, _class, reinterpret_cast<jlong>(protect))));
757 }
dbf05bfd 758
dbf05bfd
JF
759 CYJavaSignature bound(count);
760 for (auto overload(table->overload_.lower_bound(bound)), e(table->overload_.upper_bound(bound)); overload != e; ++overload) {
4b645e23
JF
761 jvalue array[count];
762 if (!CYCastJavaArguments(jni, overload->shorty_, context, arguments, array))
763 continue;
c9c16dde
JF
764 jobject object(_envcall(jni, NewObjectA(_class, overload->method_, array)));
765 return CYCastJSObject(context, jni, object);
dbf05bfd
JF
766 }
767
768 CYThrow("invalid constructor call");
769} CYCatch(NULL) }
770
c9c16dde
JF
771static bool JavaStaticInterior_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
772 CYJavaStaticInterior *internal(reinterpret_cast<CYJavaStaticInterior *>(JSObjectGetPrivate(object)));
4b645e23 773 CYJavaClass *table(internal->table_);
dbf05bfd
JF
774 CYPool pool;
775 auto name(CYPoolUTF8String(pool, context, property));
776 auto field(table->static_.find(name));
777 if (field == table->static_.end())
778 return false;
779 return true;
780}
781
c9c16dde
JF
782static JSValueRef JavaStaticInterior_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
783 CYJavaStaticInterior *internal(reinterpret_cast<CYJavaStaticInterior *>(JSObjectGetPrivate(object)));
4b645e23 784 CYJavaClass *table(internal->table_);
dbf05bfd
JF
785 JNIEnv *jni(table->value_);
786 CYPool pool;
787 auto name(CYPoolUTF8String(pool, context, property));
788 auto field(table->static_.find(name));
789 if (field == table->static_.end())
790 return NULL;
791
792 switch (field->second.primitive_) {
793 case CYJavaPrimitiveObject:
794 return CYCastJSValue(context, jni, _envcall(jni, GetStaticObjectField(table->value_, field->second.field_)));
4b645e23 795#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
dbf05bfd
JF
796 case CYJavaPrimitive ## Type: \
797 return CYJavaCastJSValue(context, _envcall(jni, GetStatic ## Typ ## Field(table->value_, field->second.field_)));
798CYJavaForEachPrimitive
799#undef CYJavaForEachPrimitive_
800 default: _assert(false);
801 }
802} CYCatch(NULL) }
803
c9c16dde
JF
804static bool JavaStaticInterior_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry {
805 CYJavaStaticInterior *internal(reinterpret_cast<CYJavaStaticInterior *>(JSObjectGetPrivate(object)));
4b645e23 806 CYJavaClass *table(internal->table_);
dbf05bfd
JF
807 JNIEnv *jni(table->value_);
808 CYPool pool;
809 auto name(CYPoolUTF8String(pool, context, property));
810 auto field(table->static_.find(name));
811 if (field == table->static_.end())
812 return false;
813
814 switch (field->second.primitive_) {
815 case CYJavaPrimitiveObject:
816 _envcallv(jni, SetStaticObjectField(table->value_, field->second.field_, CYCastJavaObject(jni, context, value)));
4b645e23 817#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
dbf05bfd
JF
818 case CYJavaPrimitive ## Type: \
819 _envcallv(jni, SetStatic ## Typ ## Field(table->value_, field->second.field_, CYCastDouble(context, value))); \
820 break;
821CYJavaForEachPrimitive
822#undef CYJavaForEachPrimitive_
823 default: _assert(false);
824 }
825
826 return true;
827} CYCatch(false) }
828
c9c16dde
JF
829static void JavaStaticInterior_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
830 CYJavaStaticInterior *internal(reinterpret_cast<CYJavaStaticInterior *>(JSObjectGetPrivate(object)));
4b645e23 831 CYJavaClass *table(internal->table_);
dbf05bfd
JF
832 for (const auto &field : table->static_)
833 JSPropertyNameAccumulatorAddName(names, CYJSString(field.first));
834}
835
836static JSValueRef JavaClass_getProperty_class(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
837 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
838 return CYCastJSValue(context, table->value_, table->value_);
839} CYCatch(NULL) }
840
8effd381
JF
841static bool JavaInterior_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
842 CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object)));
843 CYJavaClass *table(internal->table_);
dbf05bfd
JF
844 CYPool pool;
845 auto name(CYPoolUTF8String(pool, context, property));
846 auto field(table->instance_.find(name));
847 if (field == table->instance_.end())
848 return false;
849 return true;
850}
851
8effd381
JF
852static JSValueRef JavaInterior_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
853 CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object)));
dbf05bfd 854 JNIEnv *jni(internal->value_);
8effd381 855 CYJavaClass *table(internal->table_);
dbf05bfd
JF
856 CYPool pool;
857 auto name(CYPoolUTF8String(pool, context, property));
858 auto field(table->instance_.find(name));
859 if (field == table->instance_.end())
860 return NULL;
861
862 switch (field->second.primitive_) {
863 case CYJavaPrimitiveObject:
864 return CYCastJSValue(context, jni, _envcall(jni, GetObjectField(internal->value_, field->second.field_)));
4b645e23 865#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
dbf05bfd
JF
866 case CYJavaPrimitive ## Type: \
867 return CYJavaCastJSValue(context, _envcall(jni, Get ## Typ ## Field(internal->value_, field->second.field_)));
868CYJavaForEachPrimitive
869#undef CYJavaForEachPrimitive_
870 default: _assert(false);
871 }
872} CYCatch(NULL) }
873
8effd381
JF
874static bool JavaInterior_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry {
875 CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object)));
dbf05bfd 876 JNIEnv *jni(internal->value_);
8effd381 877 CYJavaClass *table(internal->table_);
dbf05bfd
JF
878 CYPool pool;
879 auto name(CYPoolUTF8String(pool, context, property));
880 auto field(table->instance_.find(name));
881 if (field == table->instance_.end())
882 return false;
883
884 switch (field->second.primitive_) {
885 case CYJavaPrimitiveObject:
886 _envcallv(jni, SetObjectField(table->value_, field->second.field_, CYCastJavaObject(jni, context, value)));
4b645e23 887#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
dbf05bfd
JF
888 case CYJavaPrimitive ## Type: \
889 _envcallv(jni, Set ## Typ ## Field(table->value_, field->second.field_, CYCastDouble(context, value))); \
890 break;
891CYJavaForEachPrimitive
892#undef CYJavaForEachPrimitive_
893 default: _assert(false);
894 }
895
896 return true;
897} CYCatch(false) }
898
8effd381
JF
899static void JavaInterior_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
900 CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object)));
901 CYJavaClass *table(internal->table_);
dbf05bfd
JF
902 for (const auto &field : table->instance_)
903 JSPropertyNameAccumulatorAddName(names, CYJSString(field.first));
904}
905
906static JSValueRef JavaObject_getProperty_constructor(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
907 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
908 JNIEnv *jni(internal->value_);
909 return CYGetJavaClass(context, jni, _envcall(jni, GetObjectClass(internal->value_)));
910} CYCatch(NULL) }
911
4b645e23
JF
912static JSValueRef JavaClass_getProperty_$cyi(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
913 CYJavaClass *internal(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
914 JNIEnv *jni(internal->value_);
c9c16dde 915 return CYJavaStaticInterior::Make(context, jni, internal->value_, internal);
4b645e23
JF
916} CYCatch(NULL) }
917
8effd381
JF
918static JSValueRef JavaObject_getProperty_$cyi(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
919 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
920 JNIEnv *jni(internal->value_);
921 return CYJavaInterior::Make(context, jni, internal->value_, internal->table_);
922} CYCatch(NULL) }
923
4b645e23
JF
924static JSValueRef JavaClass_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
925 CYJavaClass *internal(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(_this)));
926 JNIEnv *jni(internal->value_);
927 jclass Class$(_envcall(jni, FindClass("java/lang/Class")));
928 jmethodID Class$getCanonicalName(_envcall(jni, GetMethodID(Class$, "getCanonicalName", "()Ljava/lang/String;")));
929 return CYCastJSValue(context, CYJSString(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(internal->value_, Class$getCanonicalName)))));
930} CYCatch(NULL) }
931
932static JSValueRef JavaMethod_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
933 std::ostringstream cyon;
934 return CYCastJSValue(context, CYJSString(cyon.str()));
935} CYCatch(NULL) }
936
c9c16dde
JF
937static JSValueRef JavaStaticMethod_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
938 std::ostringstream cyon;
939 return CYCastJSValue(context, CYJSString(cyon.str()));
940} CYCatch(NULL) }
941
4b645e23
JF
942static JSValueRef JavaArray_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
943 CYJavaArray *internal(reinterpret_cast<CYJavaArray *>(JSObjectGetPrivate(object)));
944 JNIEnv *jni(internal->value_);
945 if (JSStringIsEqual(property, length_s))
946 return CYCastJSValue(context, _envcall(jni, GetArrayLength(internal->value_)));
947
948 CYPool pool;
949 ssize_t offset;
950 if (!CYGetOffset(pool, context, property, offset))
951 return NULL;
952
953 if (internal->primitive_ == CYJavaPrimitiveObject)
954 return CYCastJSValue(context, jni, _envcall(jni, GetObjectArrayElement(static_cast<jobjectArray>(internal->value_.value_), offset)));
955 else switch (internal->primitive_) {
956#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
957 case CYJavaPrimitive ## Type: { \
958 j ## type element; \
959 _envcallv(jni, Get ## Typ ## ArrayRegion(static_cast<j ## type ## Array>(internal->value_.value_), offset, 1, &element)); \
960 return CYJavaCastJSValue(context, element); \
961 } break;
962CYJavaForEachPrimitive
963#undef CYJavaForEachPrimitive_
964 default: _assert(false);
965 }
966} CYCatch(NULL) }
967
968static bool JavaArray_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry {
969 CYJavaArray *internal(reinterpret_cast<CYJavaArray *>(JSObjectGetPrivate(object)));
970 JNIEnv *jni(internal->value_);
971
972 CYPool pool;
973 ssize_t offset;
974 if (!CYGetOffset(pool, context, property, offset))
975 return false;
976
977 if (internal->primitive_ == CYJavaPrimitiveObject)
978 _envcallv(jni, SetObjectArrayElement(static_cast<jobjectArray>(internal->value_.value_), offset, CYCastJavaObject(jni, context, value)));
979 else switch (internal->primitive_) {
980#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
981 case CYJavaPrimitive ## Type: { \
982 j ## type element; \
983 _envcallv(jni, Get ## Typ ## ArrayRegion(static_cast<j ## type ## Array>(internal->value_.value_), offset, 1, &element)); \
984 return CYJavaCastJSValue(context, element); \
985 } break;
986CYJavaForEachPrimitive
987#undef CYJavaForEachPrimitive_
988 default: _assert(false);
989 }
990
991 return true;
992} CYCatch(false) }
993
994static JSValueRef JavaPackage_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
995 CYJavaPackage *internal(reinterpret_cast<CYJavaPackage *>(JSObjectGetPrivate(_this)));
996 std::ostringstream name;
997 for (auto &package : internal->package_)
998 name << package << '.';
999 name << '*';
1000 return CYCastJSValue(context, CYJSString(name.str()));
1001} CYCatch(NULL) }
1002
dbf05bfd
JF
1003static bool CYJavaPackage_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
1004 return true;
1005}
1006
1007static JSValueRef CYJavaPackage_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
1008 CYJavaPackage *internal(reinterpret_cast<CYJavaPackage *>(JSObjectGetPrivate(object)));
1009 CYJavaPackage::Path package(internal->package_);
1010
1011 CYPool pool;
1012 const char *next(CYPoolCString(pool, context, property));
1013
1014 std::ostringstream name;
1015 for (auto &package : internal->package_)
1016 name << package << '/';
1017 name << next;
1018
c9c16dde 1019 JNIEnv *jni(GetJNI(context));
dbf05bfd
JF
1020 if (jclass _class = jni->FindClass(name.str().c_str()))
1021 return CYGetJavaClass(context, jni, _class);
1022 jni->ExceptionClear();
1023
1024 package.push_back(next);
1025 return CYJavaPackage::Make(context, package);
1026} CYCatch(NULL) }
1027
c9c16dde 1028static void Cycript_delete(JNIEnv *jni, jclass api, jlong jprotect) { CYJavaTry {
824bc1ec 1029 delete &protect;
c9c16dde
JF
1030} CYJavaCatch() }
1031
1032static jobject Cycript_handle(JNIEnv *jni, jclass api, jlong jprotect, jstring property, jobjectArray jarguments) { CYJavaTry {
824bc1ec 1033 JSValueRef function(CYGetProperty(context, object, CYJSString(jni, property)));
c9c16dde
JF
1034 if (JSValueIsUndefined(context, function))
1035 return NULL;
1036
1037 size_t count(jarguments == NULL ? 0 : _envcall(jni, GetArrayLength(jarguments)));
1038 JSValueRef arguments[count];
1039 for (size_t index(0); index != count; ++index)
1040 arguments[index] = CYCastJSValue(context, jni, _envcall(jni, GetObjectArrayElement(jarguments, index)));
1041
824bc1ec 1042 return CYCastJavaObject(jni, context, CYCallAsFunction(context, CYCastJSObject(context, function), object, count, arguments));
c9c16dde
JF
1043} CYJavaCatch(NULL) }
1044
1045static JNINativeMethod Cycript_[] = {
1046 {(char *) "delete", (char *) "(J)V", (void *) &Cycript_delete},
1047 {(char *) "handle", (char *) "(JLjava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;", (void *) &Cycript_handle},
1048};
1049
1050static void CYRegisterNatives(JSContextRef context, JNIEnv *jni) {
1051 jclass Cycript$(_envcall(jni, FindClass("Cycript")));
1052 _envcall(jni, RegisterNatives(Cycript$, Cycript_, sizeof(Cycript_) / sizeof(Cycript_[0])));
1053}
1054
4b645e23 1055static JSStaticValue JavaClass_staticValues[3] = {
dbf05bfd 1056 {"class", &JavaClass_getProperty_class, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
4b645e23 1057 {"$cyi", &JavaClass_getProperty_$cyi, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
dbf05bfd
JF
1058 {NULL, NULL, NULL, 0}
1059};
1060
4b645e23
JF
1061static JSStaticFunction JavaClass_staticFunctions[2] = {
1062 {"toCYON", &JavaClass_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1063 {NULL, NULL, 0}
1064};
1065
8effd381 1066static JSStaticValue JavaObject_staticValues[3] = {
dbf05bfd 1067 {"constructor", &JavaObject_getProperty_constructor, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
8effd381 1068 {"$cyi", &JavaObject_getProperty_$cyi, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
dbf05bfd
JF
1069 {NULL, NULL, NULL, 0}
1070};
1071
4b645e23
JF
1072static JSStaticFunction JavaMethod_staticFunctions[2] = {
1073 {"toCYON", &JavaMethod_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1074 {NULL, NULL, 0}
1075};
1076
c9c16dde
JF
1077static JSStaticFunction JavaStaticMethod_staticFunctions[2] = {
1078 {"toCYON", &JavaStaticMethod_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1079 {NULL, NULL, 0}
1080};
1081
4b645e23
JF
1082static JSStaticFunction JavaPackage_staticFunctions[2] = {
1083 {"toCYON", &JavaPackage_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
dbf05bfd
JF
1084 {NULL, NULL, 0}
1085};
1086
1087void CYJava_Initialize() {
1088 Primitives_.insert(std::make_pair("void", CYJavaPrimitiveVoid));
4b645e23 1089#define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \
42619b59
JF
1090 Primitives_.insert(std::make_pair(#type, CYJavaPrimitive ## Type));
1091CYJavaForEachPrimitive
1092#undef CYJavaForEachPrimitive_
dbf05bfd
JF
1093
1094 JSClassDefinition definition;
1095
1096 definition = kJSClassDefinitionEmpty;
1097 definition.className = "JavaClass";
1098 definition.staticValues = JavaClass_staticValues;
4b645e23 1099 definition.staticFunctions = JavaClass_staticFunctions;
dbf05bfd
JF
1100 definition.callAsConstructor = &JavaClass_callAsConstructor;
1101 definition.finalize = &CYFinalize;
1102 CYJavaClass::Class_ = JSClassCreate(&definition);
1103
8effd381
JF
1104 definition = kJSClassDefinitionEmpty;
1105 definition.attributes = kJSClassAttributeNoAutomaticPrototype;
1106 definition.className = "JavaInterior";
1107 definition.hasProperty = &JavaInterior_hasProperty;
1108 definition.getProperty = &JavaInterior_getProperty;
1109 definition.setProperty = &JavaInterior_setProperty;
1110 definition.getPropertyNames = &JavaInterior_getPropertyNames;
1111 definition.finalize = &CYFinalize;
1112 CYJavaInterior::Class_ = JSClassCreate(&definition);
1113
dbf05bfd
JF
1114 definition = kJSClassDefinitionEmpty;
1115 definition.className = "JavaMethod";
4b645e23 1116 definition.staticFunctions = JavaMethod_staticFunctions;
dbf05bfd
JF
1117 definition.callAsFunction = &JavaMethod_callAsFunction;
1118 definition.finalize = &CYFinalize;
1119 CYJavaMethod::Class_ = JSClassCreate(&definition);
1120
c9c16dde
JF
1121 definition = kJSClassDefinitionEmpty;
1122 definition.className = "JavaStaticMethod";
1123 definition.staticFunctions = JavaStaticMethod_staticFunctions;
1124 definition.callAsFunction = &JavaStaticMethod_callAsFunction;
1125 definition.finalize = &CYFinalize;
1126 CYJavaStaticMethod::Class_ = JSClassCreate(&definition);
1127
dbf05bfd
JF
1128 definition = kJSClassDefinitionEmpty;
1129 definition.attributes = kJSClassAttributeNoAutomaticPrototype;
1130 definition.className = "JavaObject";
1131 definition.staticValues = JavaObject_staticValues;
dbf05bfd
JF
1132 definition.finalize = &CYFinalize;
1133 CYJavaObject::Class_ = JSClassCreate(&definition);
1134
4b645e23
JF
1135 definition = kJSClassDefinitionEmpty;
1136 definition.className = "JavaArray";
1137 definition.getProperty = &JavaArray_getProperty;
1138 definition.setProperty = &JavaArray_setProperty;
1139 definition.finalize = &CYFinalize;
1140 CYJavaArray::Class_ = JSClassCreate(&definition);
1141
dbf05bfd
JF
1142 definition = kJSClassDefinitionEmpty;
1143 definition.className = "JavaPackage";
1144 definition.staticFunctions = JavaPackage_staticFunctions;
1145 definition.hasProperty = &CYJavaPackage_hasProperty;
1146 definition.getProperty = &CYJavaPackage_getProperty;
1147 definition.finalize = &CYFinalize;
1148 CYJavaPackage::Class_ = JSClassCreate(&definition);
4b645e23
JF
1149
1150 definition = kJSClassDefinitionEmpty;
1151 definition.attributes = kJSClassAttributeNoAutomaticPrototype;
c9c16dde
JF
1152 definition.className = "JavaStaticInterior";
1153 definition.hasProperty = &JavaStaticInterior_hasProperty;
1154 definition.getProperty = &JavaStaticInterior_getProperty;
1155 definition.setProperty = &JavaStaticInterior_setProperty;
1156 definition.getPropertyNames = &JavaStaticInterior_getPropertyNames;
4b645e23 1157 definition.finalize = &CYFinalize;
c9c16dde 1158 CYJavaStaticInterior::Class_ = JSClassCreate(&definition);
dbf05bfd
JF
1159}
1160
1161void CYJava_SetupContext(JSContextRef context) {
1162 JSObjectRef global(CYGetGlobalObject(context));
1163 //JSObjectRef cy(CYCastJSObject(context, CYGetProperty(context, global, cy_s)));
1164 JSObjectRef cycript(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Cycript"))));
1165 JSObjectRef all(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("all"))));
1166 //JSObjectRef alls(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("alls"))));
1167
1168 JSObjectRef Java(JSObjectMake(context, NULL, NULL));
1169 CYSetProperty(context, cycript, CYJSString("Java"), Java);
1170
1171 JSObjectRef Packages(CYJavaPackage::Make(context, CYJavaPackage::Path()));
1172 CYSetProperty(context, all, CYJSString("Packages"), Packages);
1173
1174 for (auto name : (const char *[]) {"java", "javax", "android", "com", "net", "org"}) {
1175 CYJSString js(name);
1176 CYSetProperty(context, all, js, CYGetProperty(context, Packages, js));
1177 }
1178}
1179
1180static CYHook CYJavaHook = {
1181 NULL,
1182 NULL,
1183 NULL,
1184 &CYJava_Initialize,
1185 &CYJava_SetupContext,
1186 NULL,
1187};
1188
1189CYRegisterHook CYJava(&CYJavaHook);