]> git.saurik.com Git - cycript.git/blame - Java/Execute.cpp
This implementation for Java works, but I hate it.
[cycript.git] / Java / Execute.cpp
CommitLineData
e17ad658 1/* Cycript - Optimizing JavaScript Compiler/Runtime
c1d3e52e 2 * Copyright (C) 2009-2015 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"
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
60extern "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
66JNIEnv *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
92class 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 size = jni_->GetStringUTFLength(value_);
105 data = jni_->GetStringUTFChars(value_, NULL);
106 }
107
108 ~CYJavaUTF8String() {
109 if (value_ != NULL)
110 jni_->ReleaseStringUTFChars(value_, data);
111 }
112
113 CYJavaUTF8String(const CYJavaUTF8String &) = delete;
114
115 CYJavaUTF8String(CYJavaUTF8String &&rhs) :
116 jni_(rhs.jni_),
117 value_(rhs.value_)
118 {
119 rhs.value_ = NULL;
120 }
121};
122
123CYJavaUTF8String CYCastUTF8String(JNIEnv *jni, jstring value) {
124 return CYJavaUTF8String(jni, value);
125}
126
127JSStringRef CYCopyJSString(JNIEnv *jni, jstring value) {
128 return CYCopyJSString(CYCastUTF8String(jni, value));
129}
130
131template <typename Value_>
132struct CYJavaGlobal {
133 JNIEnv *jni_;
134 Value_ value_;
135
136 CYJavaGlobal() :
137 jni_(NULL),
138 value_(NULL)
139 {
140 }
141
142 CYJavaGlobal(JNIEnv *jni, Value_ value) :
143 jni_(jni),
144 value_(static_cast<Value_>(_envcall(jni_, NewGlobalRef(value))))
145 {
146 }
147
148 CYJavaGlobal(const CYJavaGlobal &value) :
149 CYJavaGlobal(value.jni_, value.value_)
150 {
151 }
152
153 CYJavaGlobal(CYJavaGlobal &&value) :
154 jni_(value.jni_),
155 value_(value.value_)
156 {
157 value.value_ = NULL;
158 }
159
160 ~CYJavaGlobal() {
161 if (value_ != NULL)
162 _envcallv(jni_, DeleteGlobalRef(value_));
163 }
164
165 operator bool() const {
166 return value_ != NULL;
167 }
168
169 operator JNIEnv *() const {
170 return jni_;
171 }
172
173 operator Value_() const {
174 return value_;
175 }
176};
177
178template <typename Internal_, typename Value_>
179struct CYJavaValue :
180 CYPrivate<Internal_>
181{
182 CYJavaGlobal<Value_> value_;
183
184 CYJavaValue(JNIEnv *jni, Value_ value) :
185 value_(jni, value)
186 {
187 }
188
189 CYJavaValue(const CYJavaValue &) = delete;
190};
191
192#define CYJavaForEachPrimitive \
193 CYJavaForEachPrimitive_(Z, Boolean, Boolean) \
194 CYJavaForEachPrimitive_(B, Byte, Byte) \
195 CYJavaForEachPrimitive_(C, Char, Character) \
196 CYJavaForEachPrimitive_(S, Short, Short) \
197 CYJavaForEachPrimitive_(I, Int, Integer) \
198 CYJavaForEachPrimitive_(J, Long, Long) \
199 CYJavaForEachPrimitive_(F, Float, Float) \
200 CYJavaForEachPrimitive_(D, Double, Double)
201
202enum CYJavaPrimitive : char {
203 CYJavaPrimitiveObject,
204 CYJavaPrimitiveVoid,
205#define CYJavaForEachPrimitive_(T, Typ, Type) \
206 CYJavaPrimitive ## Type,
207CYJavaForEachPrimitive
208#undef CYJavaForEachPrimitive_
209};
210
211template <typename Type_>
212static _finline JSValueRef CYJavaCastJSValue(JSContextRef context, Type_ value) {
213 return CYCastJSValue(context, value);
214}
215
216static _finline JSValueRef CYJavaCastJSValue(JSContextRef context, jboolean value) {
217 return CYCastJSValue(context, static_cast<bool>(value));
218}
219
220static std::map<std::string, CYJavaPrimitive> Primitives_;
221
222static CYJavaPrimitive CYJavaGetPrimitive(JNIEnv *jni, jobject type, jmethodID Class$get$$Name) {
223 CYJavaUTF8String name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(type, Class$get$$Name))));
224 auto primitive(Primitives_.find(name));
225 return primitive != Primitives_.end() ? primitive->second : CYJavaPrimitiveObject;
226}
227
228typedef std::vector<CYJavaPrimitive> CYJavaShorty;
229
230static CYJavaShorty CYJavaGetShorty(JNIEnv *jni, jobjectArray types, jmethodID Class$get$$Name) {
231 size_t count(_envcall(jni, GetArrayLength(types)));
232 CYJavaShorty shorty(count);
233 for (size_t index(0); index != count; ++index)
234 shorty[index] = CYJavaGetPrimitive(jni, _envcall(jni, GetObjectArrayElement(types, index)), Class$get$$Name);
235 return shorty;
236}
237
238struct CYJavaField {
239 jfieldID field_;
240 CYJavaPrimitive primitive_;
241};
242
243typedef std::map<std::string, CYJavaField> CYJavaFieldMap;
244
245struct CYJavaSignature {
246 CYJavaGlobal<jobject> method;
247 CYJavaPrimitive primitive;
248 CYJavaShorty shorty;
249
250 CYJavaSignature(JNIEnv *jni, jobject method, CYJavaPrimitive primitive, const CYJavaShorty &shorty) :
251 method(jni, method),
252 primitive(primitive),
253 shorty(shorty)
254 {
255 }
256
257 CYJavaSignature(unsigned count) :
258 shorty(count)
259 {
260 }
261
262 bool operator <(const CYJavaSignature &rhs) const {
263 return shorty.size() < rhs.shorty.size();
264 }
265};
266
267typedef std::multiset<CYJavaSignature> CYJavaOverload;
268
269struct CYJavaMethod :
270 CYPrivate<CYJavaMethod>
271{
272 JNIEnv *jni_;
273 CYJavaOverload overload_;
274
275 // XXX: figure out move constructors on Apple's crappy toolchain
276 CYJavaMethod(JNIEnv *jni, const CYJavaOverload &overload) :
277 jni_(jni),
278 overload_(overload)
279 {
280 }
281};
282
283struct CYJavaClass :
284 CYJavaValue<CYJavaClass, jclass>
285{
286 CYJavaFieldMap static_;
287 CYJavaFieldMap instance_;
288 CYJavaOverload overload_;
289
290 CYJavaClass(JNIEnv *jni, jclass value) :
291 CYJavaValue(jni, value)
292 {
293 }
294};
295
296struct CYJavaObject :
297 CYJavaValue<CYJavaObject, jobject>
298{
299 CYJavaObject(JNIEnv *jni, jobject value) :
300 CYJavaValue(jni, value)
301 {
302 }
303
304 JSValueRef GetPrototype(JSContextRef context) const;
305};
306
307struct CYJavaPackage :
308 CYPrivate<CYJavaPackage>
309{
310 typedef std::vector<std::string> Path;
311 Path package_;
312
313 _finline CYJavaPackage(const Path &package) :
314 package_(package)
315 {
316 }
317};
318
319static JSObjectRef CYGetJavaClass(JSContextRef context, JNIEnv *jni, jclass _class);
320
321JSValueRef CYJavaObject::GetPrototype(JSContextRef context) const {
322 JNIEnv *jni(value_);
323 return CYGetProperty(context, CYGetJavaClass(context, jni, _envcall(jni, GetObjectClass(value_))), prototype_s);
324}
325
326static JSValueRef CYCastJSValue(JSContextRef context, JNIEnv *jni, jobject value) {
327 if (value == NULL)
328 return CYJSNull(context);
329
330 return CYJavaObject::Make(context, jni, value);
331}
332
333static jstring CYCastJavaString(JNIEnv *jni, CYUTF16String value) {
334 return _envcall(jni, NewString(value.data, value.size));
335}
336
337static jstring CYCastJavaString(JNIEnv *jni, JSStringRef value) {
338 return CYCastJavaString(jni, CYCastUTF16String(value));
339}
340
341#define CYCastJava$(T, Type, jtype, Cast) \
342_disused static jobject CYCastJava ## Type(JNIEnv *jni, JSContextRef context, JSValueRef value) { \
343 jclass Type$(_envcall(jni, FindClass("java/lang/" #Type))); \
344 jmethodID Type$init$(_envcall(jni, GetMethodID(Type$, "<init>", "(" #T ")V"))); \
345 return _envcall(jni, NewObject(Type$, Type$init$, static_cast<jtype>(Cast(context, value)))); \
346}
347
348CYCastJava$(Z, Boolean, jboolean, CYCastBool)
349CYCastJava$(B, Byte, jbyte, CYCastDouble)
350CYCastJava$(C, Character, jchar, CYCastDouble)
351CYCastJava$(S, Short, jshort, CYCastDouble)
352CYCastJava$(I, Integer, jint, CYCastDouble)
353CYCastJava$(J, Long, jlong, CYCastDouble)
354CYCastJava$(F, Float, jfloat, CYCastDouble)
355CYCastJava$(D, Double, jdouble, CYCastDouble)
356
357static jobject CYCastJavaObject(JNIEnv *jni, JSContextRef context, JSObjectRef value) {
358 JSObjectRef object(CYCastJSObject(context, value));
359 if (JSValueIsObjectOfClass(context, value, CYJavaObject::Class_)) {
360 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
361 return internal->value_;
362 }
363
364 _assert(false);
365}
366
367static jobject CYCastJavaObject(JNIEnv *jni, JSContextRef context, JSValueRef value) {
368 switch (JSValueGetType(context, value)) {
369 case kJSTypeNull:
370 return NULL;
371 case kJSTypeBoolean:
372 return CYCastJavaBoolean(jni, context, value);
373 case kJSTypeNumber:
374 return CYCastJavaDouble(jni, context, value);
375 case kJSTypeString:
376 return CYCastJavaString(jni, CYJSString(context, value));
377 case kJSTypeObject:
378 return CYCastJavaObject(jni, context, CYCastJSObject(context, value));
379
380 case kJSTypeUndefined:
381 default:
382 _assert(false);
383 }
384}
385
386CYJavaClass *CYGetJavaTable(JSContextRef context, JNIEnv *jni, jobject object) {
387 // XXX: this implementation is absolutely unacceptable :/
388 return reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(CYGetJavaClass(context, jni, _envcall(jni, GetObjectClass(object)))));
389}
390
391static JSObjectRef CYGetJavaClass(JSContextRef context, JNIEnv *jni, jclass value) {
392 JSObjectRef global(CYGetGlobalObject(context));
393 JSObjectRef cy(CYCastJSObject(context, CYGetProperty(context, global, cy_s)));
394
395 jclass Class$(_envcall(jni, FindClass("java/lang/Class")));
396 jmethodID Class$getCanonicalName(_envcall(jni, GetMethodID(Class$, "getCanonicalName", "()Ljava/lang/String;")));
397
398 CYJSString name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(value, Class$getCanonicalName))));
399 JSValueRef cached(CYGetProperty(context, cy, name));
400 if (!JSValueIsUndefined(context, cached))
401 return CYCastJSObject(context, cached);
402
403 jmethodID Class$getDeclaredConstructors(_envcall(jni, GetMethodID(Class$, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;")));
404 jmethodID Class$getDeclaredFields(_envcall(jni, GetMethodID(Class$, "getDeclaredFields", "()[Ljava/lang/reflect/Field;")));
405 jmethodID Class$getDeclaredMethods(_envcall(jni, GetMethodID(Class$, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;")));
406
407 jclass Constructor$(_envcall(jni, FindClass("java/lang/reflect/Constructor")));
408 //jmethodID Constructor$getModifiers(_envcall(jni, GetMethodID(Constructor$, "getModifiers", "()I")));
409 jmethodID Constructor$getParameterTypes(_envcall(jni, GetMethodID(Constructor$, "getParameterTypes", "()[Ljava/lang/Class;")));
410
411 jclass Field$(_envcall(jni, FindClass("java/lang/reflect/Field")));
412 jmethodID Field$getModifiers(_envcall(jni, GetMethodID(Field$, "getModifiers", "()I")));
413 jmethodID Field$getName(_envcall(jni, GetMethodID(Field$, "getName", "()Ljava/lang/String;")));
414 jmethodID Field$getType(_envcall(jni, GetMethodID(Field$, "getType", "()Ljava/lang/Class;")));
415
416 jclass Method$(_envcall(jni, FindClass("java/lang/reflect/Method")));
417 jmethodID Method$getModifiers(_envcall(jni, GetMethodID(Method$, "getModifiers", "()I")));
418 jmethodID Method$getName(_envcall(jni, GetMethodID(Method$, "getName", "()Ljava/lang/String;")));
419 jmethodID Method$getParameterTypes(_envcall(jni, GetMethodID(Method$, "getParameterTypes", "()[Ljava/lang/Class;")));
420 jmethodID Method$getReturnType(_envcall(jni, GetMethodID(Method$, "getReturnType", "()Ljava/lang/Class;")));
421
422 jclass Modifier$(_envcall(jni, FindClass("java/lang/reflect/Modifier")));
423 jmethodID Modifier$isStatic(_envcall(jni, GetStaticMethodID(Modifier$, "isStatic", "(I)Z")));
424
425 CYJavaClass *table(new CYJavaClass(jni, value));
426
427 for (jclass prototype(value); prototype != NULL; prototype = _envcall(jni, GetSuperclass(prototype))) {
428 jobjectArray fields(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(prototype, Class$getDeclaredFields))));
429
430 for (jsize i(0), e(_envcall(jni, GetArrayLength(fields))); i != e; ++i) {
431 jobject field(_envcall(jni, GetObjectArrayElement(fields, e - i - 1)));
432 jint modifiers(_envcall(jni, CallIntMethod(field, Field$getModifiers)));
433 bool instance(!_envcall(jni, CallStaticBooleanMethod(Modifier$, Modifier$isStatic, modifiers)));
434 auto &map(instance ? table->instance_ : table->static_);
435 CYJavaUTF8String name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(field, Field$getName))));
436 jfieldID id(_envcall(jni, FromReflectedField(field)));
437 jobject type(_envcall(jni, CallObjectMethod(field, Field$getType)));
438 map.insert(std::make_pair(std::string(name), CYJavaField{id, CYJavaGetPrimitive(jni, type, Class$getCanonicalName)}));
439 }
440 }
441
442 JSObjectRef constructor(JSObjectMake(context, CYJavaClass::Class_, table));
443 JSObjectRef indirect(JSObjectMake(context, NULL, NULL));
444 CYSetPrototype(context, constructor, indirect);
445
446 JSObjectRef prototype(JSObjectMake(context, NULL, NULL));
447 CYSetProperty(context, constructor, prototype_s, prototype, kJSPropertyAttributeDontEnum);
448
449 jobjectArray constructors(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(value, Class$getDeclaredConstructors))));
450
451 for (jsize i(0), e(_envcall(jni, GetArrayLength(constructors))); i != e; ++i) {
452 jobject constructor(_envcall(jni, GetObjectArrayElement(constructors, i)));
453 jobjectArray parameters(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(constructor, Constructor$getParameterTypes))));
454 CYJavaShorty shorty(CYJavaGetShorty(jni, parameters, Class$getCanonicalName));
455 table->overload_.insert(CYJavaSignature(jni, constructor, CYJavaPrimitiveObject, shorty));
456 }
457
458 jobjectArray methods(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(value, Class$getDeclaredMethods))));
459
460 std::map<std::pair<bool, std::string>, CYJavaOverload> entries;
461
462 for (jsize i(0), e(_envcall(jni, GetArrayLength(methods))); i != e; ++i) {
463 jobject method(_envcall(jni, GetObjectArrayElement(methods, i)));
464 jint modifiers(_envcall(jni, CallIntMethod(method, Method$getModifiers)));
465 bool instance(!_envcall(jni, CallStaticBooleanMethod(Modifier$, Modifier$isStatic, modifiers)));
466 CYJavaUTF8String name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(method, Method$getName))));
467 jobjectArray parameters(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(method, Method$getParameterTypes))));
468 CYJavaShorty shorty(CYJavaGetShorty(jni, parameters, Class$getCanonicalName));
469 jobject type(_envcall(jni, CallObjectMethod(method, Method$getReturnType)));
470 auto primitive(CYJavaGetPrimitive(jni, type, Class$getCanonicalName));
471 entries[std::make_pair(instance, std::string(name))].insert(CYJavaSignature(jni, method, primitive, shorty));
472 }
473
474 for (const auto &entry : entries) {
475 bool instance(entry.first.first);
476 CYJSString name(entry.first.second);
477 auto &overload(entry.second);
478 auto target(instance ? prototype : indirect);
479 JSValueRef wrapper(CYJavaMethod::Make(context, jni, overload));
480 CYSetProperty(context, target, name, wrapper, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete);
481 }
482
483 // XXX: for some reason kJSPropertyAttributeDontEnum doesn't work if there's already a property with the same name
484 // by not linking the prototypes until after we set the properties, we hide the parent property from this issue :(
485
486 if (jclass super = _envcall(jni, GetSuperclass(value))) {
487 JSObjectRef parent(CYGetJavaClass(context, jni, super));
488 CYSetPrototype(context, indirect, CYGetPrototype(context, parent));
489 CYSetPrototype(context, prototype, CYGetProperty(context, parent, prototype_s));
490 }
491
492 CYSetProperty(context, cy, name, constructor);
493 return constructor;
494}
495
496static jobjectArray CYCastJavaArguments(JNIEnv *jni, const CYJavaShorty &shorty, JSContextRef context, const JSValueRef arguments[], jclass Object$) {
497 jobjectArray array(_envcall(jni, NewObjectArray(shorty.size(), Object$, NULL)));
498 for (size_t index(0); index != shorty.size(); ++index) {
499 jobject argument;
500 switch (shorty[index]) {
501 case CYJavaPrimitiveObject:
502 argument = CYCastJavaObject(jni, context, arguments[index]);
503 break;
504#define CYJavaForEachPrimitive_(T, Typ, Type) \
505 case CYJavaPrimitive ## Type: \
506 argument = CYCastJava ## Type(jni, context, arguments[index]); \
507 break;
508CYJavaForEachPrimitive
509#undef CYJavaForEachPrimitive_
510 default:
511 _assert(false);
512 }
513 _envcallv(jni, SetObjectArrayElement(array, index, argument));
514 }
515
516 return array;
517}
518
519static JSValueRef JavaMethod_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
520 CYJavaMethod *internal(reinterpret_cast<CYJavaMethod *>(JSObjectGetPrivate(object)));
521 JNIEnv *jni(internal->jni_);
522
523 jclass Object$(_envcall(jni, FindClass("java/lang/Object")));
524
525 jclass Method$(_envcall(jni, FindClass("java/lang/reflect/Method")));
526 jmethodID Method$invoke(_envcall(jni, GetMethodID(Method$, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")));
527
528 jobject self(CYCastJavaObject(jni, context, _this));
529
530 CYJavaSignature bound(count);
531 for (auto overload(internal->overload_.lower_bound(bound)), e(internal->overload_.upper_bound(bound)); overload != e; ++overload) {
532 jobjectArray array(CYCastJavaArguments(jni, overload->shorty, context, arguments, Object$));
533 jobject object(_envcall(jni, CallObjectMethod(overload->method, Method$invoke, self, array)));
534 return CYCastJSValue(context, jni, object);
535 }
536
537 CYThrow("invalid method call");
538} CYCatch(NULL) }
539
540static JSObjectRef JavaClass_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
541 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
542 JNIEnv *jni(table->value_);
543
544 jclass Object$(_envcall(jni, FindClass("java/lang/Object")));
545
546 jclass Constructor$(_envcall(jni, FindClass("java/lang/reflect/Constructor")));
547 jmethodID Constructor$newInstance(_envcall(jni, GetMethodID(Constructor$, "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;")));
548
549 CYJavaSignature bound(count);
550 for (auto overload(table->overload_.lower_bound(bound)), e(table->overload_.upper_bound(bound)); overload != e; ++overload) {
551 jobjectArray array(CYCastJavaArguments(jni, overload->shorty, context, arguments, Object$));
552 jobject object(_envcall(jni, CallObjectMethod(overload->method, Constructor$newInstance, array)));
553 return CYCastJSObject(context, CYCastJSValue(context, jni, object));
554 }
555
556 CYThrow("invalid constructor call");
557} CYCatch(NULL) }
558
559static bool JavaClass_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
560 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
561 CYPool pool;
562 auto name(CYPoolUTF8String(pool, context, property));
563 auto field(table->static_.find(name));
564 if (field == table->static_.end())
565 return false;
566 return true;
567}
568
569static JSValueRef JavaClass_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
570 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
571 JNIEnv *jni(table->value_);
572 CYPool pool;
573 auto name(CYPoolUTF8String(pool, context, property));
574 auto field(table->static_.find(name));
575 if (field == table->static_.end())
576 return NULL;
577
578 switch (field->second.primitive_) {
579 case CYJavaPrimitiveObject:
580 return CYCastJSValue(context, jni, _envcall(jni, GetStaticObjectField(table->value_, field->second.field_)));
581#define CYJavaForEachPrimitive_(T, Typ, Type) \
582 case CYJavaPrimitive ## Type: \
583 return CYJavaCastJSValue(context, _envcall(jni, GetStatic ## Typ ## Field(table->value_, field->second.field_)));
584CYJavaForEachPrimitive
585#undef CYJavaForEachPrimitive_
586 default: _assert(false);
587 }
588} CYCatch(NULL) }
589
590static bool JavaClass_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry {
591 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
592 JNIEnv *jni(table->value_);
593 CYPool pool;
594 auto name(CYPoolUTF8String(pool, context, property));
595 auto field(table->static_.find(name));
596 if (field == table->static_.end())
597 return false;
598
599 switch (field->second.primitive_) {
600 case CYJavaPrimitiveObject:
601 _envcallv(jni, SetStaticObjectField(table->value_, field->second.field_, CYCastJavaObject(jni, context, value)));
602#define CYJavaForEachPrimitive_(T, Typ, Type) \
603 case CYJavaPrimitive ## Type: \
604 _envcallv(jni, SetStatic ## Typ ## Field(table->value_, field->second.field_, CYCastDouble(context, value))); \
605 break;
606CYJavaForEachPrimitive
607#undef CYJavaForEachPrimitive_
608 default: _assert(false);
609 }
610
611 return true;
612} CYCatch(false) }
613
614static void JavaClass_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
615 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
616 for (const auto &field : table->static_)
617 JSPropertyNameAccumulatorAddName(names, CYJSString(field.first));
618}
619
620static JSValueRef JavaClass_getProperty_class(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
621 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
622 return CYCastJSValue(context, table->value_, table->value_);
623} CYCatch(NULL) }
624
625static bool JavaObject_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
626 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
627 JNIEnv *jni(internal->value_);
628 CYJavaClass *table(CYGetJavaTable(context, jni, internal->value_));
629 CYPool pool;
630 auto name(CYPoolUTF8String(pool, context, property));
631 auto field(table->instance_.find(name));
632 if (field == table->instance_.end())
633 return false;
634 return true;
635}
636
637static JSValueRef JavaObject_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
638 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
639 JNIEnv *jni(internal->value_);
640 CYJavaClass *table(CYGetJavaTable(context, jni, internal->value_));
641 CYPool pool;
642 auto name(CYPoolUTF8String(pool, context, property));
643 auto field(table->instance_.find(name));
644 if (field == table->instance_.end())
645 return NULL;
646
647 switch (field->second.primitive_) {
648 case CYJavaPrimitiveObject:
649 return CYCastJSValue(context, jni, _envcall(jni, GetObjectField(internal->value_, field->second.field_)));
650#define CYJavaForEachPrimitive_(T, Typ, Type) \
651 case CYJavaPrimitive ## Type: \
652 return CYJavaCastJSValue(context, _envcall(jni, Get ## Typ ## Field(internal->value_, field->second.field_)));
653CYJavaForEachPrimitive
654#undef CYJavaForEachPrimitive_
655 default: _assert(false);
656 }
657} CYCatch(NULL) }
658
659static bool JavaObject_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry {
660 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
661 JNIEnv *jni(internal->value_);
662 CYJavaClass *table(CYGetJavaTable(context, jni, internal->value_));
663 CYPool pool;
664 auto name(CYPoolUTF8String(pool, context, property));
665 auto field(table->instance_.find(name));
666 if (field == table->instance_.end())
667 return false;
668
669 switch (field->second.primitive_) {
670 case CYJavaPrimitiveObject:
671 _envcallv(jni, SetObjectField(table->value_, field->second.field_, CYCastJavaObject(jni, context, value)));
672#define CYJavaForEachPrimitive_(T, Typ, Type) \
673 case CYJavaPrimitive ## Type: \
674 _envcallv(jni, Set ## Typ ## Field(table->value_, field->second.field_, CYCastDouble(context, value))); \
675 break;
676CYJavaForEachPrimitive
677#undef CYJavaForEachPrimitive_
678 default: _assert(false);
679 }
680
681 return true;
682} CYCatch(false) }
683
684static void JavaObject_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
685 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
686 JNIEnv *jni(internal->value_);
687 CYJavaClass *table(CYGetJavaTable(context, jni, internal->value_));
688 for (const auto &field : table->instance_)
689 JSPropertyNameAccumulatorAddName(names, CYJSString(field.first));
690}
691
692static JSValueRef JavaObject_getProperty_constructor(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
693 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
694 JNIEnv *jni(internal->value_);
695 return CYGetJavaClass(context, jni, _envcall(jni, GetObjectClass(internal->value_)));
696} CYCatch(NULL) }
697
698static bool CYJavaPackage_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
699 return true;
700}
701
702static JSValueRef CYJavaPackage_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
703 CYJavaPackage *internal(reinterpret_cast<CYJavaPackage *>(JSObjectGetPrivate(object)));
704 CYJavaPackage::Path package(internal->package_);
705
706 CYPool pool;
707 const char *next(CYPoolCString(pool, context, property));
708
709 std::ostringstream name;
710 for (auto &package : internal->package_)
711 name << package << '/';
712 name << next;
713
714 JNIEnv *jni(GetJNI());
715 if (jclass _class = jni->FindClass(name.str().c_str()))
716 return CYGetJavaClass(context, jni, _class);
717 jni->ExceptionClear();
718
719 package.push_back(next);
720 return CYJavaPackage::Make(context, package);
721} CYCatch(NULL) }
722
723static JSStaticValue JavaClass_staticValues[2] = {
724 {"class", &JavaClass_getProperty_class, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
725 {NULL, NULL, NULL, 0}
726};
727
728static JSStaticValue JavaObject_staticValues[2] = {
729 {"constructor", &JavaObject_getProperty_constructor, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
730 {NULL, NULL, NULL, 0}
731};
732
733static JSStaticFunction JavaPackage_staticFunctions[1] = {
734 {NULL, NULL, 0}
735};
736
737void CYJava_Initialize() {
738 Primitives_.insert(std::make_pair("void", CYJavaPrimitiveVoid));
739 Primitives_.insert(std::make_pair("boolean", CYJavaPrimitiveBoolean));
740 Primitives_.insert(std::make_pair("byte", CYJavaPrimitiveByte));
741 Primitives_.insert(std::make_pair("char", CYJavaPrimitiveCharacter));
742 Primitives_.insert(std::make_pair("short", CYJavaPrimitiveShort));
743 Primitives_.insert(std::make_pair("int", CYJavaPrimitiveInteger));
744 Primitives_.insert(std::make_pair("long", CYJavaPrimitiveLong));
745 Primitives_.insert(std::make_pair("float", CYJavaPrimitiveFloat));
746 Primitives_.insert(std::make_pair("double", CYJavaPrimitiveDouble));
747
748 JSClassDefinition definition;
749
750 definition = kJSClassDefinitionEmpty;
751 definition.className = "JavaClass";
752 definition.staticValues = JavaClass_staticValues;
753 definition.hasProperty = &JavaClass_hasProperty;
754 definition.getProperty = &JavaClass_getProperty;
755 definition.setProperty = &JavaClass_setProperty;
756 definition.getPropertyNames = &JavaClass_getPropertyNames;
757 definition.callAsConstructor = &JavaClass_callAsConstructor;
758 definition.finalize = &CYFinalize;
759 CYJavaClass::Class_ = JSClassCreate(&definition);
760
761 definition = kJSClassDefinitionEmpty;
762 definition.className = "JavaMethod";
763 definition.callAsFunction = &JavaMethod_callAsFunction;
764 definition.finalize = &CYFinalize;
765 CYJavaMethod::Class_ = JSClassCreate(&definition);
766
767 definition = kJSClassDefinitionEmpty;
768 definition.attributes = kJSClassAttributeNoAutomaticPrototype;
769 definition.className = "JavaObject";
770 definition.staticValues = JavaObject_staticValues;
771 definition.hasProperty = &JavaObject_hasProperty;
772 definition.getProperty = &JavaObject_getProperty;
773 definition.setProperty = &JavaObject_setProperty;
774 definition.getPropertyNames = &JavaObject_getPropertyNames;
775 definition.finalize = &CYFinalize;
776 CYJavaObject::Class_ = JSClassCreate(&definition);
777
778 definition = kJSClassDefinitionEmpty;
779 definition.className = "JavaPackage";
780 definition.staticFunctions = JavaPackage_staticFunctions;
781 definition.hasProperty = &CYJavaPackage_hasProperty;
782 definition.getProperty = &CYJavaPackage_getProperty;
783 definition.finalize = &CYFinalize;
784 CYJavaPackage::Class_ = JSClassCreate(&definition);
785}
786
787void CYJava_SetupContext(JSContextRef context) {
788 JSObjectRef global(CYGetGlobalObject(context));
789 //JSObjectRef cy(CYCastJSObject(context, CYGetProperty(context, global, cy_s)));
790 JSObjectRef cycript(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Cycript"))));
791 JSObjectRef all(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("all"))));
792 //JSObjectRef alls(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("alls"))));
793
794 JSObjectRef Java(JSObjectMake(context, NULL, NULL));
795 CYSetProperty(context, cycript, CYJSString("Java"), Java);
796
797 JSObjectRef Packages(CYJavaPackage::Make(context, CYJavaPackage::Path()));
798 CYSetProperty(context, all, CYJSString("Packages"), Packages);
799
800 for (auto name : (const char *[]) {"java", "javax", "android", "com", "net", "org"}) {
801 CYJSString js(name);
802 CYSetProperty(context, all, js, CYGetProperty(context, Packages, js));
803 }
804}
805
806static CYHook CYJavaHook = {
807 NULL,
808 NULL,
809 NULL,
810 &CYJava_Initialize,
811 &CYJava_SetupContext,
812 NULL,
813};
814
815CYRegisterHook CYJava(&CYJavaHook);