]> git.saurik.com Git - cycript.git/blame - Java/Execute.cpp
Use CYJavaForEachPrimitive to map primitive types.
[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 \
42619b59
JF
193 CYJavaForEachPrimitive_(Z, Boolean, Boolean, boolean) \
194 CYJavaForEachPrimitive_(B, Byte, Byte, byte) \
195 CYJavaForEachPrimitive_(C, Char, Character, char) \
196 CYJavaForEachPrimitive_(S, Short, Short, short) \
197 CYJavaForEachPrimitive_(I, Int, Integer, int) \
198 CYJavaForEachPrimitive_(J, Long, Long, long) \
199 CYJavaForEachPrimitive_(F, Float, Float, float) \
200 CYJavaForEachPrimitive_(D, Double, Double, double)
dbf05bfd
JF
201
202enum CYJavaPrimitive : char {
203 CYJavaPrimitiveObject,
204 CYJavaPrimitiveVoid,
42619b59 205#define CYJavaForEachPrimitive_(T, Typ, Type, type) \
dbf05bfd
JF
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
8effd381
JF
296static JSObjectRef CYGetJavaClass(JSContextRef context, JNIEnv *jni, jclass _class);
297
dbf05bfd
JF
298struct CYJavaObject :
299 CYJavaValue<CYJavaObject, jobject>
300{
8effd381
JF
301 CYJavaClass *table_;
302
303 CYJavaObject(JNIEnv *jni, jobject value, JSContextRef context) :
304 CYJavaValue(jni, value),
305 table_(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(CYGetJavaClass(context, jni, _envcall(jni, GetObjectClass(value))))))
dbf05bfd
JF
306 {
307 }
308
309 JSValueRef GetPrototype(JSContextRef context) const;
310};
311
8effd381
JF
312struct CYJavaInterior :
313 CYJavaValue<CYJavaInterior, jobject>
314{
315 CYJavaClass *table_;
316
317 CYJavaInterior(JNIEnv *jni, jobject value, CYJavaClass *table) :
318 CYJavaValue(jni, value),
319 table_(table)
320 {
321 }
322};
323
dbf05bfd
JF
324struct CYJavaPackage :
325 CYPrivate<CYJavaPackage>
326{
327 typedef std::vector<std::string> Path;
328 Path package_;
329
330 _finline CYJavaPackage(const Path &package) :
331 package_(package)
332 {
333 }
334};
335
dbf05bfd
JF
336JSValueRef CYJavaObject::GetPrototype(JSContextRef context) const {
337 JNIEnv *jni(value_);
338 return CYGetProperty(context, CYGetJavaClass(context, jni, _envcall(jni, GetObjectClass(value_))), prototype_s);
339}
340
341static JSValueRef CYCastJSValue(JSContextRef context, JNIEnv *jni, jobject value) {
342 if (value == NULL)
343 return CYJSNull(context);
8effd381 344 return CYJavaObject::Make(context, jni, value, context);
dbf05bfd
JF
345}
346
347static jstring CYCastJavaString(JNIEnv *jni, CYUTF16String value) {
348 return _envcall(jni, NewString(value.data, value.size));
349}
350
351static jstring CYCastJavaString(JNIEnv *jni, JSStringRef value) {
352 return CYCastJavaString(jni, CYCastUTF16String(value));
353}
354
355#define CYCastJava$(T, Type, jtype, Cast) \
356_disused static jobject CYCastJava ## Type(JNIEnv *jni, JSContextRef context, JSValueRef value) { \
357 jclass Type$(_envcall(jni, FindClass("java/lang/" #Type))); \
358 jmethodID Type$init$(_envcall(jni, GetMethodID(Type$, "<init>", "(" #T ")V"))); \
359 return _envcall(jni, NewObject(Type$, Type$init$, static_cast<jtype>(Cast(context, value)))); \
360}
361
362CYCastJava$(Z, Boolean, jboolean, CYCastBool)
363CYCastJava$(B, Byte, jbyte, CYCastDouble)
364CYCastJava$(C, Character, jchar, CYCastDouble)
365CYCastJava$(S, Short, jshort, CYCastDouble)
366CYCastJava$(I, Integer, jint, CYCastDouble)
367CYCastJava$(J, Long, jlong, CYCastDouble)
368CYCastJava$(F, Float, jfloat, CYCastDouble)
369CYCastJava$(D, Double, jdouble, CYCastDouble)
370
371static jobject CYCastJavaObject(JNIEnv *jni, JSContextRef context, JSObjectRef value) {
372 JSObjectRef object(CYCastJSObject(context, value));
373 if (JSValueIsObjectOfClass(context, value, CYJavaObject::Class_)) {
374 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
375 return internal->value_;
376 }
377
378 _assert(false);
379}
380
381static jobject CYCastJavaObject(JNIEnv *jni, JSContextRef context, JSValueRef value) {
382 switch (JSValueGetType(context, value)) {
383 case kJSTypeNull:
384 return NULL;
385 case kJSTypeBoolean:
386 return CYCastJavaBoolean(jni, context, value);
387 case kJSTypeNumber:
388 return CYCastJavaDouble(jni, context, value);
389 case kJSTypeString:
390 return CYCastJavaString(jni, CYJSString(context, value));
391 case kJSTypeObject:
392 return CYCastJavaObject(jni, context, CYCastJSObject(context, value));
393
394 case kJSTypeUndefined:
395 default:
396 _assert(false);
397 }
398}
399
dbf05bfd
JF
400static JSObjectRef CYGetJavaClass(JSContextRef context, JNIEnv *jni, jclass value) {
401 JSObjectRef global(CYGetGlobalObject(context));
402 JSObjectRef cy(CYCastJSObject(context, CYGetProperty(context, global, cy_s)));
403
404 jclass Class$(_envcall(jni, FindClass("java/lang/Class")));
405 jmethodID Class$getCanonicalName(_envcall(jni, GetMethodID(Class$, "getCanonicalName", "()Ljava/lang/String;")));
406
407 CYJSString name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(value, Class$getCanonicalName))));
408 JSValueRef cached(CYGetProperty(context, cy, name));
409 if (!JSValueIsUndefined(context, cached))
410 return CYCastJSObject(context, cached);
411
412 jmethodID Class$getDeclaredConstructors(_envcall(jni, GetMethodID(Class$, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;")));
413 jmethodID Class$getDeclaredFields(_envcall(jni, GetMethodID(Class$, "getDeclaredFields", "()[Ljava/lang/reflect/Field;")));
414 jmethodID Class$getDeclaredMethods(_envcall(jni, GetMethodID(Class$, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;")));
415
416 jclass Constructor$(_envcall(jni, FindClass("java/lang/reflect/Constructor")));
417 //jmethodID Constructor$getModifiers(_envcall(jni, GetMethodID(Constructor$, "getModifiers", "()I")));
418 jmethodID Constructor$getParameterTypes(_envcall(jni, GetMethodID(Constructor$, "getParameterTypes", "()[Ljava/lang/Class;")));
419
420 jclass Field$(_envcall(jni, FindClass("java/lang/reflect/Field")));
421 jmethodID Field$getModifiers(_envcall(jni, GetMethodID(Field$, "getModifiers", "()I")));
422 jmethodID Field$getName(_envcall(jni, GetMethodID(Field$, "getName", "()Ljava/lang/String;")));
423 jmethodID Field$getType(_envcall(jni, GetMethodID(Field$, "getType", "()Ljava/lang/Class;")));
424
425 jclass Method$(_envcall(jni, FindClass("java/lang/reflect/Method")));
426 jmethodID Method$getModifiers(_envcall(jni, GetMethodID(Method$, "getModifiers", "()I")));
427 jmethodID Method$getName(_envcall(jni, GetMethodID(Method$, "getName", "()Ljava/lang/String;")));
428 jmethodID Method$getParameterTypes(_envcall(jni, GetMethodID(Method$, "getParameterTypes", "()[Ljava/lang/Class;")));
429 jmethodID Method$getReturnType(_envcall(jni, GetMethodID(Method$, "getReturnType", "()Ljava/lang/Class;")));
430
431 jclass Modifier$(_envcall(jni, FindClass("java/lang/reflect/Modifier")));
432 jmethodID Modifier$isStatic(_envcall(jni, GetStaticMethodID(Modifier$, "isStatic", "(I)Z")));
433
434 CYJavaClass *table(new CYJavaClass(jni, value));
435
436 for (jclass prototype(value); prototype != NULL; prototype = _envcall(jni, GetSuperclass(prototype))) {
437 jobjectArray fields(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(prototype, Class$getDeclaredFields))));
438
439 for (jsize i(0), e(_envcall(jni, GetArrayLength(fields))); i != e; ++i) {
440 jobject field(_envcall(jni, GetObjectArrayElement(fields, e - i - 1)));
441 jint modifiers(_envcall(jni, CallIntMethod(field, Field$getModifiers)));
442 bool instance(!_envcall(jni, CallStaticBooleanMethod(Modifier$, Modifier$isStatic, modifiers)));
443 auto &map(instance ? table->instance_ : table->static_);
444 CYJavaUTF8String name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(field, Field$getName))));
445 jfieldID id(_envcall(jni, FromReflectedField(field)));
446 jobject type(_envcall(jni, CallObjectMethod(field, Field$getType)));
447 map.insert(std::make_pair(std::string(name), CYJavaField{id, CYJavaGetPrimitive(jni, type, Class$getCanonicalName)}));
448 }
449 }
450
451 JSObjectRef constructor(JSObjectMake(context, CYJavaClass::Class_, table));
452 JSObjectRef indirect(JSObjectMake(context, NULL, NULL));
453 CYSetPrototype(context, constructor, indirect);
454
455 JSObjectRef prototype(JSObjectMake(context, NULL, NULL));
456 CYSetProperty(context, constructor, prototype_s, prototype, kJSPropertyAttributeDontEnum);
457
458 jobjectArray constructors(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(value, Class$getDeclaredConstructors))));
459
460 for (jsize i(0), e(_envcall(jni, GetArrayLength(constructors))); i != e; ++i) {
461 jobject constructor(_envcall(jni, GetObjectArrayElement(constructors, i)));
462 jobjectArray parameters(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(constructor, Constructor$getParameterTypes))));
463 CYJavaShorty shorty(CYJavaGetShorty(jni, parameters, Class$getCanonicalName));
464 table->overload_.insert(CYJavaSignature(jni, constructor, CYJavaPrimitiveObject, shorty));
465 }
466
467 jobjectArray methods(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(value, Class$getDeclaredMethods))));
468
469 std::map<std::pair<bool, std::string>, CYJavaOverload> entries;
470
471 for (jsize i(0), e(_envcall(jni, GetArrayLength(methods))); i != e; ++i) {
472 jobject method(_envcall(jni, GetObjectArrayElement(methods, i)));
473 jint modifiers(_envcall(jni, CallIntMethod(method, Method$getModifiers)));
474 bool instance(!_envcall(jni, CallStaticBooleanMethod(Modifier$, Modifier$isStatic, modifiers)));
475 CYJavaUTF8String name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(method, Method$getName))));
476 jobjectArray parameters(static_cast<jobjectArray>(_envcall(jni, CallObjectMethod(method, Method$getParameterTypes))));
477 CYJavaShorty shorty(CYJavaGetShorty(jni, parameters, Class$getCanonicalName));
478 jobject type(_envcall(jni, CallObjectMethod(method, Method$getReturnType)));
479 auto primitive(CYJavaGetPrimitive(jni, type, Class$getCanonicalName));
480 entries[std::make_pair(instance, std::string(name))].insert(CYJavaSignature(jni, method, primitive, shorty));
481 }
482
483 for (const auto &entry : entries) {
484 bool instance(entry.first.first);
485 CYJSString name(entry.first.second);
486 auto &overload(entry.second);
487 auto target(instance ? prototype : indirect);
488 JSValueRef wrapper(CYJavaMethod::Make(context, jni, overload));
489 CYSetProperty(context, target, name, wrapper, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete);
490 }
491
492 // XXX: for some reason kJSPropertyAttributeDontEnum doesn't work if there's already a property with the same name
493 // by not linking the prototypes until after we set the properties, we hide the parent property from this issue :(
494
495 if (jclass super = _envcall(jni, GetSuperclass(value))) {
496 JSObjectRef parent(CYGetJavaClass(context, jni, super));
497 CYSetPrototype(context, indirect, CYGetPrototype(context, parent));
498 CYSetPrototype(context, prototype, CYGetProperty(context, parent, prototype_s));
499 }
500
501 CYSetProperty(context, cy, name, constructor);
502 return constructor;
503}
504
505static jobjectArray CYCastJavaArguments(JNIEnv *jni, const CYJavaShorty &shorty, JSContextRef context, const JSValueRef arguments[], jclass Object$) {
506 jobjectArray array(_envcall(jni, NewObjectArray(shorty.size(), Object$, NULL)));
507 for (size_t index(0); index != shorty.size(); ++index) {
508 jobject argument;
509 switch (shorty[index]) {
510 case CYJavaPrimitiveObject:
511 argument = CYCastJavaObject(jni, context, arguments[index]);
512 break;
42619b59 513#define CYJavaForEachPrimitive_(T, Typ, Type, type) \
dbf05bfd
JF
514 case CYJavaPrimitive ## Type: \
515 argument = CYCastJava ## Type(jni, context, arguments[index]); \
516 break;
517CYJavaForEachPrimitive
518#undef CYJavaForEachPrimitive_
519 default:
520 _assert(false);
521 }
522 _envcallv(jni, SetObjectArrayElement(array, index, argument));
523 }
524
525 return array;
526}
527
528static JSValueRef JavaMethod_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
529 CYJavaMethod *internal(reinterpret_cast<CYJavaMethod *>(JSObjectGetPrivate(object)));
530 JNIEnv *jni(internal->jni_);
531
532 jclass Object$(_envcall(jni, FindClass("java/lang/Object")));
533
534 jclass Method$(_envcall(jni, FindClass("java/lang/reflect/Method")));
535 jmethodID Method$invoke(_envcall(jni, GetMethodID(Method$, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")));
536
537 jobject self(CYCastJavaObject(jni, context, _this));
538
539 CYJavaSignature bound(count);
540 for (auto overload(internal->overload_.lower_bound(bound)), e(internal->overload_.upper_bound(bound)); overload != e; ++overload) {
541 jobjectArray array(CYCastJavaArguments(jni, overload->shorty, context, arguments, Object$));
542 jobject object(_envcall(jni, CallObjectMethod(overload->method, Method$invoke, self, array)));
543 return CYCastJSValue(context, jni, object);
544 }
545
546 CYThrow("invalid method call");
547} CYCatch(NULL) }
548
549static JSObjectRef JavaClass_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
550 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
551 JNIEnv *jni(table->value_);
552
553 jclass Object$(_envcall(jni, FindClass("java/lang/Object")));
554
555 jclass Constructor$(_envcall(jni, FindClass("java/lang/reflect/Constructor")));
556 jmethodID Constructor$newInstance(_envcall(jni, GetMethodID(Constructor$, "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;")));
557
558 CYJavaSignature bound(count);
559 for (auto overload(table->overload_.lower_bound(bound)), e(table->overload_.upper_bound(bound)); overload != e; ++overload) {
560 jobjectArray array(CYCastJavaArguments(jni, overload->shorty, context, arguments, Object$));
561 jobject object(_envcall(jni, CallObjectMethod(overload->method, Constructor$newInstance, array)));
562 return CYCastJSObject(context, CYCastJSValue(context, jni, object));
563 }
564
565 CYThrow("invalid constructor call");
566} CYCatch(NULL) }
567
568static bool JavaClass_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
569 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
570 CYPool pool;
571 auto name(CYPoolUTF8String(pool, context, property));
572 auto field(table->static_.find(name));
573 if (field == table->static_.end())
574 return false;
575 return true;
576}
577
578static JSValueRef JavaClass_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
579 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
580 JNIEnv *jni(table->value_);
581 CYPool pool;
582 auto name(CYPoolUTF8String(pool, context, property));
583 auto field(table->static_.find(name));
584 if (field == table->static_.end())
585 return NULL;
586
587 switch (field->second.primitive_) {
588 case CYJavaPrimitiveObject:
589 return CYCastJSValue(context, jni, _envcall(jni, GetStaticObjectField(table->value_, field->second.field_)));
42619b59 590#define CYJavaForEachPrimitive_(T, Typ, Type, type) \
dbf05bfd
JF
591 case CYJavaPrimitive ## Type: \
592 return CYJavaCastJSValue(context, _envcall(jni, GetStatic ## Typ ## Field(table->value_, field->second.field_)));
593CYJavaForEachPrimitive
594#undef CYJavaForEachPrimitive_
595 default: _assert(false);
596 }
597} CYCatch(NULL) }
598
599static bool JavaClass_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry {
600 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
601 JNIEnv *jni(table->value_);
602 CYPool pool;
603 auto name(CYPoolUTF8String(pool, context, property));
604 auto field(table->static_.find(name));
605 if (field == table->static_.end())
606 return false;
607
608 switch (field->second.primitive_) {
609 case CYJavaPrimitiveObject:
610 _envcallv(jni, SetStaticObjectField(table->value_, field->second.field_, CYCastJavaObject(jni, context, value)));
42619b59 611#define CYJavaForEachPrimitive_(T, Typ, Type, type) \
dbf05bfd
JF
612 case CYJavaPrimitive ## Type: \
613 _envcallv(jni, SetStatic ## Typ ## Field(table->value_, field->second.field_, CYCastDouble(context, value))); \
614 break;
615CYJavaForEachPrimitive
616#undef CYJavaForEachPrimitive_
617 default: _assert(false);
618 }
619
620 return true;
621} CYCatch(false) }
622
623static void JavaClass_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
624 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
625 for (const auto &field : table->static_)
626 JSPropertyNameAccumulatorAddName(names, CYJSString(field.first));
627}
628
629static JSValueRef JavaClass_getProperty_class(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
630 CYJavaClass *table(reinterpret_cast<CYJavaClass *>(JSObjectGetPrivate(object)));
631 return CYCastJSValue(context, table->value_, table->value_);
632} CYCatch(NULL) }
633
8effd381
JF
634static bool JavaInterior_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
635 CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object)));
636 CYJavaClass *table(internal->table_);
dbf05bfd
JF
637 CYPool pool;
638 auto name(CYPoolUTF8String(pool, context, property));
639 auto field(table->instance_.find(name));
640 if (field == table->instance_.end())
641 return false;
642 return true;
643}
644
8effd381
JF
645static JSValueRef JavaInterior_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
646 CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object)));
dbf05bfd 647 JNIEnv *jni(internal->value_);
8effd381 648 CYJavaClass *table(internal->table_);
dbf05bfd
JF
649 CYPool pool;
650 auto name(CYPoolUTF8String(pool, context, property));
651 auto field(table->instance_.find(name));
652 if (field == table->instance_.end())
653 return NULL;
654
655 switch (field->second.primitive_) {
656 case CYJavaPrimitiveObject:
657 return CYCastJSValue(context, jni, _envcall(jni, GetObjectField(internal->value_, field->second.field_)));
42619b59 658#define CYJavaForEachPrimitive_(T, Typ, Type, type) \
dbf05bfd
JF
659 case CYJavaPrimitive ## Type: \
660 return CYJavaCastJSValue(context, _envcall(jni, Get ## Typ ## Field(internal->value_, field->second.field_)));
661CYJavaForEachPrimitive
662#undef CYJavaForEachPrimitive_
663 default: _assert(false);
664 }
665} CYCatch(NULL) }
666
8effd381
JF
667static bool JavaInterior_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry {
668 CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object)));
dbf05bfd 669 JNIEnv *jni(internal->value_);
8effd381 670 CYJavaClass *table(internal->table_);
dbf05bfd
JF
671 CYPool pool;
672 auto name(CYPoolUTF8String(pool, context, property));
673 auto field(table->instance_.find(name));
674 if (field == table->instance_.end())
675 return false;
676
677 switch (field->second.primitive_) {
678 case CYJavaPrimitiveObject:
679 _envcallv(jni, SetObjectField(table->value_, field->second.field_, CYCastJavaObject(jni, context, value)));
42619b59 680#define CYJavaForEachPrimitive_(T, Typ, Type, type) \
dbf05bfd
JF
681 case CYJavaPrimitive ## Type: \
682 _envcallv(jni, Set ## Typ ## Field(table->value_, field->second.field_, CYCastDouble(context, value))); \
683 break;
684CYJavaForEachPrimitive
685#undef CYJavaForEachPrimitive_
686 default: _assert(false);
687 }
688
689 return true;
690} CYCatch(false) }
691
8effd381
JF
692static void JavaInterior_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
693 CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object)));
694 CYJavaClass *table(internal->table_);
dbf05bfd
JF
695 for (const auto &field : table->instance_)
696 JSPropertyNameAccumulatorAddName(names, CYJSString(field.first));
697}
698
699static JSValueRef JavaObject_getProperty_constructor(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
700 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
701 JNIEnv *jni(internal->value_);
702 return CYGetJavaClass(context, jni, _envcall(jni, GetObjectClass(internal->value_)));
703} CYCatch(NULL) }
704
8effd381
JF
705static JSValueRef JavaObject_getProperty_$cyi(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
706 CYJavaObject *internal(reinterpret_cast<CYJavaObject *>(JSObjectGetPrivate(object)));
707 JNIEnv *jni(internal->value_);
708 return CYJavaInterior::Make(context, jni, internal->value_, internal->table_);
709} CYCatch(NULL) }
710
dbf05bfd
JF
711static bool CYJavaPackage_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
712 return true;
713}
714
715static JSValueRef CYJavaPackage_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
716 CYJavaPackage *internal(reinterpret_cast<CYJavaPackage *>(JSObjectGetPrivate(object)));
717 CYJavaPackage::Path package(internal->package_);
718
719 CYPool pool;
720 const char *next(CYPoolCString(pool, context, property));
721
722 std::ostringstream name;
723 for (auto &package : internal->package_)
724 name << package << '/';
725 name << next;
726
727 JNIEnv *jni(GetJNI());
728 if (jclass _class = jni->FindClass(name.str().c_str()))
729 return CYGetJavaClass(context, jni, _class);
730 jni->ExceptionClear();
731
732 package.push_back(next);
733 return CYJavaPackage::Make(context, package);
734} CYCatch(NULL) }
735
736static JSStaticValue JavaClass_staticValues[2] = {
737 {"class", &JavaClass_getProperty_class, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
738 {NULL, NULL, NULL, 0}
739};
740
8effd381 741static JSStaticValue JavaObject_staticValues[3] = {
dbf05bfd 742 {"constructor", &JavaObject_getProperty_constructor, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
8effd381 743 {"$cyi", &JavaObject_getProperty_$cyi, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
dbf05bfd
JF
744 {NULL, NULL, NULL, 0}
745};
746
747static JSStaticFunction JavaPackage_staticFunctions[1] = {
748 {NULL, NULL, 0}
749};
750
751void CYJava_Initialize() {
752 Primitives_.insert(std::make_pair("void", CYJavaPrimitiveVoid));
42619b59
JF
753#define CYJavaForEachPrimitive_(T, Typ, Type, type) \
754 Primitives_.insert(std::make_pair(#type, CYJavaPrimitive ## Type));
755CYJavaForEachPrimitive
756#undef CYJavaForEachPrimitive_
dbf05bfd
JF
757
758 JSClassDefinition definition;
759
760 definition = kJSClassDefinitionEmpty;
761 definition.className = "JavaClass";
762 definition.staticValues = JavaClass_staticValues;
763 definition.hasProperty = &JavaClass_hasProperty;
764 definition.getProperty = &JavaClass_getProperty;
765 definition.setProperty = &JavaClass_setProperty;
766 definition.getPropertyNames = &JavaClass_getPropertyNames;
767 definition.callAsConstructor = &JavaClass_callAsConstructor;
768 definition.finalize = &CYFinalize;
769 CYJavaClass::Class_ = JSClassCreate(&definition);
770
8effd381
JF
771 definition = kJSClassDefinitionEmpty;
772 definition.attributes = kJSClassAttributeNoAutomaticPrototype;
773 definition.className = "JavaInterior";
774 definition.hasProperty = &JavaInterior_hasProperty;
775 definition.getProperty = &JavaInterior_getProperty;
776 definition.setProperty = &JavaInterior_setProperty;
777 definition.getPropertyNames = &JavaInterior_getPropertyNames;
778 definition.finalize = &CYFinalize;
779 CYJavaInterior::Class_ = JSClassCreate(&definition);
780
dbf05bfd
JF
781 definition = kJSClassDefinitionEmpty;
782 definition.className = "JavaMethod";
783 definition.callAsFunction = &JavaMethod_callAsFunction;
784 definition.finalize = &CYFinalize;
785 CYJavaMethod::Class_ = JSClassCreate(&definition);
786
787 definition = kJSClassDefinitionEmpty;
788 definition.attributes = kJSClassAttributeNoAutomaticPrototype;
789 definition.className = "JavaObject";
790 definition.staticValues = JavaObject_staticValues;
dbf05bfd
JF
791 definition.finalize = &CYFinalize;
792 CYJavaObject::Class_ = JSClassCreate(&definition);
793
794 definition = kJSClassDefinitionEmpty;
795 definition.className = "JavaPackage";
796 definition.staticFunctions = JavaPackage_staticFunctions;
797 definition.hasProperty = &CYJavaPackage_hasProperty;
798 definition.getProperty = &CYJavaPackage_getProperty;
799 definition.finalize = &CYFinalize;
800 CYJavaPackage::Class_ = JSClassCreate(&definition);
801}
802
803void CYJava_SetupContext(JSContextRef context) {
804 JSObjectRef global(CYGetGlobalObject(context));
805 //JSObjectRef cy(CYCastJSObject(context, CYGetProperty(context, global, cy_s)));
806 JSObjectRef cycript(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Cycript"))));
807 JSObjectRef all(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("all"))));
808 //JSObjectRef alls(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("alls"))));
809
810 JSObjectRef Java(JSObjectMake(context, NULL, NULL));
811 CYSetProperty(context, cycript, CYJSString("Java"), Java);
812
813 JSObjectRef Packages(CYJavaPackage::Make(context, CYJavaPackage::Path()));
814 CYSetProperty(context, all, CYJSString("Packages"), Packages);
815
816 for (auto name : (const char *[]) {"java", "javax", "android", "com", "net", "org"}) {
817 CYJSString js(name);
818 CYSetProperty(context, all, js, CYGetProperty(context, Packages, js));
819 }
820}
821
822static CYHook CYJavaHook = {
823 NULL,
824 NULL,
825 NULL,
826 &CYJava_Initialize,
827 &CYJava_SetupContext,
828 NULL,
829};
830
831CYRegisterHook CYJava(&CYJavaHook);