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