]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | { | |
4b645e23 | 104 | _assert(value != NULL); |
dbf05bfd JF |
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 \ | |
4b645e23 JF |
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) | |
dbf05bfd JF |
202 | |
203 | enum CYJavaPrimitive : char { | |
204 | CYJavaPrimitiveObject, | |
205 | CYJavaPrimitiveVoid, | |
4b645e23 | 206 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
dbf05bfd JF |
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) { | |
4b645e23 JF |
224 | jstring string(static_cast<jstring>(_envcall(jni, CallObjectMethod(type, Class$get$$Name)))); |
225 | _assert(string != NULL); | |
226 | CYJavaUTF8String name(jni, string); | |
dbf05bfd JF |
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 { | |
4b645e23 JF |
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) | |
dbf05bfd JF |
259 | { |
260 | } | |
261 | ||
262 | CYJavaSignature(unsigned count) : | |
4b645e23 | 263 | shorty_(count) |
dbf05bfd JF |
264 | { |
265 | } | |
266 | ||
267 | bool operator <(const CYJavaSignature &rhs) const { | |
4b645e23 | 268 | return shorty_.size() < rhs.shorty_.size(); |
dbf05bfd JF |
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 | ||
8effd381 JF |
301 | static JSObjectRef CYGetJavaClass(JSContextRef context, JNIEnv *jni, jclass _class); |
302 | ||
dbf05bfd JF |
303 | struct CYJavaObject : |
304 | CYJavaValue<CYJavaObject, jobject> | |
305 | { | |
8effd381 JF |
306 | CYJavaClass *table_; |
307 | ||
4b645e23 | 308 | CYJavaObject(JNIEnv *jni, jobject value, CYJavaClass *table) : |
8effd381 | 309 | CYJavaValue(jni, value), |
4b645e23 | 310 | table_(table) |
dbf05bfd JF |
311 | { |
312 | } | |
313 | ||
314 | JSValueRef GetPrototype(JSContextRef context) const; | |
315 | }; | |
316 | ||
8effd381 JF |
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 | ||
4b645e23 JF |
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 | ||
dbf05bfd JF |
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 | ||
dbf05bfd JF |
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 | ||
4b645e23 JF |
372 | JSValueRef CYJavaArray::GetPrototype(JSContextRef context) const { |
373 | return CYGetCachedObject(context, CYJSString("Array_prototype")); | |
374 | } | |
375 | ||
dbf05bfd JF |
376 | static JSValueRef CYCastJSValue(JSContextRef context, JNIEnv *jni, jobject value) { |
377 | if (value == NULL) | |
378 | return CYJSNull(context); | |
4b645e23 JF |
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); | |
dbf05bfd JF |
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 | ||
dbf05bfd JF |
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"))); | |
4b645e23 | 455 | jmethodID Class$getName(_envcall(jni, GetMethodID(Class$, "getName", "()Ljava/lang/String;"))); |
dbf05bfd | 456 | |
4b645e23 | 457 | CYJSString name(jni, static_cast<jstring>(_envcall(jni, CallObjectMethod(value, Class$getName)))); |
dbf05bfd JF |
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))); | |
4b645e23 | 497 | map.insert(std::make_pair(std::string(name), CYJavaField{id, CYJavaGetPrimitive(jni, type, Class$getName)})); |
dbf05bfd JF |
498 | } |
499 | } | |
500 | ||
501 | JSObjectRef constructor(JSObjectMake(context, CYJavaClass::Class_, table)); | |
dbf05bfd JF |
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)))); | |
4b645e23 JF |
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)); | |
dbf05bfd JF |
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)))); | |
4b645e23 | 526 | CYJavaShorty shorty(CYJavaGetShorty(jni, parameters, Class$getName)); |
dbf05bfd | 527 | jobject type(_envcall(jni, CallObjectMethod(method, Method$getReturnType))); |
4b645e23 JF |
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)); | |
dbf05bfd JF |
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); | |
4b645e23 | 537 | auto target(instance ? prototype : constructor); |
dbf05bfd JF |
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)); | |
4b645e23 | 547 | CYSetPrototype(context, constructor, parent); |
dbf05bfd JF |
548 | CYSetPrototype(context, prototype, CYGetProperty(context, parent, prototype_s)); |
549 | } | |
550 | ||
551 | CYSetProperty(context, cy, name, constructor); | |
552 | return constructor; | |
553 | } | |
554 | ||
4b645e23 JF |
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; | |
dbf05bfd JF |
561 | CYJavaForEachPrimitive |
562 | #undef CYJavaForEachPrimitive_ | |
4b645e23 JF |
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 | ||
dbf05bfd JF |
610 | default: |
611 | _assert(false); | |
612 | } | |
dbf05bfd JF |
613 | } |
614 | ||
4b645e23 | 615 | return true; |
dbf05bfd JF |
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_); | |
dbf05bfd JF |
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) { | |
4b645e23 JF |
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 | } | |
dbf05bfd JF |
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 | ||
dbf05bfd JF |
647 | CYJavaSignature bound(count); |
648 | for (auto overload(table->overload_.lower_bound(bound)), e(table->overload_.upper_bound(bound)); overload != e; ++overload) { | |
4b645e23 JF |
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? | |
dbf05bfd JF |
654 | return CYCastJSObject(context, CYCastJSValue(context, jni, object)); |
655 | } | |
656 | ||
657 | CYThrow("invalid constructor call"); | |
658 | } CYCatch(NULL) } | |
659 | ||
4b645e23 JF |
660 | static bool JavaStatic_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) { |
661 | CYJavaStatic *internal(reinterpret_cast<CYJavaStatic *>(JSObjectGetPrivate(object))); | |
662 | CYJavaClass *table(internal->table_); | |
dbf05bfd JF |
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 | ||
4b645e23 JF |
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_); | |
dbf05bfd JF |
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_))); | |
4b645e23 | 684 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
dbf05bfd JF |
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 | ||
4b645e23 JF |
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_); | |
dbf05bfd JF |
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))); | |
4b645e23 | 706 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
dbf05bfd JF |
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 | ||
4b645e23 JF |
718 | static void JavaStatic_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) { |
719 | CYJavaStatic *internal(reinterpret_cast<CYJavaStatic *>(JSObjectGetPrivate(object))); | |
720 | CYJavaClass *table(internal->table_); | |
dbf05bfd JF |
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 | ||
8effd381 JF |
730 | static bool JavaInterior_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) { |
731 | CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object))); | |
732 | CYJavaClass *table(internal->table_); | |
dbf05bfd JF |
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 | ||
8effd381 JF |
741 | static JSValueRef JavaInterior_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { |
742 | CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object))); | |
dbf05bfd | 743 | JNIEnv *jni(internal->value_); |
8effd381 | 744 | CYJavaClass *table(internal->table_); |
dbf05bfd JF |
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_))); | |
4b645e23 | 754 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
dbf05bfd JF |
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 | ||
8effd381 JF |
763 | static bool JavaInterior_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry { |
764 | CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object))); | |
dbf05bfd | 765 | JNIEnv *jni(internal->value_); |
8effd381 | 766 | CYJavaClass *table(internal->table_); |
dbf05bfd JF |
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))); | |
4b645e23 | 776 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
dbf05bfd JF |
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 | ||
8effd381 JF |
788 | static void JavaInterior_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) { |
789 | CYJavaInterior *internal(reinterpret_cast<CYJavaInterior *>(JSObjectGetPrivate(object))); | |
790 | CYJavaClass *table(internal->table_); | |
dbf05bfd JF |
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 | ||
4b645e23 JF |
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 | ||
8effd381 JF |
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 | ||
4b645e23 JF |
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 | ||
dbf05bfd JF |
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 | ||
4b645e23 | 912 | static JSStaticValue JavaClass_staticValues[3] = { |
dbf05bfd | 913 | {"class", &JavaClass_getProperty_class, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, |
4b645e23 | 914 | {"$cyi", &JavaClass_getProperty_$cyi, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, |
dbf05bfd JF |
915 | {NULL, NULL, NULL, 0} |
916 | }; | |
917 | ||
4b645e23 JF |
918 | static JSStaticFunction JavaClass_staticFunctions[2] = { |
919 | {"toCYON", &JavaClass_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, | |
920 | {NULL, NULL, 0} | |
921 | }; | |
922 | ||
8effd381 | 923 | static JSStaticValue JavaObject_staticValues[3] = { |
dbf05bfd | 924 | {"constructor", &JavaObject_getProperty_constructor, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, |
8effd381 | 925 | {"$cyi", &JavaObject_getProperty_$cyi, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, |
dbf05bfd JF |
926 | {NULL, NULL, NULL, 0} |
927 | }; | |
928 | ||
4b645e23 JF |
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}, | |
dbf05bfd JF |
936 | {NULL, NULL, 0} |
937 | }; | |
938 | ||
939 | void CYJava_Initialize() { | |
940 | Primitives_.insert(std::make_pair("void", CYJavaPrimitiveVoid)); | |
4b645e23 | 941 | #define CYJavaForEachPrimitive_(T, t, Typ, Type, type) \ |
42619b59 JF |
942 | Primitives_.insert(std::make_pair(#type, CYJavaPrimitive ## Type)); |
943 | CYJavaForEachPrimitive | |
944 | #undef CYJavaForEachPrimitive_ | |
dbf05bfd JF |
945 | |
946 | JSClassDefinition definition; | |
947 | ||
948 | definition = kJSClassDefinitionEmpty; | |
949 | definition.className = "JavaClass"; | |
950 | definition.staticValues = JavaClass_staticValues; | |
4b645e23 | 951 | definition.staticFunctions = JavaClass_staticFunctions; |
dbf05bfd JF |
952 | definition.callAsConstructor = &JavaClass_callAsConstructor; |
953 | definition.finalize = &CYFinalize; | |
954 | CYJavaClass::Class_ = JSClassCreate(&definition); | |
955 | ||
8effd381 JF |
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 | ||
dbf05bfd JF |
966 | definition = kJSClassDefinitionEmpty; |
967 | definition.className = "JavaMethod"; | |
4b645e23 | 968 | definition.staticFunctions = JavaMethod_staticFunctions; |
dbf05bfd JF |
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; | |
dbf05bfd JF |
977 | definition.finalize = &CYFinalize; |
978 | CYJavaObject::Class_ = JSClassCreate(&definition); | |
979 | ||
4b645e23 JF |
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 | ||
dbf05bfd JF |
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); | |
4b645e23 JF |
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); | |
dbf05bfd JF |
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); |