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